diff --git a/BACKLOG.md b/BACKLOG.md index 2e1838b..fda8095 100644 --- a/BACKLOG.md +++ b/BACKLOG.md @@ -1640,10 +1640,27 @@ rules", and of doing that discovery with an audit device rather than a grep. The found 19 files and could not have found this, because **the fact is not in the files** — it is a property of the running host. -**Pin the address before writing rules.** An allowlist naming both is correct but papers -over the cause; the real fix is deciding what `enp1s0` is for and either giving it a -losing route metric or taking it off that subnet. See §4.12 — same configuration, and -this is its second distinct symptom. +**PINNED 2026-09-06, not yet applied.** `h4-dns-resolvers.yml` now emits +`route-metric: 200` on the non-primary link, so `enp2s0` (.160) wins deterministically +and the H4's source address stops depending on what the kernel felt like. + +**Hung off the variable that already decides which link is primary.** `h4_dns_link: +enp2s0` governed the resolver list; it now governs the route metric too. The resolver +link and the preferred-route link must be the *same* link, and two independent knobs +would eventually disagree in a way nothing would notice. + +**It does not remove `.156` or down the link** — only which route is preferred. And +nothing is bound to `.156` any more in any case: the unconfigured dnsmasq holding it was +disabled in §2.14, found during this same investigation. + +**Applying is still manual**, as this playbook has always been: it writes the override +and stops. `netplan try` gives a 120-second auto-revert, and unlike the N150s it should +work here — that refusal was specific to a bridge carrying `parameters:`, and the H4 has +plain ethernets. Confirm it actually offers the prompt rather than assuming; the +`systemd-run` revert timer from `kvm-netplan-fix.yml` is the fallback if it refuses. + +An allowlist naming both addresses would still have been *correct*; this makes it +unnecessary. See §4.12 — same configuration, and this was its second distinct symptom. ### 2.10 `.claude/settings.json` is a MicroShift-era artifact `docs/REVIEW-2026-07-24.md:310` (H30) diff --git a/ansible/playbooks/h4-dns-resolvers.yml b/ansible/playbooks/h4-dns-resolvers.yml index c395323..bfa6e12 100644 --- a/ansible/playbooks/h4-dns-resolvers.yml +++ b/ansible/playbooks/h4-dns-resolvers.yml @@ -99,8 +99,36 @@ # its applied line. Right servers, wrong count. # # enp2s0 is the primary: it carries 192.168.1.160 and the kube-vip VIPs .200/.201. + # + # THIS VARIABLE NOW GOVERNS ROUTING AS WELL AS DNS, deliberately. The resolver link + # and the preferred-route link must be the same link; two independent knobs would + # eventually disagree, and the disagreement would be invisible. h4_dns_link: enp2s0 + # ---- route metric — BACKLOG §2.9, added 2026-09-06 ----------------------- + # Both interfaces are on the SAME /24 and both take DHCP, so both installed a + # default route at metric 100: + # + # default via 192.168.1.1 dev enp1s0 proto dhcp src 192.168.1.156 metric 100 + # default via 192.168.1.1 dev enp2s0 proto dhcp src 192.168.1.160 metric 100 + # + # Equal metric means the kernel picks one and nothing pins the choice. It had + # picked enp1s0, so the H4 was sourcing LAN traffic from **192.168.1.156** — an + # address referenced nowhere in this repo. Found in Vault audit logs (§2.6), where + # the H4's own requests arrived from an address no document knew about. + # + # WHY THIS MATTERS BEYOND TIDINESS. §2.9's firewall allowlist would have been built + # from the repo, which says the H4 is .160. It would have worked — until a reboot + # flipped the source address and locked out the machine that runs every playbook, + # including the one needed to undo the rule. A failure arriving hours or weeks + # after the change, looking unrelated to it. + # + # A higher metric loses. 200 on the secondary leaves enp2s0's 100 winning, and does + # NOT remove .156 or take the link down — it only decides which route is preferred. + # Nothing is bound to .156 any more in any case: the unconfigured dnsmasq that held + # it was disabled in §2.14, found during this same investigation. + h4_secondary_route_metric: 200 + tasks: - name: Confirm the expected links exist before touching anything ansible.builtin.command: "ip -o link show {{ item }}" @@ -265,6 +293,14 @@ dhcp4: true dhcp4-overrides: use-dns: false + {% if link != h4_dns_link %} + # Both links are on the same /24 and both take DHCP, so both + # installed a default route at metric 100 and the kernel picked + # whichever it liked — which made the H4's source address for LAN + # traffic non-deterministic. A higher metric loses. This does not + # remove the address or down the link. BACKLOG §2.9. + route-metric: {{ h4_secondary_route_metric }} + {% endif %} {% if link == h4_dns_link %} # Resolver list lives on this link only. Repeating it on the other # link doubles the entries in resolv.conf — resolved does not dedupe