Skip to content

Commit 1abf0ce

Browse files
committed
Add NAT64 to enable IPv6 provision in v4 only host
- Install Tayga as NAT64 service - Install Bind9 and configure it as local DNS server with DNS64 - Add IPv6 specific addresses and logic to cluster, controlplane, and worker templates - Update vars.md with new variables - Update CNI configuration for IPv6 Signed-off-by: Nuutti Hakala <[email protected]>
1 parent 19e63ce commit 1abf0ce

File tree

19 files changed

+275
-24
lines changed

19 files changed

+275
-24
lines changed

tests/roles/run_tests/tasks/verify.yml

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,60 @@
3939
replace: "{{ POD_CIDR }}"
4040
register: updated_manifest
4141

42-
- name: Add IP_AUTODETECTION_METHOD in calico config Ubuntu
43-
blockinfile:
44-
path: /tmp/calico.yaml
45-
insertafter: "{{ POD_CIDR }}"
46-
block: |
47-
# for indentation
48-
- name: IP_AUTODETECTION_METHOD
49-
value: "cidr={{ EXTERNAL_SUBNET_V4_HOST }}/{{ EXTERNAL_SUBNET_V4_PREFIX }}"
42+
- name: Calico IPv4 specific setup
43+
block:
44+
- name: Add IP_AUTODETECTION_METHOD in calico config Ubuntu
45+
blockinfile:
46+
path: /tmp/calico.yaml
47+
insertafter: "{{ POD_CIDR }}"
48+
block: |
49+
# for indentation
50+
- name: IP_AUTODETECTION_METHOD
51+
value: "cidr={{ EXTERNAL_SUBNET_V4_HOST }}/{{ EXTERNAL_SUBNET_V4_PREFIX }}"
52+
53+
when: IP_STACK == "v4" or IP_STACK == "v4v6"
54+
55+
- name: Calico IPv6 specific setup
56+
block:
57+
58+
- name: Edit IPAM pool
59+
replace:
60+
path: /tmp/calico.yaml
61+
regexp: '"type": "calico-ipam"'
62+
replace: '"type": "calico-ipam", "assign_ipv4": "false", "assign_ipv6": "true"'
63+
64+
- name: Add IP_AUTODETECTION_METHOD and POD_CIDR in calico config Ubuntu
65+
blockinfile:
66+
path: /tmp/calico.yaml
67+
insertafter: "{{ POD_CIDR }}"
68+
block: |
69+
# for indentation
70+
- name: IP_AUTODETECTION_METHOD
71+
value: "cidr=127.0.0.1/24"
72+
- name: IP6_AUTODETECTION_METHOD
73+
value: "cidr={{ EXTERNAL_SUBNET_V6_HOST }}/{{ EXTERNAL_SUBNET_V6_PREFIX }}"
74+
- name: IP6
75+
value: "autodetect"
76+
# CALICO_ROUTER_ID needed when v4 is disabled
77+
- name: CALICO_ROUTER_ID
78+
value: "hash"
79+
80+
- name: Disable IPv4 autodetection
81+
replace:
82+
path: /tmp/calico.yaml
83+
after: "name: IP"
84+
before: "name: CALICO_IPV4POOL_IPIP"
85+
regexp: "autodetect"
86+
replace: "none"
87+
88+
- name: set FELIX_IPV6SUPPORT to true
89+
replace:
90+
path: /tmp/calico.yaml
91+
after: "name: FELIX_IPV6SUPPORT"
92+
before: "name: FELIX_HEALTHENABLED"
93+
regexp: "false"
94+
replace: "true"
95+
when: IP_STACK == "v6"
5096

5197
- name: Apply Calico manifest
5298
kubernetes.core.k8s:

