Skip to content

WireGuard to the lab jumpbox: terminate on VLAN 30, route it, and record what it costs - #495

Merged
Gerrrt merged 1 commit into
mainfrom
gerrrt/wireguard-jumpbox-remote-f2108b
Sep 17, 2026
Merged

Gerrrt merged 1 commit into
mainfrom
gerrrt/wireguard-jumpbox-remote-f2108b

Conversation

@Gerrrt

@Gerrrt Gerrrt commented Sep 17, 2026

Copy link
Copy Markdown
Owner

What changed

Designs the estate's first remote path, and records the decision rather than
the mechanism: WireGuard terminating on the #436
jumpbox, on ImaginationLAN, reaching the lab only. Adds ADR-0041 and
docs/runbooks/open-the-remote-path.md, widens the lab tripwire's alert for
the peer subnet, and puts forward pointers on the four ADRs this disturbs.

Nothing is deployed. The jumpbox does not exist — #436 is Wave 4 and
unbuilt — so this is authored ahead of the host the way stacks/sensitive was
authored ahead of trinity. Refs #442, not Closes.

Why

#442 asks for the remote path to be designed deliberately rather than
improvised under time pressure. Three things surfaced that the issue did not
carry, and each changed the shape of the work.

ADR-0022's second trigger is fired, and is recorded as fired. The issue
says terminating on 30 "trips nothing". The narrow reading that gets you there
— "any of it" means the sensitive tier, which a lab-only tunnel leaves
unreachable — does not survive ADR-0014, whose reopening clause already says
remote access into VLAN 30 "takes ADR-0008's 'no external access' premise with
it"
, and trigger 2 is that premise made testable. It is also wrong on the
facts: stacks/lab publishes Grafana OSS on alexander, which ADR-0022's own
table says can carry no second factor in any edition. So the trigger is
declared fired and the deferral re-accepted with reasons, which is exactly
what ADR-0022 provides for. Triggers 1 and 3 keep their full force.

Routed, not masqueraded — and that has a cost the issue does not mention.
NAT would collapse every peer into the jumpbox's own address, so the Proxmox
pass #436 needs on Saruman would silently extend to every VPN peer, and the
tripwire would lose attribution. Dropping the NAT fixes that and introduces a
second source subnet on igc0.30 — which the blocks and the tripwire, sourced
from an interface network macro, do not match, and which
LabSegmentReachedInternalNetwork does not match either because it hard-codes
10.0.30.x. Both are widened here. Unwidened, routed mode is strictly worse
than NAT
, because it puts a subnet on the lab interface that nothing watches.

There is no way to be an endpoint today. The WAN address is ISP-assigned by
DHCP, there is no dynamic DNS anywhere in the repo, and ADR-0011 records a
live-verified "no rdr port forwards, no inbound WAN passes beyond DHCP
client replies"
. Recorded as blocking and left to its own decision: a static
IP is a recurring purchase, and a DDNS provider is a third party handed a
continuously-updated pointer to a house whose WAN address security.md
deliberately withholds.

Blast radius

