The first real enrolment failed:
$ ./infra/vpn-ingress/new-peer.sh keith-phone 2
new-peer.sh: 10.90.0.2/32 is already allocated ... Pick another octet.
Nothing was allocated -- peers.yaml had zero peers, as the pod's init
container reported (`0 peer(s)`). The duplicate-address check grepped the
whole file, and peers.yaml line 66 is a COMMENTED-OUT example stanza I wrote
containing literally `# AllowedIPs = 10.90.0.2/32`. So .2 -- the first
address anyone would ask for, and the one my own runbook uses as its example
-- was permanently un-allocatable, with a message asserting the opposite of
the truth.
Fixed on both sides, because either alone leaves the trap half-set:
* the check strips comment lines before matching, which is the correct
predicate rather than a patch: a real stanza's AllowedIPs line is never
commented, while the template and every peer's `# <name> — <owner>` label
always are.
* the example now uses placeholders that cannot match anything real
(`10.90.0.<octet>/32`, `<device-name>`), so a future grep cannot be fooled
by it either.
The bug class is worth naming: a documentation example that is byte-identical
to real data will eventually be parsed as real data. This is its third
instance in this component -- flux-postbuild-substitution flagged a `${...}`
written inside a comment explaining that hazard, and no-plaintext-secrets
flagged the .sops.yaml.example because a kustomization named it in a comment.
Make examples structurally unmatchable, not merely commented out.
Verified by running it: .2 now renders a profile (via the real
`sops -d | wg pubkey` path, which this also proves against the operator's
actual key for the first time), and an uncommented duplicate stanza is still
correctly refused.
No sensitive paths; merges on a green gate.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
4.2 KiB
date, branch, title
| date | branch | title |
|---|---|---|
| 2026-10-06 | fix/new-peer-ignores-template-comments | new-peer.sh refused the first VPN address because its duplicate check matched its own commented-out example |
What changed
The operator's first real enrolment failed:
$ ./infra/vpn-ingress/new-peer.sh keith-phone 2
new-peer.sh: 10.90.0.2/32 is already allocated in platform/vpn-ingress/peers.yaml. Pick another octet.
Nothing was allocated. peers.yaml had zero peers — the pod's own init container reported
wg0.conf assembled: 0 peer(s). The duplicate-address check grepped the WHOLE file, and
peers.yaml line 66 is a commented-out example stanza I wrote, containing literally
# AllowedIPs = 10.90.0.2/32. So .2 — the first address anybody would ask for, and the one my
own runbook uses in its example — was permanently un-allocatable, with a message asserting the
opposite of the truth.
Both halves fixed, because either alone leaves the trap half-set:
infra/vpn-ingress/new-peer.sh: the check strips comment lines before matching (grep -vE '^[[:space:]]*#' | grep -q …). That is the correct predicate rather than a patch: a real stanza'sAllowedIPsline is never commented, while both the template and every peer's# <name> — <owner>label always are.platform/vpn-ingress/peers.yaml: the example now uses placeholders that cannot match anything real —# <device-name> — <owner>, enrolled <YYYY-MM-DD>andAllowedIPs = 10.90.0.<octet>/32. A future grep cannot be fooled by it either.
The bug class is worth naming: a documentation example that is byte-identical to real data will
eventually be parsed as real data. The same shape caused two earlier defects in this component —
flux-postbuild-substitution flagged a ${...} written inside a comment explaining that hazard,
and no-plaintext-secrets flagged the .sops.yaml.example because the kustomization named it in a
comment. Three instances now; the rule is to make examples structurally unmatchable, not merely
commented out.
No sensitive paths (platform/vpn-ingress/**, infra/vpn-ingress/**, decisions/**), so this
merges on a green gate with no override.
What I verified
Run in the pinned toolbox against the live tree, 2026-10-06.
.2now works, and it went through the REAL key path:new-peer.sh keith-phone 2rendered a profile withAddress 10.90.0.2/32,AllowedIPs = 10.90.0.2/32in the printed stanza, full tunnel, at.cache/wg-peers/keith-phone.conf(0600). The throwaway keypair was deleted after.- This incidentally closes a gap left open in #60: the profile could only have been rendered by
deriving the server's public key via
sops -d --extract … | wg pubkeyfromwg-server-key.sops.yaml— so that pipeline is now proven against the operator's real key, not just against a placeholder it was expected to reject. - A real duplicate is still refused. Appending an uncommented
AllowedIPs = 10.90.0.5/32stanza and asking for octet 5 produced the correct refusal; the tree was restored afterwards andgit diff --statshowed only the intended template edit. - Zero active allocations remain:
grep -vE '^[[:space:]]*#' peers.yaml | grep -c AllowedIPs→0. - Toolbox gate: rc 3, with
spec-immutability,decisions-log,lint,l0,render-golden,l1andapproval-policyall pass; rc 3 is the two environmental skips (gitea-settings, conformance L2/L3). Not green. Reported as rc 3.
What I did NOT verify
- No peer is enrolled by this branch. The keypair I generated was a throwaway and was deleted;
the operator must run the script themselves, since the device's private key must exist only on
the device. So the end of the enrolment path — a stanza merged into
peers.yaml, Flux rolling the DaemonSet, andwg showlisting a peer — is still unexercised. - The duplicate-NAME warning (
grep -qi "^ *# *${NAME} ") deliberately still inspects comments, because per-peer labels are comments. It is no longer tripped by the template, but I did not test it against a real enrolled peer. - Nothing about client traffic. The WAN port-forward is still not configured and there is still no off-LAN vantage point, so the tunnel has carried no packet.