Nik Afiq 325d3bc5c7 feat: add pia-gateway role for minisforum PIA WireGuard egress
Registers minisforum as a PIA WireGuard peer for VPN VLAN 50, with a
boot-ordered kill switch (dedicated PIA-VLAN50 iptables chain + a
terminal unreachable route in a dedicated routing table), multi-region
addKey fallback (Hong Kong -> Taiwan -> JP Tokyo, each region's full
server list, in order), and an observability-only health check.

Verified live against minisforum: registration succeeds, wg-quick@pia-wg
is up.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 17:54:16 +09:00

57 lines
3.3 KiB
Django/Jinja

# Managed by Ansible (role: pia-gateway). Do not edit by hand — changes
# are overwritten on the next run of ansible/playbooks/pia-gateway.yaml.
#
# Table = off: wg-quick must NOT touch the main routing table. All routing
# for this interface lives in the dedicated "{{ pia_route_table_name }}"
# table (see tasks/routing.yaml), reached only by traffic sourced from
# {{ pia_vlan50_subnet }}. minisforum's own default route is never
# affected by this interface coming up or down.
[Interface]
PrivateKey = {{ pia_private_key }}
Address = {{ pia_peer.peer_ip }}/32
Table = off
# Kill-switch ordering: the boot-time pia-killswitch.service (see
# tasks/routing.yaml) has already seeded an "unreachable" default route in
# table {{ pia_route_table_name }} and created the dedicated
# {{ pia_iptables_chain }} chain (jumped into from FORWARD position 1 for
# source {{ pia_vlan50_subnet }} only — never touches Flannel/k3s traffic)
# ending in an unconditional LOG+DROP, before this unit ever starts
# (systemd After=/Requires=). PostUp only ever *opens* the path narrower
# than that pre-seeded closed state, by inserting ACCEPT rules ahead of
# the chain's own catch-all; PreDown removes exactly those same rules,
# re-closing it before removing anything else.
PostUp = ip route replace default dev %i table {{ pia_route_table_name }}
PostUp = iptables -I {{ pia_iptables_chain }} 1 -s {{ pia_vlan50_subnet }} -o %i -j ACCEPT
PostUp = iptables -I {{ pia_iptables_chain }} 1 -d {{ pia_vlan50_subnet }} -i %i -m state --state ESTABLISHED,RELATED -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -s {{ pia_vlan50_subnet }} -o %i -j MASQUERADE
{% if pia_mss_clamp_enabled %}
PostUp = iptables -t mangle -A FORWARD -s {{ pia_vlan50_subnet }} -o %i -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
{% endif %}
PreDown = ip route replace unreachable default table {{ pia_route_table_name }}
PreDown = iptables -t nat -D POSTROUTING -s {{ pia_vlan50_subnet }} -o %i -j MASQUERADE
PreDown = iptables -D {{ pia_iptables_chain }} -d {{ pia_vlan50_subnet }} -i %i -m state --state ESTABLISHED,RELATED -j ACCEPT
PreDown = iptables -D {{ pia_iptables_chain }} -s {{ pia_vlan50_subnet }} -o %i -j ACCEPT
{% if pia_mss_clamp_enabled %}
PreDown = iptables -t mangle -D FORWARD -s {{ pia_vlan50_subnet }} -o %i -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
{% endif %}
[Peer]
# PIA WireGuard server actually used — {{ pia_region_name_used }}
# ({{ pia_region_used }}), {{ pia_wg_server_used.cn }}. This is whichever
# region+server in the pia_region_candidates/pia_region fallback order
# (see defaults/main.yaml) responded first with status: OK — not
# necessarily the first-configured region. Re-run the playbook with
# pia_force_reregister (or check journalctl for the last apply's
# per-attempt diagnostics) to see what else was tried before this one.
PublicKey = {{ pia_peer.server_key }}
# The endpoint IP is the server we actually connected to
# (pia_wg_server_used, set by addkey-attempt.yaml on whichever candidate
# in servers.wg succeeded), not a field from the addKey JSON response —
# matching connect_to_wireguard_with_token.sh, which builds the Endpoint
# from its own $WG_SERVER_IP rather than trusting a response field for it.
Endpoint = {{ pia_wg_server_used.ip }}:{{ pia_peer.server_port }}
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25