Documentation and one alert regex. No firewall rule is created, no host is
touched, and the tunnel does not exist.

  • No change to network segmentation or firewall rules
  • No new port published to a VLAN that could not already reach the service
  • No credential added outside secrets/*.sops.yaml

The first box is deliberately unticked: this decides a segmentation change
(one inbound WAN pass, one static route, a doubled block set and tripwire on
igc0.30) without making one. docs/firewall-claims.yaml is unchanged and
still accurate — igc0.30's wholesale stays [] — and ADR-0041 records that
the file's per-interface shape cannot express "a second source subnet arrives
here", so a green make check-firewall should not be read as more than it is.

.sops.yaml is deliberately untouched: a jumpbox creation_rule with no
matching file fails scripts/check_sops_rules.py, which is the check that
exists because a dead rule once fell through to the catch-all.

On ADR immutability

ADR-0014's clause says this gets a superseding ADR. It does not, and ADR-0041
says so out loud rather than quietly writing a note.
The clause fired on a
premise ADR-0014 borrowed from ADR-0008, not on anything ADR-0014 decided —
every bullet it decided still governs and is still checked by
build-the-playground.md. ADR-0007's note-block form is the precedent.
ADR-0008, ADR-0011, ADR-0014 and ADR-0022 all get forward pointers; no body is
edited.

Known collision, recorded not resolved

ADR-0014 closes Proxmox 8006 to 10.0.50.0/24 only, and ADR-0039 restates it
as "Nothing on VLAN 30 gains a path to 8006 on either hypervisor." #436's
jumpbox is on VLAN 30 and exists to hold a Proxmox API token. Either that rule
widens — which changes an ADR-0014 decision and would need a partial
supersession rather than a note — or the jumpbox cannot do its job. That is
#436's call and is not taken here.
Routed mode is what stops a widened rule
from silently admitting every peer.

Verification

Offline; nothing touches a host.

  • scripts/check_docs.py — passes; 41 ADRs / 26 runbooks bumped in
    README.md, and the already-stale 39 architecture decision records
    fixed
  • scripts/check_loki_rules.sh — 18 rules parse and evaluate
  • scripts/lint.sh — yamllint, markdownlint, shellcheck, actionlint,
    editorconfig all green
  • check_sops_rules.py, check-sops-encrypted.sh,
    check-tracked-artefacts.sh, check_image_pins.py — all green
  • Full make validate — still running locally against a docker daemon
    shared with ~20 concurrent runs from sibling worktrees. Every check this
    diff touches is green above; CI is the authority here
  • Deployed to the lab and confirmed working — not possible. The jumpbox
    does not exist and the endpoint question is unanswered
  • Docs updated

🤖 Generated with Claude Code

The estate has no remote access, and nothing in docs/ describes an
alternative. This designs one on a quiet afternoon rather than under
time pressure: WireGuard terminating on the #436 jumpbox, on
ImaginationLAN, reaching the lab only.

Three things the issue did not carry:

ADR-0022's second trigger is fired, not dodged. The narrow reading —
that "any of it" means the sensitive tier, which a lab-only tunnel
leaves unreachable — does not survive ADR-0014, whose reopening clause
already says remote access into VLAN 30 "takes ADR-0008's no external
access premise with it", and trigger 2 is that premise made testable.
It is also wrong on the facts: stacks/lab publishes Grafana OSS on
alexander, which ADR-0022's own table says can carry no second factor
in any edition. So the trigger is recorded as fired and the deferral
re-accepted with reasons, which is what ADR-0022 provides for.

Routed, not masqueraded. NAT would collapse every peer into the
jumpbox's address, so the Proxmox pass #436 needs on Saruman would
extend to every peer and the tripwire would lose attribution. The cost
is that a second source subnet arrives on igc0.30: the blocks and the
tripwire are sourced from an interface network macro and
LabSegmentReachedInternalNetwork hard-codes 10.0.30.x, so neither
matches a peer. Both are widened here — unwidened, routed mode is
strictly worse than NAT.

The endpoint does not exist. The WAN address is DHCP-assigned, there is
no dynamic DNS, and ADR-0011 measured "no rdr, no inbound WAN pass".
Recorded as blocking and left to its own issue: a static IP is a
recurring purchase and a DDNS provider is a third party handed a
pointer to the house.

172.31.0.0/24 for the peers, /32 each. An earlier draft used
172.31.30.0/24 to echo the VLAN; rejected because it is one digit from
ifrit's 172.30.30.0/24 and the two mean opposite things in a log line.
172.30. is the range, 172.31. is the tunnel.

ADR-0014's clause says this gets a superseding ADR. It does not, and
declining that is said out loud: the clause fired on a premise borrowed
from ADR-0008, not on anything ADR-0014 decided, and every bullet it
decided still governs. ADR-0007's note form is the precedent. ADR-0008,
ADR-0011, ADR-0014 and ADR-0022 get forward pointers; no body is edited.

Authored ahead of the host, as stacks/sensitive was ahead of trinity.
The jumpbox does not exist and nothing here is deployed.

Refs #442

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Gerrrt
Gerrrt merged commit 84418ae into main Sep 17, 2026
3 checks passed
@Gerrrt
Gerrrt deleted the gerrrt/wireguard-jumpbox-remote-f2108b branch September 17, 2026 23:28
Gerrrt added a commit that referenced this pull request Sep 17, 2026
… counts

`main` is red. Three failures, all from two PRs merging within a minute of each
other without seeing one another:

    README.md:95 claims 41 ADRs; the repository has 42
    README.md:96 claims 26 runbooks; the repository has 27
    ADR-0041 is claimed by 2 files — renumber the one that landed second

#491 gave ADR-0041 to *Run the CRS326 on RouterOS* at 23:27:57Z and #495 gave
the same number to *Terminate the remote path on the lab* at 23:28:52Z. The
ADR-numbering assertion is exactly the check for this; it fired one merge too
late, because each branch was green against a `main` that did not yet hold the
other.

The remote-path ADR landed second, so it becomes **ADR-0042** — which is what
`check_docs.py` asks for by name. Renumbered: the filename, its own H1, eleven
links that targeted it, and thirteen bare `ADR-0041` mentions that meant it.
Every remaining `ADR-0041` in the tree is the CRS326 one, checked individually
rather than by pattern, because the two ADRs shared a number and a
search-and-replace would have renumbered both.

Two of the bare mentions are inside other ADRs' amendment blockquotes (ADR-0011,
ADR-0014). Updating a pointer to a renumbered file is not revising a decision,
so ADR-0001's immutability is not in question — and leaving them would be
leaving a reference to a number that now means something else.

`open-the-remote-path.md`'s pfSense rule descriptions change with it
(`WireGuard peers — ADR-0042`). Safe: the tunnel does not exist, its runbook is
gated on the jumpbox of #436, and `restore-the-firewall.md` still counts the
tripwires as though it were unbuilt.

The two README counts are the arithmetic consequence of the same two merges —
one ADR and two runbooks arrived, and the sentence that counts them did not
move.

Refs #491 #495

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant