`[vpn_egress_gateway_prod]` is a SEPARATE inventory group with its own vaulted credentials, not a second member of `[vpn_egress_gateway]`. That group is the default target of playbooks/vpn.egress.gateway.yml, so adding a prod host to it would mean a flagless `ansible-playbook playbooks/vpn.egress.gateway.yml` silently reconfigures a prod node's kill-switch datapath. Prod is reached only by asking for it, with `--list-hosts` first: -e vpn_egress_gateway_hosts=vpn_egress_gateway_prod --limit k3s-prod-3 Node 3 because node 1's SSD was replaced yesterday (least operational history) and node 2 carries the §0b memattr history. Exactly one node, forever: Cilium OSS picks "the first node in lexical ordering" among nodeSelector matches, so a labelled k3s-prod-1 would silently steal the gateway role — and the public egress IP — from k3s-prod-3, with no event and no alert. vault.yml is a verbatim copy of the dev group's CIPHERTEXT: same PIA account per the operator, and copying the encrypted blob means the plaintext was never decrypted to disk, through a shell, or into a transcript. The two files are now independent — rotating means re-encrypting both, which is the right behaviour for two environments. Also corrects a stale inventory note that claimed k3s-prod-3 has wlp1s0 up on 192.168.2.195. Re-verified before choosing it as the gateway, because a second live uplink in a kill-switch datapath is not a footnote: there is no wl* netdev on the node at all, no phy80211 device, the /etc/network/interfaces wlp1s0 stanza is commented out, 192.168.2.195 is assigned nowhere, and the only IPv4 addresses are lo and eno1 192.168.2.12. Nothing needed bringing down. Co-Authored-By: Claude Opus 5 <[email protected]>
39 lines
2.4 KiB
YAML
39 lines
2.4 KiB
YAML
---
|
|
# Stand up the node-level VPN egress tunnel on the designated egress-gateway node(s)
|
|
# (ADR-0015). Host infra — the split-tunnel OpenVPN service + policy routing + scoped
|
|
# nftables kill-switch. The CiliumEgressGatewayPolicy that steers vpn-class pods to this
|
|
# node's tun0 is authored separately as GitOps (platform/), against the same contract:
|
|
# * inventory group `vpn_egress_gateway` (this play's default target)
|
|
# * k8s node label homelab.keithjackson.dev/egress-gateway=true (set by the role)
|
|
# * host tunnel iface tun0 (split-tunnel; never the node default route)
|
|
#
|
|
# Secrets come from Ansible vault (inventory/group_vars/vpn_egress_gateway/vault.yml):
|
|
# vault_vpn_egress_gateway_pia_username, vault_vpn_egress_gateway_pia_password, vault_vpn_egress_gateway_pia_ovpn
|
|
# Run: ansible-playbook playbooks/vpn.egress.gateway.yml
|
|
#
|
|
# TARGET (R-ENV-02): the group is a var, defaulting to dev's `vpn_egress_gateway`. PROD has
|
|
# its own group as of 2026-09-12 — `vpn_egress_gateway_prod` (k3s-prod-3), with its own
|
|
# vaulted credentials in group_vars/vpn_egress_gateway_prod/:
|
|
#
|
|
# ansible-playbook playbooks/vpn.egress.gateway.yml \
|
|
# -e vpn_egress_gateway_hosts=vpn_egress_gateway_prod --limit k3s-prod-3 --list-hosts
|
|
#
|
|
# The var is `vpn_egress_gateway_hosts`, NOT the cluster-wide `k3s_hosts`. Prod is
|
|
# deliberately NOT a member of the default group, so a flagless run cannot reach it — and
|
|
# `--list-hosts` before every run is the habit that catches a mistyped flag, which in this
|
|
# repo has silently targeted all three dev nodes before.
|
|
#
|
|
# THE LABEL IS WHAT ARMS THE DATAPATH. The CiliumEgressGatewayPolicy is already reconciled
|
|
# on both clusters, so the instant a node carries
|
|
# `homelab.keithjackson.dev/egress-gateway=true` steering goes live. The role sequences
|
|
# kill-switch -> tunnel -> re-steer -> label, with the label LAST, on purpose. Never apply
|
|
# that label by hand, and never run this play with tags that skip the tunnel or kill-switch
|
|
# tasks. Exactly ONE node per cluster may carry it (Cilium OSS picks the lexically first
|
|
# match, so a labelled k3s-prod-1 would silently steal the role from k3s-prod-3).
|
|
- name: Configure the node-level VPN egress gateway
|
|
hosts: "{{ vpn_egress_gateway_hosts | default('vpn_egress_gateway') }}"
|
|
become: true
|
|
gather_facts: true # package module + service facts need os facts
|
|
roles:
|
|
- role: vpn_egress_gateway
|