From 4cde690008340baffab1aa4523161f07a47f4b2e Mon Sep 17 00:00:00 2001 From: Keith Jackson Date: Sat, 22 Aug 2026 16:39:30 -0500 Subject: [PATCH] fix(vpn): PIA OpenVPN port 53 -> 8080 (53 is DNS-intercepted on this network) Port 53 also got no PIA replies: this network transparently intercepts outbound :53, so dig works but OpenVPN-over-53 is swallowed. An NTP/123 probe from the host returned fine, proving arbitrary non-53 UDP works bidirectionally (so it's port-specific: 1197 conflict + 53 DNS-intercept, not a UDP block). Move to 8080 (community-verified for PIA, non-DNS, non-1197). Co-Authored-By: Claude Opus 4.8 --- DECISIONS-LOG.md | 8 ++++++++ clusters/base/cluster-vars.yaml | 5 +++-- platform/kustomization.yaml | 6 +++--- platform/vpn-egress/gluetun-gateway.yaml | 11 ++++++----- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/DECISIONS-LOG.md b/DECISIONS-LOG.md index 4c2f19a..550b9cd 100644 --- a/DECISIONS-LOG.md +++ b/DECISIONS-LOG.md @@ -517,3 +517,11 @@ traverse this network (the `dig @9.9.9.9` test) and no conflict. Added cluster-v wired to gluetun `OPENVPN_ENDPOINT_PORT`, and re-enabled `vpn-egress`. Port-forwarding is an over-tunnel PIA API call, unaffected by the transport port. (WireGuard fallback would also have worked — PIA WG is UDP/1337, not 1197 — but staying on Gluetun+OpenVPN is the smaller change.) + +**Correction: 53 didn't work either → settled on 8080.** Deploying on 53 still got no PIA replies — +this network **transparently intercepts outbound :53 (DNS)**, so `dig` succeeds (proxy forwards it) but +OpenVPN-over-53 is swallowed. Cheap decisive test: a raw **NTP/123** probe from the host got a reply → +arbitrary non-53 UDP works bidirectionally, so it's port-specific (1197 conflict + 53 DNS-intercept), not a +UDP block. Moved `OPENVPN_ENDPOINT_PORT` to **8080** (community-verified for PIA, non-DNS, non-1197). +Also hit the `${VAR}`→int-coercion gotcha (k8s "expected string, got 53") — the port is hardcoded as a +quoted string in gluetun-gateway.yaml (Longhorn-fix precedent), not a cluster-var. diff --git a/clusters/base/cluster-vars.yaml b/clusters/base/cluster-vars.yaml index c7ae857..8d84364 100644 --- a/clusters/base/cluster-vars.yaml +++ b/clusters/base/cluster-vars.yaml @@ -43,9 +43,10 @@ data: # which lacks PF; NOT a US region, which also lack PF) CLUSTER_POD_CIDR: "10.0.0.0/8" # Cilium cluster-pool pod CIDR (pods get 10.x); Gluetun # FIREWALL_OUTBOUND_SUBNETS = allow these to egress via the tunnel - # NOTE: PIA OpenVPN port is HARDCODED "53" in gluetun-gateway.yaml, not a cluster-var — Flux/kustomize + # NOTE: PIA OpenVPN port is HARDCODED "8080" in gluetun-gateway.yaml, not a cluster-var — Flux/kustomize # strips quotes around a bare "${VAR}" so a numeric value coerces to int, which k8s rejects for an env - # value (same gotcha as the Longhorn numberOfReplicas fix). See DECISIONS-LOG. + # value (same gotcha as the Longhorn numberOfReplicas fix). 8080 chosen: 1197 conflicts with the home + # OpenVPN server, 53 is DNS-intercepted here; both confirmed by tcpdump. See DECISIONS-LOG. # --- identity / auth (ADR-0014) --- KEYCLOAK_REPLICAS: "1" # dev 1 | prod 2 (HA) diff --git a/platform/kustomization.yaml b/platform/kustomization.yaml index c6a4d25..6dddd77 100644 --- a/platform/kustomization.yaml +++ b/platform/kustomization.yaml @@ -17,9 +17,9 @@ resources: - cert-manager # Let's Encrypt DNS-01 wildcard — needs SOPS `cloudflare-api-token` - externaldns # auto DNS records — needs SOPS `cloudflare-api-token` - gateway # Gateway API listeners (ingress; replaces Traefik) - # ---- Phase 3 — Egress / VPN (PIA OpenVPN-UDP on port 53, ADR-0013 amendment). - # NOT PIA's default 1197 — this home network NATs its own OpenVPN server on WAN:1197, which - # collided with the outbound PIA-on-1197 flow (tcpdump-confirmed; DECISIONS-LOG). ---- + # ---- Phase 3 — Egress / VPN (PIA OpenVPN-UDP on port 8080, ADR-0013 amendment). + # NOT 1197 (home NATs its own OpenVPN server there) and NOT 53 (network intercepts DNS/:53); + # both tcpdump-confirmed. See DECISIONS-LOG. ---- - vpn-egress # shared Gluetun PIA/OpenVPN-UDP egress gateway + kill-switch (ADR-0013) # ---- Phase 4 — Observability (creds provisioned via SOPS: grafana-admin, alertmanager-smtp) ---- - monitoring # kube-prometheus-stack + Loki + Alloy + Alertmanager diff --git a/platform/vpn-egress/gluetun-gateway.yaml b/platform/vpn-egress/gluetun-gateway.yaml index b5202ae..9432ad1 100644 --- a/platform/vpn-egress/gluetun-gateway.yaml +++ b/platform/vpn-egress/gluetun-gateway.yaml @@ -41,11 +41,12 @@ spec: - name: VPN_TYPE value: "openvpn" # OpenVPN-UDP — satisfies "protocol != TCP" (R-NET); ADR-0013 amended - name: OPENVPN_ENDPOINT_PORT - value: "53" # NOT 1197 — the home network NATs its own OpenVPN server there, - # so PIA-on-1197 replies never return (tcpdump-confirmed; DECISIONS-LOG). - # Hardcoded (not a ${var}): Flux/kustomize strips quotes on "${VAR}" - # → int coercion, which k8s rejects for env value (Longhorn-fix gotcha). - # Valid PIA ports: 53, 1194, 1197, 1198, 8080, 9201. + value: "8080" # NOT 1197 (home NATs its own OpenVPN server there → replies never + # return) and NOT 53 (this network transparently intercepts :53 DNS, + # swallowing OpenVPN-over-53). 8080 verified: arbitrary UDP works here + # (NTP/123 test). Hardcoded string (not ${var}): Flux/kustomize strips + # quotes on "${VAR}" → int coercion k8s rejects (Longhorn-fix gotcha). + # Valid PIA ports: 53, 1194, 1197, 1198, 8080, 9201. See DECISIONS-LOG. # PIA credentials from the SOPS secret (Gluetun's PIA OpenVPN creds env): - name: OPENVPN_USER valueFrom: