Merge pull request 'Issue 047 — the firewall does not cover published container ports' (#41) from issue/the-firewall-does-not-filter-published-ports into main
This commit was merged in pull request #41.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
---
|
||||
status: open
|
||||
opened: 2026-09-14
|
||||
located-in: []
|
||||
fixed-by:
|
||||
amended-design:
|
||||
---
|
||||
|
||||
# 047 — The firewall does not cover published container ports
|
||||
|
||||
## Symptom
|
||||
|
||||
A machine running the mesh's firewall drops what it should drop and leaves every container port
|
||||
published on all addresses open to anything that can reach the machine.
|
||||
|
||||
Rehearsed in the lab on three machines, one probing another, with a single change between the two
|
||||
measurements:
|
||||
|
||||
| | ordinary port on the host | container port published on `0.0.0.0` |
|
||||
|---|---|---|
|
||||
| before the mesh's rules | reachable | reachable |
|
||||
| after the mesh's rules | **refused** | **still reachable** |
|
||||
|
||||
Same prober, same ports, same second. The first column is the firewall working. The second is the
|
||||
question.
|
||||
|
||||
## Why this is the way it is
|
||||
|
||||
It is deliberate, and the reasoning is written where the rules are generated: the mesh's table hooks
|
||||
`input` and `output` and **no forward chain**, because what a machine forwards is the container
|
||||
runtime's business — the runtime writes rules for the bridges it creates, and a second table
|
||||
dropping in `forward` would drop container traffic the runtime explicitly allowed, stopping every
|
||||
container on the node including the control plane.
|
||||
|
||||
That reasoning is sound. The conclusion drawn from it is not the only one available, because traffic
|
||||
to a published port does not traverse `input` at all: it is redirected and then forwarded. So a
|
||||
default-drop `input` chain says nothing whatsoever about it, and the firewall is silent on the ports
|
||||
most likely to matter.
|
||||
|
||||
## Why it matters, measured rather than argued
|
||||
|
||||
The anchor this mesh will be installed onto publishes **38 distinct container ports on all
|
||||
addresses** — a document store, three cache sentinels, mail transfer and retrieval, the forge's own
|
||||
SSH, and thirty more.
|
||||
|
||||
Today those are filtered, by the system being replaced, which does it with rules that hook exactly
|
||||
the path this one leaves alone. So the cutover from the old firewall to this one **opens all
|
||||
thirty-eight at once, silently, on a machine with a public address**, while every check reports a
|
||||
firewall that is up and dropping by default.
|
||||
|
||||
That is the worst shape a security fault can take: the mechanism is present, it reports success, and
|
||||
the thing it is believed to be doing is not the thing it does.
|
||||
|
||||
## How it would be checked
|
||||
|
||||
Two probes from another machine, against a port on the host and a published container port, before
|
||||
and after the rules are loaded. Both must change together. The lab can do this — it is how the table
|
||||
above was produced — and nothing does it today.
|
||||
|
||||
## Open questions
|
||||
|
||||
- Can the mesh filter forwarded traffic without owning the whole `forward` chain? A table that
|
||||
accepts by default and drops only what the mesh knows about would not stop the runtime's traffic,
|
||||
and hooking `forward` at all is the thing the current comment rejects.
|
||||
- Should a module's declared reach apply to its published ports? A module that says it listens to
|
||||
the mesh only, and is then reachable from everywhere because its port is published, is not doing
|
||||
what its manifest says.
|
||||
- Is the answer instead that the mesh should not publish on all addresses? A port bound to the
|
||||
private network needs no filtering, and most of these want exactly that.
|
||||
Reference in New Issue
Block a user