The host says what filters the machine, with owners, and keeps the found firewall retired on every converged apply (hq ADR 0168)

Every table and chain that refuses traffic is reported with whose it is:
the mesh's, the found firewall's, the container runtime's own, a ban, or
other — the runtime's user chain is other, which is where both predecessors
kept their rules, in the legacy filter on one machine and invisible to the
mesh. Adoption's threshold does not move; a converged machine's report
grows by its filters and its found firewall's state.

Convergence is a state the host keeps: a found firewall enabled again is
retired again and said; a reconcile that finds it inactive records that it
was found so, never that the mesh did it; a step skipped after a failed
apply is said. A retirement the mesh began and did not finish is finished.

Fixtures are rulesets captured from three machines of the first mesh.
This commit is contained in:
2026-10-02 11:58:16 +02:00
parent ee2359f29f
commit 627ac97d4f
13 changed files with 1776 additions and 100 deletions
+15 -2
View File
@@ -27,6 +27,7 @@ import (
"time"
"github.com/novox/mesh-host/internal/declaration"
"github.com/novox/mesh-host/internal/firewall"
"github.com/novox/mesh-host/internal/store"
"github.com/novox/mesh-host/internal/system"
)
@@ -67,6 +68,10 @@ type Outcome struct {
// Report is what an apply did, in the order it did it.
type Report struct {
Outcomes []Outcome `json:"outcomes"`
// Firewall is what this apply did about the firewall a converged machine was found with, when
// it did or declined anything: retired, retired again, or left in force and why (novox/hq ADR
// 0168). Said rather than an outcome: the plan says the same step the same way.
Firewall string `json:"firewall,omitempty"`
// Tunnel is what this apply says about the tunnel the private network took over, when the
// declaration names one (novox/hq ADR 0105).
Tunnel *TakenTunnel `json:"tunnel,omitempty"`
@@ -611,16 +616,24 @@ func ApplyKeeping(
}
// A converged node whose found firewall was in force retires it only now, once everything —
// the mesh's derived filter among it — applied cleanly (novox/hq ADR 0100).
// the mesh's derived filter among it — applied cleanly (novox/hq ADR 0100), and on every
// converged apply, not once (ADR 0168). Skipped, it is said: a step that does nothing is never
// silent (issue 143).
if len(failures) == 0 {
if err := retireFirewall(ctx, d, origin, &known, run, log); err != nil {
did, err := retireFirewall(ctx, d, origin, &known, run, log)
if err != nil {
return report, known, &Error{Resource: "the firewall found on this machine", Err: err, Done: report}
}
report.Firewall = did
for _, orphan := range protecting {
if err := removeOrphan(orphan); err != nil {
return report, known, err
}
}
} else if rec := known.Firewall; origin == store.OriginDeclared && d.Adoption == nil && rec != nil &&
rec.Kind == string(firewall.UFW) && rec.WasActive && firewall.Active(ctx, run) {
report.Firewall = fmt.Sprintf("left in force: %d resource(s) failed, and the found firewall is retired only after a clean apply", len(failures))
log(" kept ufw in force: " + report.Firewall)
}
if len(failures) > 0 {