72 lines
3.4 KiB
Markdown
72 lines
3.4 KiB
Markdown
---
|
|
status: resolved
|
|
opened: 2026-09-14
|
|
located-in: [mesh-controller (the derived filter)]
|
|
fixed-by: mesh-controller dda001d (the firewall opens the port the mesh itself runs on), 25e42b3; TestTheBrokersPortIsOpenedThoughNoModuleDeclaresIt
|
|
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
|
|
<mesh address> tcp dport 22 accept ssh
|
|
<mesh address> tcp dport 5000 accept the registry
|
|
<mesh address> 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. |
|