fix(svc-net-wireguard-firewalled): stop appending a duplicate rule per deploy

The role ran one shell line, iptables -A FORWARD -i wg0-client -j ACCEPT followed by iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE. An append checks nothing, so every deploy added the same two rules again and both chains grew for the life of the host.

The iptables module probes with -C before it inserts, so the rules converge instead of accumulating, and the two interface names move out of the shell string into variables.

The mechanism stays iptables deliberately. An nftables fragment in its own table cannot do this job: an accept there terminates its own base chain and nothing more, so docker's DROP policy on the filter FORWARD chain still executes and the forwarded packet dies. Measured in a namespace with a real forwarding rig: the appended rule delivers 3 of 3 packets under that policy, the fragment delivers 0 of 3, with the fragment's own counter showing the accept matched all three. The inet family would also have started masquerading IPv6, which iptables -t nat never touched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5 committed 2026-09-15 20:31:20 +02:00
1 parent bf32f57b85
commit 0ddcf033cd
3 files changed
+20 -3

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ This role adapts iptables rules to enable proper connectivity for a WireGuard cl
Optimized for environments with network address translation (NAT), this role:
- Executes shell commands to modify iptables rules.
- Installs its iptables rules through Ansible's `iptables` module, so a redeploy converges instead of appending a duplicate.
- Allows traffic from the WireGuard client interface (e.g. `wg0-client`) and sets up NAT masquerading on the external interface (e.g. `eth0`).
- Works as an extension to the native WireGuard client role.
@@ -1,5 +1,19 @@
---
- name: 🔧 adapt iptable rules
shell: iptables -A FORWARD -i wg0-client -j ACCEPT && iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
- name: "🧱 Accept forwarded traffic from the wireguard client"
become: true
ansible.builtin.iptables:
chain: FORWARD
in_interface: "{{ WIREGUARD_CLIENT_INTERFACE }}"
jump: ACCEPT
state: present
- name: "🧱 Masquerade the client behind the uplink address"
become: true
ansible.builtin.iptables:
table: nat
chain: POSTROUTING
out_interface: "{{ WIREGUARD_UPLINK_INTERFACE }}"
jump: MASQUERADE
state: present
- include_tasks: utils/once/flag.yml
@@ -1,2 +1,5 @@
---
application_id: svc-net-wireguard-firewalled
WIREGUARD_CLIENT_INTERFACE: wg0-client
WIREGUARD_UPLINK_INTERFACE: eth0