diff --git a/docs/changelog-20260713-office1-dhcp-apply.md b/docs/changelog-20260713-office1-dhcp-apply.md new file mode 100644 index 0000000..4e7d8f4 --- /dev/null +++ b/docs/changelog-20260713-office1-dhcp-apply.md @@ -0,0 +1,97 @@ +# 2026-07-13 -- DOCFIX-193 APPLIED: the Office1 edge now serves DHCP + +Executed the live apply of the Kea DHCP config built (but deliberately NOT applied) under +DOCFIX-193. The Office1 OPNsense edge is now a router **and** a DHCP server for office1-local. + +## What was executed + +Target: `root@10.10.0.1` (`office1-opnsense`), the live, routing Office1 edge. +Path: the D-112(c) steady-state path -- render -> scp over SSH -> SHA-256 verify on the +guest -> install to `/conf/config.xml` -> reboot. + + set -a; . ~/vr1-office1.env; set +a + bash scripts/opnsense-render-config.sh "$OPNSENSE_CONFIG_OUT" + scp -i "$OPNSENSE_SSH_KEY" "$OPNSENSE_CONFIG_OUT" root@10.10.0.1:/tmp/config.new + # guest: sha256 -q /tmp/config.new == local sha256, else abort + # guest: cp /tmp/config.new /conf/config.xml ; reboot + +Rendered config sha256 `bc100bd49c9d2bfec7dfd799e03da7cc5fa48a2608a6521ed2dfea645366a9be`; +guest hash matched before install. Edge returned in ~30s. + +## Measured end state (post-reboot, over SSH) + +- `kea-dhcp4` running (pid 15090), **bound udp4 10.10.0.1:67** -- the deliverable. +- Kea: `enabled=1`, `interfaces=lan`, subnet `10.10.0.0/24`, pool `10.10.0.100-10.10.0.199`, + routers/DNS `10.10.0.1`. +- Router intact: WAN `172.30.1.2`, LAN `10.10.0.1`, default route `172.30.1.1`, + NAT `automatic`, egress to `1.1.1.1` **0.0% packet loss**. +- Console intact: `kern.console=ttyu0`, getty running (DOCFIX-192 holds). + +NOT yet verified end-to-end: an actual client DHCPDISCOVER->lease. No client exists on +office1-local yet. The udp/67 binding + loaded subnet is the strongest available evidence +until Stage 2 puts a host on that LAN. + +## The finding that nearly bit us (and why the push was still safe) + +A full-config push REPLACES `/conf/config.xml` wholesale. The rendered template is 128 +elements; the live config was 796. The overwrite therefore **drops 667 elements** that +OPNsense's own migrations had populated -- including, critically: + + /OPNsense/Firewall/Filter/rules/rule x2 "Default allow LAN to any" (+ IPv6) + +Those two MVC rules are the ONLY pass rules on the box (`/filter/rule` count is **0** -- +the legacy rules were migrated away in 26.1), and pf's base policy is +`block drop in log inet all`. Dropping them with no replacement = an edge that boots but +does not route. + +This was NOT resolved by inference. It was settled from the box's own `/conf/backup/` +trail, which shows the overwrite is **self-healing** and had already been exercised three +times on this exact host: + +| time (07-12) | elements | MVC rules | legacy /filter | revision | +|---|---|---|---|---| +| 07:50 | 713-733 | 0 | **2** | factory nano; rules live in legacy `/filter` | +| 20:25 | 774 | **2** | 0 | after a template push (0 rules) + boot -> `run_migrations` | +| 23:28 | 776 | **2** | 0 | after a template push (0 rules) + boot -> `run_migrations` | +| 23:30 | 796 | **2** | 0 | after a template push (0 rules) + boot -> `run_migrations` | + +Every push of a rule-less config came back with exactly 2 regenerated default-allow-LAN +rules and working routing. The same trail shows `Created web GUI TLS certificate` after +each boot, so `/ca`, `/cert` and `/system/webgui/ssl-certref` regenerate too. + +Today's apply was cycle #4 of that proven mechanism plus one additive block, and the +post-reboot measurement confirms it: 791 elements, both LAN rules back, TLS cert back, +and our `subnet4` **survived** the migration pass. + +## Defect in the executed command (logged, not fixed here) + +Step 1 of the apply -- the pre-install snapshot -- **silently failed**: + + ssh root@10.10.0.1 'cp /conf/config.xml /conf/config.xml.pre-dhcp && echo "... $(sha256 -q ...)"' + -> Illegal variable name. + +root's shell on OPNsense is **tcsh**, not sh. The `$(...)` inside the quoted remote command +is parsed by tcsh, which rejects it -- and because the failure was non-fatal, the install +proceeded **without the named rollback point**. The config was recoverable anyway (OPNsense +keeps `/conf/config.xml.prev` + `/conf/backup/`), so no harm resulted, but the guard did +not exist when it was relied upon. + +Lesson for the apply path: every remote command to this box must be fed to `sh -s` (as the +install and verify steps correctly were), never passed as a quoted string to root's tcsh. + +## Open gap + +The apply path is still **ad-hoc shell**, not a repo script with a harness -- which is why +the tcsh defect above could ship at all. It should become +`scripts/opnsense-apply-config.sh` (snapshot -> scp -> verify -> install -> reboot -> assert), +with the assertions from this session (kea on udp/67, 2 LAN rules, egress, console) as its +post-conditions. LOGGED, not executed -- it is out of scope for this step. + +## Revert + + ssh -i "$OPNSENSE_SSH_KEY" root@10.10.0.1 'sh -s' <<'SH' + cp /conf/config.xml.prev /conf/config.xml && reboot + SH + +Restores the pre-DHCP routing config (the edge routes; nothing serves DHCP). Repo-side, +DOCFIX-193 (`7a2b396`) is the commit that introduced the Kea template block. diff --git a/docs/session-ledger.md b/docs/session-ledger.md index 35013dc..b382ce7 100644 --- a/docs/session-ledger.md +++ b/docs/session-ledger.md @@ -1908,6 +1908,31 @@ +## Jumphost stream -- 2026-07-13: DHCP APPLIED. The Office1 edge is a ROUTER + DHCP SERVER. + +- **DOCFIX-193 APPLIED to the live edge** (was built-but-not-applied at last session's close). + `kea-dhcp4` is running and **bound udp4 10.10.0.1:67**; subnet `10.10.0.0/24`, pool + `.100-.199`, routers/DNS `10.10.0.1`. Router unaffected: WAN 172.30.1.2, LAN 10.10.0.1, + default route 172.30.1.1, NAT automatic, egress 0.0% loss, serial console + getty alive. + Full record: `docs/changelog-20260713-office1-dhcp-apply.md`. +- **KEY AS-BUILT FACT (do not re-derive):** a full-config push to this edge drops ~667 + migration-populated elements, INCLUDING the only 2 firewall pass rules on the box + ("Default allow LAN to any" + IPv6; legacy `/filter/rule` count is 0 in 26.1). OPNsense + **regenerates them on boot** -- proven from `/conf/backup/` across 4 push+boot cycles, and + re-confirmed post-apply (791 elements, both rules back, TLS cert back, our Kea subnet4 + survived). The overwrite is self-healing. Do NOT hand-merge to "preserve" them. +- **OPEN (logged, not executed):** the apply path is still ad-hoc shell, not a harnessed + script. Wanted: `scripts/opnsense-apply-config.sh` (snapshot -> scp -> SHA verify -> install + -> reboot -> assert kea/udp67 + 2 LAN rules + egress + console). The ad-hoc path already + shipped one defect: root's shell on OPNsense is **tcsh**, so a `$(...)` in a quoted remote + command dies with "Illegal variable name" -- the pre-install snapshot silently no-op'd and + the install ran without it. Always feed remote commands to `sh -s`. +- **NOT verified end-to-end:** no client has taken a lease -- office1-local has no host yet. + First Stage-2 host on that LAN is the real proof. +- Platform question (operator asked): recommendation is **stay on OPNsense**, not VyOS -- + reasoning in the prior session's close-out; would be D-113 if it needs a formal ruling. + NOT raised as a decision; no D-number assigned. + ## State facts to remember - beta cluster left at **node_count=2** (deliberate; bonus resize acceptance coverage).