ADR 0186: a ban list never holds a neighbour, and the mesh's own bans are its own wherever they hang; design 31
This commit is contained in:
@@ -0,0 +1,81 @@
|
|||||||
|
---
|
||||||
|
topic: the mesh
|
||||||
|
status: accepted
|
||||||
|
date: 2026-10-02
|
||||||
|
deciders: jochen
|
||||||
|
reconstructed: false
|
||||||
|
extends: 02-DECISIONS/0179-the-intrusion-seat-serves-its-verbs-and-every-door-declares-its-jail.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 186. A ban list never holds a neighbour, and the mesh's own bans are its own wherever they hang
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
[ADR 0179](0179-the-intrusion-seat-serves-its-verbs-and-every-door-declares-its-jail.md) gave the
|
||||||
|
public proxy a jail. Within the hour the home server's ban list held `192.168.1.1` — the house's own
|
||||||
|
router. The router reflects local traffic, so every client in the building reaches that machine as
|
||||||
|
the gateway's address; one local request for a name the mesh does not serve, three times in a day,
|
||||||
|
and the whole house is refused by the machine it was asking. The jails inherited an `ignoreip` of
|
||||||
|
the loopback and the mesh's own range, which was right when the only jail read the ssh daemon and
|
||||||
|
the only clients were the mesh's; a jail on a public front door sees the neighbours too.
|
||||||
|
|
||||||
|
The same jail broke the other half of [ADR 0168](0168-a-converged-machine-is-filtered-by-the-mesh-alone.md).
|
||||||
|
The home server began reading *NOT the mesh alone: 1 rule set the mesh did not write refuses traffic
|
||||||
|
here*, and the rule set named was the mesh's own ban chain, written by the mesh's own intrusion
|
||||||
|
prevention minutes earlier. The host's reader of the legacy filter required every path into a chain
|
||||||
|
of refusals to come from a built-in chain whose policy accepts, before it would call that chain a
|
||||||
|
ban. On that machine the chain hangs off the container runtime's user chain as well as the input
|
||||||
|
chain, and the runtime had set the forward policy to DROP — so the mesh reported its own work as a
|
||||||
|
foreigner's, on the one machine where the group's exit condition was supposed to hold.
|
||||||
|
|
||||||
|
Both faults are one mistake in two places: a rule written about the public internet, applied to
|
||||||
|
everything that arrives.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**1. A ban list never holds a neighbour.** The jails the mesh composes never ban a source on a
|
||||||
|
private range — the mesh's own range, which was already named rather than written
|
||||||
|
([ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md)), and every address space
|
||||||
|
reserved for private use beside it, in both families. A machine behind a router that reflects local
|
||||||
|
traffic sees its whole building as one address; a ban there is a self-inflicted outage, and the
|
||||||
|
sources worth banning are not on those ranges in the first place.
|
||||||
|
|
||||||
|
**2. The mesh's own bans are its own wherever they hang.** A chain of refusals is a ban list when
|
||||||
|
every refusal names the sources it refuses and the chain accepts nothing — the rule the host already
|
||||||
|
applied to the packet filter's own tables, now applied to the legacy filter too, and nothing more.
|
||||||
|
The policy of the chains that jump into it says nothing about what it is: that policy is already
|
||||||
|
classified where it belongs, as the container runtime's, and requiring it here counted it twice.
|
||||||
|
|
||||||
|
**3. A chain that accepts anything is still not a ban.** That is what keeps a predecessor's
|
||||||
|
allow-these-and-drop-the-rest chain classified as something an operator must look at, which is the
|
||||||
|
distinction [ADR 0168](0168-a-converged-machine-is-filtered-by-the-mesh-alone.md) exists to draw.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- The composed jails gain the private ranges in their never-ban list. An address already banned
|
||||||
|
stays banned until it is released; the house's router was released by hand the moment it was found.
|
||||||
|
- The home server reads *the mesh alone* again, which is group 7's exit condition and was false for
|
||||||
|
about an hour.
|
||||||
|
- A machine whose apply fails for an unrelated reason does not revisit its found firewall's record
|
||||||
|
at all — the step runs only after a clean apply ([ADR 0168](0168-a-converged-machine-is-filtered-by-the-mesh-alone.md)).
|
||||||
|
The home server's record therefore still reads *retired by the mesh* although the front end is
|
||||||
|
uninstalled, and will correct itself once that machine's own stuck module is fixed. It is a stale
|
||||||
|
record, not a wrong machine.
|
||||||
|
- The record number the front end's removal was given moved under it: another session took 0175
|
||||||
|
while that record was in review, and it is now
|
||||||
|
[ADR 0180](0180-the-found-front-end-is-uninstalled-once-a-machine-is-converged.md). The citations
|
||||||
|
the host and the control plane print were pointing at an unrelated record and are corrected here.
|
||||||
|
|
||||||
|
## How this is checked
|
||||||
|
|
||||||
|
| Rule | Checked by |
|
||||||
|
|---|---|
|
||||||
|
| A private source is never banned | the module's jail configuration, read back by `fail2ban.fail2ban_settings` on a machine |
|
||||||
|
| The mesh's own ban chain reads as a ban behind a dropping forward policy | a host test over the home server's own captured rule set |
|
||||||
|
| A chain that accepts anything is not a ban | a host test |
|
||||||
|
| Live | the home server reads *the mesh alone*; no ban held on either machine is a private address |
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [ADR 0179](0179-the-intrusion-seat-serves-its-verbs-and-every-door-declares-its-jail.md), [ADR 0168](0168-a-converged-machine-is-filtered-by-the-mesh-alone.md), [ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md), [ADR 0180](0180-the-found-front-end-is-uninstalled-once-a-machine-is-converged.md)
|
||||||
|
- [Design 08 — Connectivity](../03-DESIGN/01-to-be/08-connectivity.md), [Design 31 — A module declares its fail2ban jail](../03-DESIGN/01-to-be/31-a-module-declares-its-fail2ban-jail.md)
|
||||||
@@ -186,6 +186,7 @@ python3 00-META/checks/index.py fail if stale
|
|||||||
- **0180** — [The found front end is uninstalled once a machine is converged](0180-the-found-front-end-is-uninstalled-once-a-machine-is-converged.md)
|
- **0180** — [The found front end is uninstalled once a machine is converged](0180-the-found-front-end-is-uninstalled-once-a-machine-is-converged.md)
|
||||||
- **0184** — [A service the mesh asked to run is still running a moment later](0184-a-service-the-mesh-asked-to-run-is-still-running-a-moment-later.md)
|
- **0184** — [A service the mesh asked to run is still running a moment later](0184-a-service-the-mesh-asked-to-run-is-still-running-a-moment-later.md)
|
||||||
- **0185** — [A control plane behind its seat's row serves what it can](0185-a-control-plane-behind-its-seats-row-serves-what-it-can.md)
|
- **0185** — [A control plane behind its seat's row serves what it can](0185-a-control-plane-behind-its-seats-row-serves-what-it-can.md)
|
||||||
|
- **0186** — [A ban list never holds a neighbour, and the mesh's own bans are its own wherever they hang](0186-a-ban-list-never-holds-a-neighbour.md)
|
||||||
|
|
||||||
### Its tiers, from the bottom up
|
### Its tiers, from the bottom up
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ updated: 2026-10-02
|
|||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md
|
- 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md
|
||||||
- 02-DECISIONS/0179-the-intrusion-seat-serves-its-verbs-and-every-door-declares-its-jail.md
|
- 02-DECISIONS/0179-the-intrusion-seat-serves-its-verbs-and-every-door-declares-its-jail.md
|
||||||
|
- 02-DECISIONS/0186-a-ban-list-never-holds-a-neighbour.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 31 — A module declares its fail2ban jail, and the mesh composes them per node
|
# 31 — A module declares its fail2ban jail, and the mesh composes them per node
|
||||||
@@ -92,3 +93,13 @@ the daemon's, and both are read through the console.
|
|||||||
|
|
||||||
*How it is checked:* ADR 0179's table.
|
*How it is checked:* ADR 0179's table.
|
||||||
|
|
||||||
|
## What the first jails taught, 2026-10-02
|
||||||
|
|
||||||
|
[ADR 0186](../../02-DECISIONS/0186-a-ban-list-never-holds-a-neighbour.md). Within an hour of the
|
||||||
|
first public jail the home server had banned the house's own router: the router reflects local
|
||||||
|
traffic, so every client in the building arrives as the gateway's address. The never-ban list now
|
||||||
|
holds every private range as well as the mesh's own. And the mesh read its own ban chain as a
|
||||||
|
foreign rule set on that machine, because the chain hangs off the container runtime's user chain and
|
||||||
|
that machine's forward policy is the runtime's DROP — the reader now calls a chain of source-named
|
||||||
|
refusals a ban wherever it hangs, as it already did for the packet filter's own tables.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user