Converge openings through the firewall an adopted node was found with, and retire it only when the node converges (hq ADR 0100)
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package apply
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/novox/mesh-host/internal/declaration"
|
||||
"github.com/novox/mesh-host/internal/firewall"
|
||||
"github.com/novox/mesh-host/internal/store"
|
||||
)
|
||||
|
||||
// foundFirewall settles, before anything else in an apply, which firewall this node has — and on
|
||||
// an adopted node that the mesh had converged, puts it back in force first (novox/hq ADR 0100).
|
||||
//
|
||||
// Only an adopted node asks. It is detected on every apply rather than remembered, so a firewall
|
||||
// switched on after adoption is spoken to from the next reconcile; what is remembered is what was
|
||||
// found first, and whether the mesh retired it. An unsupported firewall refuses the whole
|
||||
// declaration: the mesh could neither open what it needs through it nor say what it would close.
|
||||
func foundFirewall(ctx context.Context, d *declaration.Declaration, known *store.State, run Runner,
|
||||
log func(string)) (firewall.Kind, error) {
|
||||
if d.Adoption == nil {
|
||||
return "", nil
|
||||
}
|
||||
rec := known.Firewall
|
||||
if rec != nil && rec.DisabledByMesh && rec.Kind == string(firewall.UFW) {
|
||||
// Returned to adopted: the found firewall is enabled again before the openings are
|
||||
// converged through it, and the derived filter is gone with this declaration.
|
||||
if err := firewall.Enable(ctx, run); err != nil {
|
||||
return "", err
|
||||
}
|
||||
rec.DisabledByMesh = false
|
||||
log(" enabled ufw again: this node is adopted, and the firewall found on it is in force")
|
||||
}
|
||||
kind, name, err := firewall.Detect(ctx, run)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if kind == firewall.Unsupported {
|
||||
return "", fmt.Errorf(
|
||||
"this machine is filtered by %s, and no host speaks that firewall yet. An adopted node "+
|
||||
"keeps the firewall it was found with, so the mesh could neither open what it needs "+
|
||||
"through it nor say what it would close; this declaration is refused whole", name)
|
||||
}
|
||||
if rec == nil {
|
||||
rec = &store.FoundFirewall{Kind: string(kind), WasActive: kind == firewall.UFW,
|
||||
FoundAt: time.Now().UTC()}
|
||||
} else {
|
||||
rec.Kind = string(kind)
|
||||
rec.WasActive = rec.WasActive || kind == firewall.UFW
|
||||
}
|
||||
known.Firewall = rec
|
||||
return kind, nil
|
||||
}
|
||||
|
||||
// retireFirewall disables the found firewall once a converged declaration has applied cleanly,
|
||||
// which is when the mesh's derived filter has taken its place. Disabled, never flushed: its
|
||||
// configuration stays on disk for a return to adopted, and the container runtime's rules are not
|
||||
// its to take.
|
||||
func retireFirewall(ctx context.Context, d *declaration.Declaration, known *store.State, run Runner,
|
||||
log func(string)) error {
|
||||
rec := known.Firewall
|
||||
if d.Adoption != nil || rec == nil || rec.Kind != string(firewall.UFW) || !rec.WasActive ||
|
||||
rec.DisabledByMesh {
|
||||
return nil
|
||||
}
|
||||
if err := firewall.Disable(ctx, run); err != nil {
|
||||
return err
|
||||
}
|
||||
rec.DisabledByMesh = true
|
||||
log(" disabled ufw: this node is converged and filtered by the mesh; ufw's configuration is left on disk")
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyOpening makes one opening true through the firewall found here.
|
||||
func applyOpening(ctx context.Context, o *declaration.Opening, run Runner, kind firewall.Kind) (Outcome, error) {
|
||||
out := begin(o)
|
||||
switch kind {
|
||||
case firewall.None:
|
||||
out.Action = "unchanged"
|
||||
out.Detail = "no firewall found; nothing filters this port"
|
||||
return out, nil
|
||||
case firewall.UFW:
|
||||
action, err := firewall.Converge(ctx, run, o)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.Action = action
|
||||
out.Detail = "through ufw, marked " + firewall.Mark(o)
|
||||
return out, nil
|
||||
}
|
||||
return out, fmt.Errorf("no firewall is known for this node, so %s cannot be opened", o.Target())
|
||||
}
|
||||
|
||||
// removeOpening deletes the rules the mesh marked for an opening no longer declared, and nothing
|
||||
// the machine had before.
|
||||
func removeOpening(ctx context.Context, a store.Applied, run Runner, rec *store.FoundFirewall) (string, string, error) {
|
||||
if rec == nil || rec.Kind != string(firewall.UFW) {
|
||||
return "forgotten", "no firewall held a rule for it", nil
|
||||
}
|
||||
n, err := firewall.Remove(ctx, run, a.ID)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if n == 0 {
|
||||
return "forgotten", "ufw held no rule marked for it", nil
|
||||
}
|
||||
return "removed", fmt.Sprintf("%d ufw rule(s) marked as the mesh's deleted", n), nil
|
||||
}
|
||||
Reference in New Issue
Block a user