--- - name: Ensure WireGuard kernel module is loaded ansible.builtin.modprobe: name: wireguard state: present - name: Persist WireGuard kernel module across reboots ansible.builtin.copy: dest: /etc/modules-load.d/wg-easy.conf content: | wireguard nft_masq mode: '0644' # WireGuard Easy VPN (v15) — UI at 10.10.0.36:51821 on npm_internal, fronted by NPM # (vpn.boxswapper.io -> http://10.10.0.36:51821). Login: admin / wireguard_ui_password from vars.yml. # v15 init is ONE-TIME: the INIT_* envs below are consumed only when /etc/wireguard/wg-easy.db is ABSENT. # Force a re-init (wipes clients + server keys): podman rm -f wg-easy && rm -rf /opt/apps/wireguard, then re-run. # NAT from VPN clients to LAN/apps (10.10.1.0/24 -> 10.10.0.0/24) uses NFTABLES hooks — # wg-easy's iptables-legacy hooks cannot init the nat table inside a podman netns # ("Table does not exist"), which makes wg-quick up fail and every client API call # 500 with "Unable to access interface". scripts/patch-wg-hooks.py rewrites the hooks # to nft rules and the tasks below restart wg-easy to apply them. See the official # wg-easy "Podman + nftables" tutorial. # EXTERNAL ACCESS: router MUST forward UDP 51820 -> 192.168.0.98. Test from LTE, # not your LAN — NAT hairpin breaks public-IP handshakes from inside the network. - name: Deploy WireGuard Easy (wg-easy) containers.podman.podman_container: name: wg-easy image: "{{ wireguard_image }}" restart_policy: always recreate: true state: started env: INIT_ENABLED: "true" INIT_USERNAME: "{{ wireguard_ui_username }}" INIT_PASSWORD: "{{ wireguard_ui_password }}" INIT_HOST: "{{ wireguard_host }}" INIT_PORT: "{{ wireguard_port }}" INIT_DNS: "{{ wireguard_default_dns }}" INIT_IPV4_CIDR: "{{ wireguard_cidr }}" INIT_IPV6_CIDR: "{{ wireguard_ipv6_cidr }}" INIT_ALLOWED_IPS: "{{ wireguard_allowed_ips }}" PORT: "{{ wireguard_ui_port }}" INSECURE: "true" DISABLE_VERSION_CHECK: "true" ports: - "{{ wireguard_port }}:{{ wireguard_port }}/udp" - "{{ wireguard_ui_port }}:{{ wireguard_ui_port }}/tcp" volumes: - "/opt/apps/wireguard:/etc/wireguard:z" - "/lib/modules:/lib/modules:ro" cap_add: - NET_ADMIN - SYS_MODULE - NET_RAW sysctl: net.ipv4.ip_forward: "1" net.ipv4.conf.all.src_valid_mark: "1" net.ipv6.conf.all.disable_ipv6: "0" net.ipv6.conf.all.forwarding: "1" net.ipv6.conf.default.forwarding: "1" network: - npm_internal ip: '{{ wireguard_ip }}' - name: Copy wg-easy hook repair script ansible.builtin.copy: src: ./scripts/patch-wg-hooks.py dest: /opt/apps/wireguard/patch-wg-hooks.py owner: root group: root mode: '0755' - name: Replace iptables hooks with nftables (iptables-legacy broken in podman) ansible.builtin.command: python3 /opt/apps/wireguard/patch-wg-hooks.py register: wg_hooks_patch changed_when: "'patched hooks' in wg_hooks_patch.stdout" - name: Restart wg-easy to apply nft hooks ansible.builtin.command: podman restart wg-easy when: "'patched hooks' in wg_hooks_patch.stdout" - name: Wait for WireGuard Easy web UI ansible.builtin.wait_for: host: '{{ wireguard_ip }}' port: '{{ wireguard_ui_port }}' delay: 5 timeout: 120