diff --git a/04-ISSUES/003-firewall-scope-is-read-by-no-code/00-report.md b/04-ISSUES/003-firewall-scope-is-read-by-no-code/00-report.md index f2b5d5c..9115d64 100644 --- a/04-ISSUES/003-firewall-scope-is-read-by-no-code/00-report.md +++ b/04-ISSUES/003-firewall-scope-is-read-by-no-code/00-report.md @@ -1,9 +1,9 @@ --- -status: open +status: fixed opened: 2026-08-22 -located-in: [] -fixed-by: -amended-design: +located-in: [mesh-control/internal/catalogue, mesh-catalog/modules/firewall] +fixed-by: the manifest refuses unknown keys, `from` is the field that scopes a port and it is rendered to nftables, and the firewall module applies it +amended-design: 0050-a-machine-firewall-is-the-sum-of-what-it-listens-on.md --- # 003 — A firewall rule's `scope:` is read by no code @@ -31,10 +31,27 @@ any check. - Five manifests carry the key. Zero code paths consume it. - Recorded as an observation on 2026-08-22. -## Open questions +## Resolution -- Should the manifest reject unknown keys outright? That is the general fix; this is one - instance of it. -- Were the five declarations intended to restrict something that is currently open? Each needs - checking against what the node actually exposes — the declaration cannot be trusted either - way. +Both open questions are answered, and the chain from a declared scope to a packet actually dropped +is closed — recorded as [ADR 0050](../../02-DECISIONS/0050-a-machine-firewall-is-the-sum-of-what-it-listens-on.md). + +- **Unknown keys are refused, not accepted.** `ParseManifest` decodes with + `DisallowUnknownFields`, so a `scope:` key the firewall type does not have is now rejected at the + manifest — the general fix, of which this was one instance. A key that reads as a restriction can + no longer be one nothing enforces. +- **The field that scopes a port is `from`, and it is read.** A `listens` entry names its source — + `mesh`, `anywhere` or `machine` — and the control plane renders the union of every module's + `listens` into a node's whole nftables rule set (`AsNftables`), default-drop with an accept scoped + to exactly the source each port named. The five `scope:` declarations were the wrong spelling of + that intent; `from` is the right one, and it is enforced. +- **A module applies it.** The rendered rule set is written to the node (the `filtering` resource), + and the `firewall` module (mesh-catalog) loads it — the last link, without which the rules were + computed and never dropped a packet. + +## Original open questions + +- Should the manifest reject unknown keys outright? — **Yes; it does now** (`DisallowUnknownFields`). +- Were the five declarations intended to restrict something that is currently open? — They meant to + scope a port and used a key nothing read; expressed through `from`, that intent is now enforced. + Any manifest still carrying `scope:` is refused at parse, so it is found rather than believed.