tests/roles/run_tests/templates/main/cluster-template-cluster.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ metadata:
2525
namespace: ${ NAMESPACE }
2626
spec:
2727
controlPlaneEndpoint:
28-
host: ${ CLUSTER_APIENDPOINT_HOST }
28+
host: ${ CLUSTER_APIENDPOINT_IP }
2929
port: ${ CLUSTER_APIENDPOINT_PORT }
3030
noCloudProvider: true
3131
---

tests/roles/run_tests/templates/main/cluster-template-controlplane-kubeadm-config-centos.yaml

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# CentOS specific controlplane kubeadm config
22
preKubeadmCommands:
3+
- sysctl -w net.ipv6.conf.all.forwarding=1
4+
- sysctl -w net.ipv6.conf.default.forwarding=1
35
- systemctl restart NetworkManager.service
46
- nmcli connection load /etc/NetworkManager/system-connections/{{ IRONIC_ENDPOINT_BRIDGE }}.nmconnection
57
- nmcli connection up {{ IRONIC_ENDPOINT_BRIDGE }}
@@ -66,7 +68,11 @@ files:
6668
priority 101
6769
advert_int 1
6870
virtual_ipaddress {
69-
{{ CLUSTER_APIENDPOINT_HOST }}
71+
{% if IP_STACK == "v6" %}
72+
{# According to VRRP, link local address should come first with v6 #}
73+
{{ CLUSTER_APIENDPOINT_LINK_LOCAL }}
74+
{% endif %}
75+
{{ CLUSTER_APIENDPOINT_IP }}
7076
}
7177
track_script {
7278
k8s_api_check
@@ -92,26 +98,38 @@ files:
9298
type=bridge
9399
autoconnect=yes
94100
autoconnect-priority=1
101+
95102
[bridge]
96103
interface-name={{ IRONIC_ENDPOINT_BRIDGE }}
97104
stp=false
105+
106+
{% if IP_STACK == "v6" %}
98107
[ipv4]
108+
method=disabled
109+
110+
[ipv6]
99111
address1={{ "{{ ds.meta_data.provisioningIP }}" }}/{{ "{{ ds.meta_data.provisioningCIDR }}" }}
112+
addr-gen-mode=eui64
100113
method=manual
114+
{% else %}
115+
[ipv4]
116+
address1={{ "{{ ds.meta_data.provisioningIP }}" }}/{{ "{{ ds.meta_data.provisioningCIDR }}" }}
117+
method=manual
118+
101119
[ipv6]
102120
addr-gen-mode=eui64
103121
method=ignore
122+
{% endif %}
104123
- path: /etc/yum.repos.d/kubernetes.repo
105124
owner: root:root
106125
permissions: '0644'
107126
content: |
108127
[kubernetes]
109128
name=Kubernetes
110-
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
129+
baseurl=https://pkgs.k8s.io/core:/stable:/v1.34/rpm/
111130
enabled=1
112131
gpgcheck=1
113-
repo_gpgcheck=0
114-
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
132+
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.34/rpm/repodata/repomd.xml.key
115133
- path : /etc/containers/registries.conf
116134
content: |
117135
[registries.search]

tests/roles/run_tests/templates/main/cluster-template-workers-kubeadm-config-centos.yaml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# CentOS specific worker kubeadm config
22
preKubeadmCommands:
3+
- sysctl -w net.ipv6.conf.all.forwarding=1
4+
- sysctl -w net.ipv6.conf.default.forwarding=1
35
- rm /etc/cni/net.d/*
46
- systemctl restart NetworkManager.service
57
- nmcli connection load /etc/NetworkManager/system-connections/{{ IRONIC_ENDPOINT_BRIDGE }}.nmconnection
@@ -57,24 +59,33 @@ files:
5759
[bridge]
5860
stp=false
5961
62+
{% if IP_STACK == "v6" %}
63+
[ipv4]
64+
method=disabled
65+
66+
[ipv6]
67+
address1={{ "{{ ds.meta_data.provisioningIP }}" }}/{{ "{{ ds.meta_data.provisioningCIDR }}" }}
68+
addr-gen-mode=eui64
69+
method=manual
70+
{% else %}
6071
[ipv4]
6172
address1={{ "{{ ds.meta_data.provisioningIP }}" }}/{{ "{{ ds.meta_data.provisioningCIDR }}" }}
6273
method=manual
6374

6475
[ipv6]
6576
addr-gen-mode=eui64
6677
method=ignore
78+
{% endif %}
6779
- path: /etc/yum.repos.d/kubernetes.repo
6880
owner: root:root
6981
permissions: '0644'
7082
content: |
7183
[kubernetes]
7284
name=Kubernetes
73-
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
85+
baseurl=https://pkgs.k8s.io/core:/stable:/v1.34/rpm/
7486
enabled=1
7587
gpgcheck=1
76-
repo_gpgcheck=0
77-
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
88+
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.34/rpm/repodata/repomd.xml.key
7889
- path : /etc/containers/registries.conf
7990
owner: root:root
8091
permissions: '0644'

tests/roles/run_tests/templates/main/metal3datatemplate-template.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,5 @@ spec:
6666
- 8.8.8.8
6767
{% endif %}
6868
{% if IP_STACK == 'v6' or IP_STACK == 'v4v6' %}
69-
- 2001:4860:4860::8888
69+
- {{ LOCAL_DNS_V6 }}
7070
{% endif %}

tests/roles/run_tests/vars/main.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ CAPI_CONFIG_DIR: "{{ lookup('env', 'CAPI_CONFIG_DIR') | default('$HOME/.config/c
1010
CLUSTER_APIENDPOINT_HOST: "{{ lookup('env', 'CLUSTER_APIENDPOINT_HOST') }}"
1111
CLUSTER_APIENDPOINT_PORT: "{{ lookup('env', 'CLUSTER_APIENDPOINT_PORT') | default(6443, true) }}"
1212
CLUSTER_APIENDPOINT_IP: "{{ lookup('env', 'CLUSTER_APIENDPOINT_IP') }}"
13+
CLUSTER_APIENDPOINT_LINK_LOCAL: "{{ ansible_facts['ironicendpoint']['ipv6'][2]['address'] }}"
1314
CLUSTER_NAME: "{{ lookup('env', 'CLUSTER_NAME') | default('test1', true) }}"
1415
CONTROL_PLANE_MACHINE_COUNT: "{{ lookup('env', 'CONTROL_PLANE_MACHINE_COUNT') | default(1, true) }}"
1516
WORKER_MACHINE_COUNT: "{{ lookup('env', 'WORKER_MACHINE_COUNT') | default(1, true) }}"
@@ -48,7 +49,7 @@ DEPLOY_RAMDISK_URL: "{{ lookup('env', 'DEPLOY_RAMDISK_URL') }}"
4849
IRONIC_URL: "{{ lookup('env', 'IRONIC_URL') }}"
4950
IRONIC_INSPECTOR_URL: "{{ lookup('env', 'IRONIC_INSPECTOR_URL') }}"
5051
POD_CIDR: "{{ lookup('env', 'POD_CIDR') }}"
51-
SERVICE_CIDR: "10.96.0.0/12"
52+
SERVICE_CIDR: "{{ lookup('env', 'SERVICE_CIDR') | default('10.96.0.0/12', true) }}"
5253
CAPM3RELEASE: "{{ lookup('env', 'CAPM3RELEASE') | default('v1.1.2', true) }}"
5354
CAPIRELEASE: "{{ lookup('env', 'CAPIRELEASE') | default('v1.1.4', true) }}"
5455
IPAMRELEASE: "{{ lookup('env', 'IPAMRELEASE') | default('v1.1.2', true) }}"
@@ -61,6 +62,7 @@ CALICO_MINOR_RELEASE: "{{ lookup('env', 'CALICO_MINOR_RELEASE') | default('v3.25
6162
CALICO_PATCH_RELEASE: "{{ lookup('env', 'CALICO_PATCH_RELEASE') | default('v3.25.1', true) }}"
6263
DOCKER_HUB_PROXY: "{{ lookup('env', 'DOCKER_HUB_PROXY') }}"
6364
WORKING_DIR: "{{ lookup('env', 'WORKING_DIR') | default('/opt/metal3-dev-env', true) }}"
65+
LOCAL_DNS_V6: "{{ lookup('env', 'LOCAL_DNS_V6') | default('fd00:abcd::1', true) }}"
6466

6567
# Environment variables for deployment. IMAGE_OS can be centos or ubuntu, change accordingly to your needs.
6668
IMAGE_OS: "{{ lookup('env', 'IMAGE_OS') | default('centos', true) }}"

vars.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ assured that they are persisted.
1212
| Name | Option | Allowed values | Default |
1313
| :------ | :------- | :--------------- | :-------- |
1414
| DOCKER_USE_IPV6_INTERNALLY | Choose whether Docker will use IPv6 internally. | "true", "false" | false |
15+
| ENABLE_NAT64 | Enable NAT64 functionality to emulate IPv6 capable host. | "true", "false" | "false
16+
| DNS_UPSTREAM | Specify upstream DNS server for locally run DNS. Only used when ENABLE_NAT64 = "true" | IPv4 or IPv6 address | 10.1.0.2
17+
| DNS64_PREFIX | Prefix for DNS64 server. Only used when ENABLE_NAT64 = "true" | Any valid prefix | 64:ff9b::/96
18+
| LOCAL_DNS_V4 | IPv4 address for local DNS server. Only used when ENABLE_NAT64 = "true". Using loopback is fine, because in the IPv6 setup VMs don't access DNS server over IPv4. | IPv4 address | 127.0.0.2
19+
| LOCAL_DNS_V6 | IPv6 address for local DNS server. Only used when ENABLE_NAT64 = "true". NOT RECOMMENDED TO USE LOOPBACK ::1, because then VMs cannot access the server. | IPv6 address | fd00:abcd::1
1520
| MAX_SURGE_VALUE | This variable defines if controlplane should scale-in or scale-out during upgrade. | 0 (scale-in) or 1 (scale-out) |1|
1621
| EPHEMERAL_CLUSTER | Tool for running management/ephemeral cluster. | minikube, kind, tilt | "kind" when using docker as the container runtime (the default on Ubuntu), "minikube" otherwise |
1722
| IP_STACK | Choose whether the "external" libvirt network will use IPv4, IPv6, or IPv4+IPv6. This network is the primary network interface for the virtual bare metal hosts. Note that this only sets up the underlying network, and fully provisioning IPv6 kubernetes clusters is not yet automated. If IPv6 is enabled, DHCPv6 will be available to the virtual bare metal hosts. | "v4", "v6", "v4v6" (dual-stack)) | v4 |
@@ -217,6 +222,10 @@ networking, the following additional steps are required:
217222
1. You need to replace the default IPv4 addresses with IPv6 addresses in the
218223
environment variables.
219224

225+
Notice that if your host does not have support for native IPv6, you need to
226+
enable NAT64 and DNS64 to provision the VMs. If you enable NAT64 with the
227+
variable below, it will also enable DNS64.
228+
220229
The following variables need to be set:
221230

222231
``` sh
@@ -229,4 +238,5 @@ export EXTERNAL_SUBNET_V6="fd55::/64"
229238
export BARE_METAL_PROVISIONER_SUBNET_IPV6_ONLY=true
230239
export DOCKER_USE_IPV6_INTERNALLY=true
231240
export POD_CIDR="fd00:6969::/64"
241+
export ENABLE_NAT64=true # use if host does not support native IPv6
232242
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
nat64_action: setup
2+
ENABLE_NAT64: "{{ lookup('env', 'ENABLE_NAT64') | default('false', true) }}"
3+
TEMPLATES_PATH: "{{ metal3_dir }}/vm-setup/roles/nat64/files"
4+
DNS_UPSTREAM: "{{ lookup('env', 'DNS_UPSTREAM') | default('10.1.0.2', true) }}"
5+
DNS64_PREFIX: "{{ lookup('env', 'DNS64_PREFIX') | default('fd00:ffff:ffff::/96', true) }}"
6+
# Local DNS is only used in IPv6 only env
7+
LOCAL_DNS_V4: "{{ lookup('env', 'LOCAL_DNS_V4') | default('127.0.0.2', true) }}"
8+
LOCAL_DNS_V6: "{{ lookup('env', 'LOCAL_DNS_V6') | default('fd00:abcd::1', true) }}"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
- name: Install IPv6 only required packages
2+
package:
3+
name:
4+
- tayga
5+
- bind9
6+
state: present
7+
when: nat64_action == "setup"
8+
9+
- name: Setup NAT64
10+
block:
11+
- include_tasks: setup_nat.yml
12+
when: nat64_action == "setup"
13+
14+
- name: Teardown NAT64
15+
block:
16+
- include_tasks: teardown_nat.yml
17+
when: nat64_action == "teardown"
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Libvirt needs dnsmasq to manage networks. Installing it will likely automatically
2+
# enable global dnsmasq. We don't want it.
3+
- name: Ensure dnsmasq is stopped
4+
ansible.builtin.systemd_service:
5+
state: stopped
6+
name: dnsmasq
7+
become: yes
8+
9+
- name: Write Tayga configuration
10+
template:
11+
src: "../templates/tayga.conf"
12+
dest: "/etc/tayga.conf"
13+
14+
- name: ensure IP forwarding is on
15+
shell: |
16+
sysctl net.ipv4.ip_forward=1
17+
sysctl net.ipv6.conf.all.forwarding=1
18+
become: yes
19+
20+
- name: Start NAT64 service tayga
21+
ansible.builtin.systemd_service:
22+
state: started
23+
name: tayga
24+
become: yes
25+
26+
- name: Generate Bind9 options file
27+
template:
28+
src: "../templates/named.conf.options"
29+
dest: /etc/bind/named.conf.options
30+
owner: root
31+
group: root
32+
mode: '0644'
33+
34+
# IPv6 only supported on ubuntu, so assume that systemd-resolved is
35+
# the default dns resolution
36+
- name: Disable systemd-resolved
37+
ansible.builtin.systemd_service:
38+
state: stopped
39+
name: systemd-resolved
40+
become: yes
41+
42+
- name: Write /etc/resolv.conf to contain local DNS server
43+
template:
44+
src: "../templates/resolv.conf"
45+
dest: /etc/resolv.conf
46+
owner: root
47+
group: root
48+
mode: '0644'
49+
backup: true
50+
51+
# Note that if the address is regular 127.0.0.1, then this will add a duplicate
52+
# address to the interface but with different subnet mask, the default
53+
# subnetmask is 8 for IPv4 and
54+
- name: Add DNS-server IPv4-address to loopback interface
55+
shell:
56+
cmd: "ip addr add {{ LOCAL_DNS_V4 }}/32 dev lo"
57+
become: yes
58+
ignore_errors: true
59+
60+
- name: Add DNS-server IPv6-address to loopback interface
61+
shell:
62+
cmd: "ip addr add {{ LOCAL_DNS_V6 }}/128 dev lo"
63+
become: yes
64+
ignore_errors: true
65+
66+
- name: Restart Bind9 service
67+
ansible.builtin.systemd_service:
68+
state: restarted
69+
daemon_reload: true
70+
name: named.service
71+
become: yes
72+
73+
- name: Add local hostname to /etc/hosts
74+
ansible.builtin.lineinfile:
75+
path: /etc/hosts
76+
line: 127.0.0.1 {{ ansible_hostname }}

0 commit comments

Comments
 (0)