Files
hq/02-DECISIONS/0140-the-filter-constrains-what-arrives-from-outside.md
T
jschoubben 1aeb4fe8d8 ADR 0140: the filter constrains what arrives from outside, and says nothing about a machine's own guests
Reading a converged machine's rendered rules showed the cause: the chain blocks
everything passing through and then allows the machine's own containers back by
listing their address ranges. 0137 made that list typeable and 0139 tried to
generate it; both refined a list that should not exist, because the mesh has no
position on a container reaching outward. Constrain what arrives from outside,
allow what did not, and let the machine report which links face outside — one
fact instead of a list. Ports keep following the modules unchanged.

The records check now allows one record to supersede several, and stops
requiring a withdrawn record's own citations to be live.
2026-09-28 23:31:50 +02:00

9.0 KiB

topic, status, date, deciders, reconstructed, extends, supersedes
topic status date deciders reconstructed extends supersedes
what runs on it accepted 2026-09-28 jochen false 02-DECISIONS/0045-a-machine-firewall-is-the-sum-of-what-it-listens-on.md
02-DECISIONS/0137-a-machine-says-which-networks-it-routes.md
02-DECISIONS/0139-a-network-is-forwarded-because-a-module-declared-it.md

140. The filter constrains what arrives from outside, and says nothing about a machine's own guests

Context

The filter the mesh derives blocks traffic passing through a machine unless something allows it, because a container's published port is traffic passing through rather than traffic arriving at the machine itself (ADR 0100, issue 047). Having blocked all of it, the filter then had to let the machine's own containers reach outward again. It does that by listing the address ranges those containers sit on.

As rendered on a converged workstation today:

policy drop
ct state established,related accept
ip saddr 172.16.0.0/12 accept
ip saddr 192.168.128.0/17 accept
ip saddr 10.0.0.0/8 accept
ip saddr 192.168.16.0/20 accept
... four more

Two of those ranges were constants in the control plane's source. The rest were typed by the operator after ADR 0137, which existed to make the typing possible, because converging that workstation had cut every one of its containers off from the internet and nothing reported a fault (issue 137).

The list is the mistake, not its contents. Every attempt to make it correct fails the same way. A constant describes one machine. A typed range goes stale, and cannot tell a network the mesh made from one a predecessor left behind — measured on the control-node, where six such ranges fall outside the constants and two of the six belong to services the mesh does not run (issue 141). ADR 0139 tried to generate the same list from the modules and put half the rule set on the machine to do it. Three records, one list, and the list should not exist.

Because the mesh has no policy about a container reaching outward. What the filter is for is stated in ADR 0045: which port is open, and to whom. That is about what arrives. A container of this machine's own opening a connection to something else is not a port being opened to anybody, and enumerating the addresses it might do so from is bookkeeping about the machine's internal plumbing, which the mesh neither owns nor can know.

The system being replaced never had this fault, and its rule says why. The chain still protecting the control-node applies only to traffic arriving on that machine's outward link, and leaves everything else alone. The mesh's filter dropped that distinction and replaced it with a list of addresses.

Considered Options

  1. Keep the list and generate it better — from the modules' declared networks, or from what the machine reports. Rejected: ADR 0139 is that, and it puts part of the rule set on the machine, which makes the rule set partly the machine's and the derivation advisory.
  2. Name the guest links instead of their addresses, and allow only those. Rejected as more than is needed: it fails in the safe direction, but it is still a list that has to keep up with the machine, and the thing it protects against — a container reaching outward — is not a thing the mesh has a position on.
  3. Do not block traffic passing through at all. Rejected: that is issue 047, where a published port was reachable from anywhere because no rule mentioned it.
  4. Constrain what arrives from outside, and nothing else. Adopted.

Decision

The filter constrains traffic arriving from outside the machine, and says nothing about traffic that did not. Traffic passing through the machine is allowed unless it arrived on one of the machine's outward links, in which case it is allowed only where a declared endpoint's reach admits it (ADR 0138). A container of this machine's own reaching anywhere is not filtered, because the mesh has no position on it.

A machine says which of its links face outside. One node-level fact, reported by the machine the way it already reports the kind of firewall it found and the tunnel it carries — not a setting, not a list of addresses, and not something anybody types. It does not change when a module is added or removed, which is what separates it from the list it replaces.

A machine that has reported no outward link is sent no filter. Rendering a rule around a link whose name is not known produces a rule set that does not load, which is a machine filtering nothing while its unit reports success. The refusal happens in the control plane, where a person reads it, and the machine keeps the filter it already has.

No addresses of the machine's own networks appear in the filter. The two constants are removed and node networks is removed with them, along with everything any machine was told to say through it. Ports continue to follow the modules exactly as before: a module assigned to a machine opens the port its assignment says it reaches on, and nothing about a network is said anywhere.

Consequences

  • Three records collapse into one rule. 0137 and 0139 are superseded. What 0137 was right about — that converging a machine had silently cut off its own containers, and that nothing previewed it — is answered by removing the cause rather than by giving the operator a way to compensate for it.
  • Every machine already converged loses its declared ranges and keeps working, because the traffic those ranges allowed is now allowed by not having arrived from outside. The workstation's five ranges and the laptop's one are deleted rather than migrated.
  • A machine's test beds stop being a special case. A bed's network is created while the machine runs and was the case no list could cover; it is now covered by not being mentioned.
  • A new fact travels in the report, and the control plane refuses to compose a filter without it, so the order of the roll-out matters: the machines report before the control plane depends on it.
  • A machine with more than one outward link says so, and a machine that acquires one while the mesh is not looking is treated as internal until its next report. That window is the cost of this shape; it is bounded by the report interval, and it exists on machines whose outward link changes, which are the machines with nothing published to the outside.
  • What got harder: nothing in the declaration, and one more thing a machine must be able to work out about itself. A machine that cannot say which link faces outside cannot be given a filter.

How it is checked

  • A machine's own container reaches outward with no network named anywhere. A bed converges a machine carrying containers on several networks, none of them mentioned in any setting, and each reaches out afterwards. This fails against the previous behaviour, where the same flip cut them off, and that is how it is written.
  • A port declared reachable from outside is reachable; one that is not, is not. Probed from off the machine's private network, for a published port and for an undeclared one, before and after the flip.
  • A network created after the filter was composed needs no new filter. A network is made on the machine after its last declaration and a container on it reaches out, with nothing re-sent.
  • No address of a machine's own networks appears in a rendered filter, asserted on the text so a range cannot creep back in.
  • A machine that reports no outward link is sent no filter, and the refusal names it — asserted in the control plane, and that the machine's existing filter is left alone.
  • A machine reporting two outward links has both constrained, asserted per chain body so a rule covering one and not the other cannot pass.

References