Add the at-home peer (10.90.0.3) and list ${WG_CLIENT_CIDR} in the PiHole
DNS frontends' loadBalancerSourceRanges. wg0 is a Cilium device, so tunnel
clients reach the source-range check untranslated; a LAN-only list is
predicted to drop their DNS. Adds an L0 guard that both ranges are present
and every range is RFC1918, and corrects the README's SNAT and IPv6 claims.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
6.6 KiB
date, branch, title
| date | branch | title |
|---|---|---|
| 2026-11-08 | fix/wg-home-peer-pihole-sources | Enrol keith-phone-home and admit the WireGuard subnet to PiHole's DNS frontends |
What changed
platform/vpn-ingress/peers.conf: new peerkeith-phone-home,10.90.0.3/32, key generated on the phone. It is thehomehalf of a two-tunnel profile (Endpoint192.168.2.10:51820), because the router has no hairpin NAT andwan.is unusable from inside the house.apps/wave-0/pihole-dns.yaml:${WG_CLIENT_CIDR}added after${LAN_CIDR}on bothpihole-dns-udpandpihole-dns-tcp. This file is hand-written, not rendered (catalog/render.pyonly lists it inEXTRA_UNITS), so it is the source of truth and--emit-waveswas not run.catalog/pihole.yamlchanges in one comment only, so nothing renders differently.- New L0 guard
conformance/tests/l0-static/pihole-dns-source-ranges(whole-repo scope, R-DNS-03): every PiHole LoadBalancer Service lists both tokens, and every range resolves through cluster-vars (base and prod overlay) to RFC1918. platform/vpn-ingress/README.md: corrected "clients arrive SNATed" (false for LoadBalancer VIPs) and the IPv6 claim (Android'sGoBackendblocks IPv6 for a single-peer default route); documented the home/away pattern, whywan.fails from inside, and the prod-1 dependency.
Rollout on merge: the wg-peers ConfigMap hash changes (m9kcckcb5t -> 29mf6k867t), so the
DaemonSet rolls, maxUnavailable: 1, maxSurge: 0, one node at a time in an order the controller
chooses. Each roll deletes and recreates that node's wg0, so an established session on that node
drops and must re-handshake. keith-phone has no session today (see below), so nothing is
interrupted in practice.
The one risk worth watching: wg0 is in Cilium's auto-detected device list and devices is
still unpinned. On 2026-10-06 a crash-looping wg0 churned that list and all three nodes lost their
main-table default route. A rollout is one delete/create per node, not a loop, so it should not
repeat that, but after merge check ip route get 1.1.1.1 on each node.
The Service change is an in-place update: same VIP, same Service objects, no pod restart.
Checked on the rest of the path and left unchanged:
- Gateway VIP
.20(gateway/cilium-gateway-shared): no source ranges, live. Nothing needed. pihole-allow-lan-dns-ingressadmitsworldon :53, which covers an untranslated 10.90.0.x source.platform/vpn-ingress/networkpolicy.yamldoes not apply to a hostNetwork pod.- No L0 check pinned these ranges to the LAN; none needed updating.
Flagged, not changed (out of scope):
runbooks/vpn-remote-access.md"Known limitations" repeats both corrected claims, and step 3 of its verification table says the Gateway sees ahost-identity source.platform/vpn-ingress/daemonset.yaml's header repeats the SNAT claim.clusters/base/cluster-vars.yaml's substitution-coverage comment does not listWG_CLIENT_CIDRunder apps-wave-0. The variable is defined and substituted there regardless.peers.confstill says "No peers enrolled yet" above the enrolled peers.- PiHole's NodePorts are not covered by source ranges (already acknowledged in
pihole-dns.yaml). Tunnel clients can reach node addresses, so they can reach those too; they are enrolled devices. - Pinning Cilium's
devicesremains the open hardening for the churn risk above.
What I verified
Live, read-only, 2026-11-08:
cilium-dbg statuson one agent:KubeProxyReplacement: True [eno1 ..., wg0 10.90.0.1, tun0 ...]andMasquerading: BPF [eno1, wg0, tun0]. Sowg0really is a load-balancing device.pihole-dns-tcpand-udpcarryranges=[192.168.2.0/24]; no other LoadBalancer Service has source ranges. Livecluster-vars:WG_CLIENT_CIDR=10.90.0.0/24,LAN_CIDR=192.168.2.0/24.wg show wg0 latest-handshakeson all three pods:keith-phone=0.wan.home.keithjackson.devresolves publicly to a single A record (via 1.1.1.1).
Offline, in the pinned toolbox:
flux envsubst --strictrenders both Services to[192.168.2.0/24, 10.90.0.0/24].kubectl kustomize platform/vpn-ingress:wg-peers-m9kcckcb5ton the oldpeers.conf,wg-peers-29mf6k867ton the new one.- Guard mutation test, each restored afterwards. Baseline passes (22 assertions, 2 Services). All
seven fail with rc 1 and a named message: WG range dropped from one Service;
0.0.0.0/0added;8.8.8.0/24added;WG_CLIENT_CIDRmade public in cluster-vars; prod overlay setsLAN_CIDRto0.0.0.0/0; ranges block removed; undefined token added. - The Android claim was read from upstream
GoBackend.java(wireguard-android, master): theallowFamilycalls are skipped whensawDefaultRoute && config.getPeers().size() == 1. - Gate (pinned toolbox, from this worktree, on the committed tree): rc 3, UNDETERMINED, not
green.
gitea-settingsis the only stage counted as not run (no token locally).conformance(L2/L3) also skipped as the declared no-cluster gap.spec-immutability,decisions-log,lint,l0(including the new guard),render-goldenandl1passed.approval-policyreported N/A offline; it will be red in CI until the operator overrides, becauseconformance/**is sensitive. git merge-treeagainstfeat/pihole-explicit-lan-names(#157): no conflicts.
What I did NOT verify
DROP_NOT_IN_SRC_RANGEwas NOT observed live. It is a prediction from Cilium's source. Only thewg0device membership was measured; the drop itself has never been seen.- Full-tunnel behaviour was NOT observed live, on either profile. No handshake has ever been
recorded for any peer:
keith-phonereads0on all three pods (a counter that resets when a pod restarts, last 22 days ago, so it covers only the time since; earlier entries here also record none). - That tunnel DNS works after this change, including the reply path from a backend on another node
and from one on the ingress node. That needs the phone:
digover the tunnel, while watchingcilium-dbg monitor --type drop. - That the Gateway serves tunnel clients. Its source is predicted to be
world, as for a LAN browser, which is what the apps admit; not tested. - That the new peer's key is the one on the phone. I copied the public key from the brief.
- That the rollout leaves node default routes intact. Reasoned from the 2026-10-06 incident being a loop, not measured.
- That a LAN client is unaffected while the Service's source ranges are updated. Expected, not measured.
- IPv6 behaviour on any non-Android client.
- That the WAN forward targets prod-1: it is off-repo.