Files
ansible-playbooks/roles/wireguard/tasks/tunnel.yml
T
2026-09-15 17:30:20 +02:00

148 lines
5.3 KiB
YAML

---
- name: "Validate required fields for tunnel {{ _tunnel.interface }}"
ansible.builtin.assert:
that:
- _tunnel.interface is defined
- _tunnel.interface | length > 0
- _tunnel.address is defined
- _tunnel.address | length > 0
fail_msg: |
Tunnel is missing required fields: 'interface' and 'address' are mandatory.
See roles/wireguard/defaults/main.yml for configuration instructions.
success_msg: "Tunnel {{ _tunnel.interface }} validation passed"
- name: "Check if private key exists for {{ _tunnel.interface }}"
ansible.builtin.stat:
path: "{{ wireguard_config_base_path }}/{{ _tunnel.interface }}.privatekey"
register: _tunnel_pkey_file
when: _tunnel.private_key is not defined
- name: "Generate wireguard keys if not present for {{ _tunnel.interface }}"
ansible.builtin.shell: |
set -o pipefail
{{ wireguard_wg_bin }} genkey |
tee {{ wireguard_config_base_path }}/{{ _tunnel.interface }}.privatekey |
{{ wireguard_wg_bin }} pubkey > {{ wireguard_config_base_path }}/{{ _tunnel.interface }}.publickey
args:
executable: /bin/bash
changed_when: true
when:
- _tunnel.private_key is not defined
- not _tunnel_pkey_file.stat.exists
- name: "Retrieve wireguard private key for {{ _tunnel.interface }}"
ansible.builtin.slurp:
src: "{{ wireguard_config_base_path }}/{{ _tunnel.interface }}.privatekey"
register: _tunnel_private_key_b64
when: _tunnel.private_key is not defined
- name: "Set wireguard private key fact from inventory for {{ _tunnel.interface }}"
ansible.builtin.set_fact:
_tunnel_private_key: "{{ _tunnel.private_key }}"
when: _tunnel.private_key is defined
- name: "Set wireguard private key fact from host for {{ _tunnel.interface }}"
ansible.builtin.set_fact:
_tunnel_private_key: "{{ _tunnel_private_key_b64['content'] | b64decode }}"
when: _tunnel.private_key is not defined
- name: "Resolve effective DNS for {{ _tunnel.interface }}"
ansible.builtin.set_fact:
_tunnel_effective_dns: "{{ (_tunnel.dns | default('')) if (unbound_custom_lan_records is not defined) else '' }}"
- name: "Install wireguard config for {{ _tunnel.interface }}"
ansible.builtin.template:
src: wireguard.conf.j2
dest: "{{ wireguard_config_base_path }}/{{ _tunnel.interface }}.conf"
owner: root
group: "{{ wireguard_config_group }}"
mode: "0600"
notify: Reload wireguard tunnel
- name: "Create systemd override directory for wg-quick@{{ _tunnel.interface }}"
ansible.builtin.file:
path: "/etc/systemd/system/wg-quick@{{ _tunnel.interface }}.service.d"
state: directory
mode: "0755"
when: ansible_facts['os_family'] != 'Darwin'
- name: "Deploy systemd override for network dependency for {{ _tunnel.interface }}"
ansible.builtin.template:
src: systemd-override.conf.j2
dest: "/etc/systemd/system/wg-quick@{{ _tunnel.interface }}.service.d/network-dependency.conf"
mode: "0644"
notify: Reload systemd
when: ansible_facts['os_family'] != 'Darwin'
- name: "Enable IP forwarding for {{ _tunnel.interface }}"
ansible.builtin.copy:
dest: /etc/sysctl.d/99-wireguard.conf
content: |
net.ipv4.ip_forward = 1
owner: root
group: "{{ wireguard_config_group }}"
mode: "0644"
notify: Apply sysctl
when:
- _tunnel.server_mode | default(false)
- ansible_facts['os_family'] != 'Darwin'
- name: "Configure the firewall for {{ _tunnel.interface }}"
community.general.ufw:
rule: allow
port: "{{ _tunnel.port }}"
proto: udp
direction: in
comment: "Wireguard VPN ({{ _tunnel.interface }})"
retries: 5
delay: 2
register: _ufw_result
until: _ufw_result is succeeded
when:
- _tunnel.server_mode | default(false)
- _tunnel.port is defined
- ansible_facts['os_family'] != 'Darwin'
- name: "Allow forwarding for {{ _tunnel.interface }}"
community.general.ufw:
rule: allow
route: true
interface_in: "{{ item.in }}"
interface_out: "{{ item.out }}"
comment: "Wireguard VPN forwarding ({{ _tunnel.interface }})"
retries: 5
delay: 2
register: _ufw_result
until: _ufw_result is succeeded
loop:
- in: "{{ _tunnel.interface }}"
out: "{{ _tunnel.primary_interface | default(wireguard_primary_interface) }}"
- in: "{{ _tunnel.primary_interface | default(wireguard_primary_interface) }}"
out: "{{ _tunnel.interface }}"
when:
- _tunnel.server_mode | default(false)
- ansible_facts['os_family'] != 'Darwin'
- name: "Start and enable wg-quick@{{ _tunnel.interface }}"
ansible.builtin.service:
name: "wg-quick@{{ _tunnel.interface }}"
state: started
enabled: true
daemon_reload: true
when: ansible_facts['os_family'] != 'Darwin'
# macOS: no systemd; wg-quick drives wireguard-go directly. Idempotent via the
# list of active interfaces rather than a `creates` path.
- name: "List active wireguard interfaces (macOS)"
ansible.builtin.command: "{{ wireguard_wg_bin }} show interfaces"
register: _wg_active_interfaces
changed_when: false
when: ansible_facts['os_family'] == 'Darwin'
- name: "Bring up tunnel (macOS) for {{ _tunnel.interface }}"
ansible.builtin.command: "{{ wireguard_darwin_bash }} {{ wireguard_wg_quick_bin }} up {{ _tunnel.interface }}"
changed_when: true
when:
- ansible_facts['os_family'] == 'Darwin'
- _tunnel.interface not in _wg_active_interfaces.stdout.split()