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

117 lines
5.9 KiB
YAML

---
# Part of role: pia-gateway
# Called by: ansible/playbooks/pia-gateway.yaml
# Description: Default variables for minisforum's PIA WireGuard egress
# gateway for VPN VLAN 50 — policy routing, kill switch, and health
# checks. See ~/repo/homelab/plan.md for the cross-repo contract this
# role implements (fixed values below match that contract).
# PIA regions to try, in order, for WireGuard addKey registration —
# first server (in the first region that has any) to return
# `status: OK` wins. These are API region **ids** from PIA's serverlist
# (https://serverlist.piaservers.net/vpninfo/servers/v6), e.g.
# {"id": "japan", "name": "JP Tokyo", ...} — NOT Gluetun's display names
# ("JP Tokyo", "Hong Kong", "Taiwan") and not the slug you might guess
# from one (confirmed live 2026-08-24: Tokyo's id is "japan", not
# "jp_tokyo" — that matched zero regions on a first pass). Order matches
# the region set already used by the Gluetun sidecars, Hong Kong/Taiwan
# moved ahead of Tokyo after all three Tokyo servers were observed
# failing in practice — see tasks/register.yaml's header for what that
# means and doesn't mean.
pia_region_candidates:
- hk
- taiwan
- japan
# Force a SINGLE region for troubleshooting, bypassing
# pia_region_candidates entirely — e.g. `-e pia_region=hk`. This is also
# an API id, not a display name; same rule as above. Leave null (the
# default) to use the ordered candidate list. tasks/register.yaml
# computes the actual list to try (pia_effective_regions) from whichever
# of these two is set — see that file's own comment for the exact logic,
# not duplicated here to avoid the two drifting out of sync.
pia_region: null
# WireGuard interface identity. Deliberately not "wg0" — that name is
# already used by ansible/roles/wireguard, minisforum's separate *inbound*
# home-VPN server role. Colliding with it would tear down remote access.
pia_wg_interface: pia-wg
pia_wg_config_dir: /etc/wireguard
# Re-registration behaviour — two independent, unambiguous knobs (an
# earlier single pia_force_reregister conflated "re-run the API calls"
# with "get a new keypair", which isn't the same thing and isn't safe to
# conflate — the key-generation task uses `creates:`, so a naive
# "force" only re-ran the PIA API calls with the SAME existing key,
# silently, unless you also knew to delete the key file yourself):
#
# - pia_force_reregister: re-run PIA registration (serverlist lookup,
# token, addKey) even if {{ pia_wg_interface }}.conf already exists.
# Reuses whatever keypair is already on disk (generates one only if
# none exists yet). Use this to pick up a new PIA-assigned peer/server
# without changing minisforum's own WireGuard identity.
# - pia_force_key_rotation: additionally generate a NEW keypair and
# register THAT with PIA (implies pia_force_reregister). Rotation is
# safe-by-construction: the new key is generated to a *.key.new path
# and only promoted (with the old key backed up alongside a timestamp)
# after PIA has actually accepted the new key and the new config has
# been written — see tasks/register.yaml. If registration fails
# partway, the previous key/config are left completely untouched
# rather than ending up in a broken half-state.
pia_force_reregister: false
pia_force_key_rotation: false
# Dedicated policy-routing table for VLAN 50 egress. 0/253/254/255 are
# kernel-reserved (unspec/default/main/local) — this must stay clear of
# those and of any other custom table already registered on the host.
pia_route_table_id: 52
pia_route_table_name: pia
# Rule priority for "from 10.10.50.0/24 lookup pia". Must sort after the
# kernel's built-in local table (priority 0, always evaluated first, keeps
# traffic addressed to the host's own local/Technitium address delivered
# locally per plan.md) and before main (priority 32766).
pia_rule_priority: 100
# Fixed cross-repo contract (see plan.md "Fixed cross-repository
# contract") — VLAN 50 subnet and the Flint gateway that owns the return
# route back to VLAN 50. Not derived from live discovery; change only if
# the contract itself changes.
pia_vlan50_subnet: 10.10.50.0/24
pia_flint_gateway: 10.10.50.1
# Dedicated iptables chain the kill switch governs, jumped into by a
# single rule at FORWARD position 1 matching only
# {{ pia_vlan50_subnet }} — deliberately not a global FORWARD default-
# policy change, so k3s/Flannel's own FORWARD-chain traffic (source
# 10.42.0.0/16, disjoint from VLAN 50) is never affected by this role at
# all. See templates/pia-killswitch.sh.j2 for the full reasoning.
pia_iptables_chain: PIA-VLAN50
# Real parent LAN interface on minisforum — verified live 2026-08-23 via
# `ip -brief addr`; it is enp1s0, not "eth0". Used for MASQUERADE-exclusion
# bookkeeping and documentation only, since NAT/forward rules below key off
# {{ pia_wg_interface }} rather than enumerating the normal interface.
pia_lan_interface: enp1s0
# Reverse-path filtering, applied to {{ pia_wg_interface }} only. The
# kernel default on minisforum (conf.default.rp_filter) is already loose
# (2) at time of writing, so new interfaces inherit loose mode — this task
# pins it explicitly rather than relying on that inherited default holding
# across future changes. Loose mode is required because PIA's return
# traffic can legitimately arrive with routing asymmetry relative to how
# minisforum would route toward that source itself.
pia_rp_filter_mode: 2
# MSS clamping on the pia-wg forward path — OFF by default. plan.md is
# explicit: do not guess this. Flip to true only after Phase 5 canary
# MTU/path-MTU testing (large-packet ping with DF set, across the
# VLAN 50 -> Flint -> minisforum -> pia-wg path) shows fragmentation or
# black-holing that clamping fixes.
pia_mss_clamp_enabled: false
# Health check cadence and thresholds. The check only observes and logs —
# see tasks/healthcheck.yaml — it must never itself open the kill switch.
pia_healthcheck_interval_sec: 60
pia_healthcheck_handshake_max_age_sec: 180