---
- include_tasks: utils/once/flag.yml
- name: "π Include dependency 'user-administrator' for the mesh transport"
include_role:
name: user-administrator
when:
- "'svc-swarm-node' in group_names"
- run_once_user_administrator is not defined
- name: "π Fail when the inventory carries no mesh for this host"
ansible.builtin.fail:
msg: >-
{{ inventory_hostname }} is to carry WireGuard but its inventory declares
no WireGuard mesh. Run cli.administration.inventory.mesh over every
inventory of this deploy before the play; the mesh is written by that one
cross-host pass, never by this role.
when: WIREGUARD_MESH_NAMES | length == 0
- name: "π¦ Install the WireGuard tooling"
package_install: # nocheck: package-install the mesh bootstrap runs before the update role and the batch run
id:
- wireguard
- iproute
- iputils-ping
- name: "π¦ Install automtu via pip (system-wide)"
include_role:
name: sys-pip-install
vars:
package_name: "automtu==1.1.1"
global_install: true
- name: "π Resolve every endpoint this host dials to its IPv4 address"
ansible.builtin.command:
argv:
- getent
- ahostsv4
- "{{ endpoint }}"
loop: "{{ WIREGUARD_DIALLED_ENDPOINTS }}"
loop_control:
loop_var: endpoint
register: wireguard_underlay_lookup
changed_when: false
failed_when: wireguard_underlay_lookup.rc not in [0, 2]
- name: "π§ Pin every dialled endpoint to the IPv4 address it resolved to"
ansible.builtin.set_fact:
wireguard_underlay: "{{ wireguard_underlay_lookup.results | mesh_underlay }}"
changed_when: false
- name: "π Fail when a dialled endpoint has no IPv4 address on this host"
ansible.builtin.fail:
msg: >-
{{ unresolved | join(', ') }} resolved to no IPv4 address on
{{ inventory_hostname }}. The mesh underlay is IPv4: give the name an A
record this host's resolver answers, give the host an IPv4 address, or
pass the peer's IPv4 address as --endpoint to
cli.administration.inventory.mesh.
vars:
unresolved: "{{ WIREGUARD_DIALLED_ENDPOINTS | reject('in', wireguard_underlay) | list }}"
when: unresolved | length > 0
- name: "π Detect the path MTU the tunnel has to fit inside"
ansible.builtin.command:
argv:
- "{{ ansible_python_interpreter }}"
- -m
- automtu
- --pmtu-target
- "{{ WIREGUARD_PMTU_TARGETS | join(',') }}"
- --pmtu-policy
- min
- --print-mtu
- effective
register: wireguard_pmtu
changed_when: false
- name: "β
Assert automtu returned an integer MTU"
ansible.builtin.assert:
that:
- wireguard_pmtu.stdout is match('^[0-9]+$')
- (wireguard_pmtu.stdout | int) >= 576
- (wireguard_pmtu.stdout | int) <= 9000
fail_msg: "automtu returned unexpected MTU output: '{{ wireguard_pmtu.stdout | default('') }}'"
- name: "π§ Derive the tunnel and overlay MTUs from the measured path"
ansible.builtin.set_fact:
wireguard_mtu: "{{ wireguard_pmtu.stdout | int - WIREGUARD_HEADER_BYTES | int }}"
wireguard_overlay_mtu: >-
{{ wireguard_pmtu.stdout | int
- WIREGUARD_HEADER_BYTES | int
- WIREGUARD_VXLAN_HEADER_BYTES | int }}
changed_when: false
- name: "π Create {{ WIREGUARD_CONFIG_DIR }}"
ansible.builtin.file:
path: "{{ WIREGUARD_CONFIG_DIR }}"
state: directory
owner: root
group: root
mode: "0700"
- name: "π Render the interface of every mesh this host belongs to"
ansible.builtin.template: # nocheck: stack-host-template (per-host /etc/wireguard)
src: wg.conf.j2
dest: "{{ [WIREGUARD_CONFIG_DIR, mesh_name ~ '.conf'] | path_join }}"
owner: root
group: root
mode: "0600"
loop: "{{ WIREGUARD_MESH_NAMES }}"
loop_control:
loop_var: mesh_name
vars:
mesh: "{{ WIREGUARD_ACTIVE_MESHES[mesh_name] }}"
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
register: wireguard_rendered
- name: "π Enable forwarding on the hub"
ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: "1"
sysctl_set: true
state: present
reload: true
when: WIREGUARD_IS_HUB | bool
- name: "πΏ Bring every rendered interface up"
ansible.builtin.include_tasks: utils/interface_up.yml
loop: "{{ WIREGUARD_MESH_NAMES }}"
loop_control:
loop_var: mesh_name
- name: "β
Gate: prove the layer works before anything depends on it"
ansible.builtin.include_tasks: 01_gate.yml