diff --git a/04-ISSUES/052-the-firewall-does-not-know-the-brokers-port/00-report.md b/04-ISSUES/052-the-firewall-does-not-know-the-brokers-port/00-report.md new file mode 100644 index 0000000..8ad3468 --- /dev/null +++ b/04-ISSUES/052-the-firewall-does-not-know-the-brokers-port/00-report.md @@ -0,0 +1,71 @@ +--- +status: open +opened: 2026-09-14 +located-in: [] +fixed-by: +amended-design: +--- + +# 052 — The firewall closes the port the mesh runs on + +## Symptom + +A one-node mesh with the packet filter assigned generates this input chain: + +``` +policy drop +ct state established,related accept +iif "lo" accept +icmp / icmpv6 accept + tcp dport 22 accept ssh + tcp dport 5000 accept the registry + tcp dport 20000 accept +udp dport 51820 accept the overlay +``` + +**The substrate broker's port is not there.** Every machine dials it to enrol and to receive every +declaration it is ever sent. Nothing opens it. + +The rules are generated from what modules declare they listen on. The broker is not a module — it +is raised by the installer from the bundle — so it declares nothing, and the generator has nothing +to generate from. + +## Why this matters + +**It is invisible on the mesh where it is first assembled and fatal on the next one.** A mesh of one +never dials its own broker across the network, so the missing rule changes nothing and the firewall +looks correct. The first machine that tries to join is refused at the packet filter, during +enrolment, before the mesh can report anything about it — and the cause is several steps from the +symptom. + +**It would have been met during the migration, not before it.** The intended order is to raise the +anchor, assign its modules, then join the other machines. Assigning the firewall before the second +machine enrols is both the natural order and the one that breaks. + +**It is [051](../051-the-mesh-cannot-update-what-it-depends-on/00-report.md) in a second place.** That +issue records that the substrate cannot be updated because the mesh holds no record of it. The same +absence means the firewall cannot know the substrate exists. Anything else generated from what +modules declare has the same hole: the store, the broker, and their ports are outside every such +computation. + +**The store is the same shape and has not been checked.** It binds on the machine and is reached by +the control plane; whether that survives a default-drop policy has not been established here. + +## Open questions + +- **Should the substrate declare its listens** — which means the substrate being something the mesh + holds a record of, i.e. 051's adoption? +- **Or should the firewall have a floor** that is not derived from modules at all, the way ssh + already is? Ssh is in the rules for exactly this reason: a machine nobody can reach is a machine + nobody can repair, and that is not a thing any module declares. The broker is arguably the same + class of fact — the mesh cannot manage a machine it cannot talk to. +- **What else is in that class?** Whatever the answer, the question "which ports must be open for + the mesh itself to work" should be answerable from one place rather than assembled from modules + that happen to exist. + +## How this would be checked + +| Rule | Checked by | +|---|---| +| A machine with the firewall assigned can still be joined | The packet filter is assigned to an anchor, and a second machine enrols afterwards. | +| The mesh's own ports are open | The generated ruleset is compared against the substrate's own bindings, not only against module declarations. |