Preview ssh as the derived filter admits it, and say a narrowing to the private network closes (hq ADR 0100)

This commit is contained in:
2026-09-22 18:00:53 +02:00
parent ade6b2bfb6
commit 3dc7d0e386
2 changed files with 106 additions and 23 deletions
+62 -11
View File
@@ -60,6 +60,21 @@ func anAdoptedAnchor(t *testing.T) (*stores, *[]string) {
// reportsHolding has the anchor report, on what it was last sent, holding what is given.
func reportsHolding(t *testing.T, open *stores, held ...link.Held) {
t.Helper()
reportsReaching(t, open, []link.Reach{
{Protocol: "tcp", Address: "0.0.0.0", Port: 22, By: "sshd"},
{Protocol: "tcp", Address: "0.0.0.0", Port: 8080, By: "hello-web", Published: true,
ContainerPort: 80},
{Protocol: "tcp", Address: "0.0.0.0", Port: 5000, By: "predecessor-registry",
Published: true, ContainerPort: 5000},
{Protocol: "tcp", Address: "127.0.0.1", Port: 15672, By: "mesh-broker",
Published: true, ContainerPort: 15672},
}, held...)
}
// reportsReaching has the anchor report, on what it was last sent, what is reachable on it and
// holding what is given.
func reportsReaching(t *testing.T, open *stores, reachable []link.Reach, held ...link.Held) {
t.Helper()
ctx := t.Context()
body, err := composed(t, open, "anchor").Body()
@@ -75,16 +90,7 @@ func reportsHolding(t *testing.T, open *stores, held ...link.Held) {
}
if err := (link.Enrolment{Inventory: open.inventory}).Heard(ctx, link.Report{
Node: "anchor", Applied: []string{"hello-web.x"}, Declared: digestOf(body),
Firewall: "ufw", Held: held,
Reachable: []link.Reach{
{Protocol: "tcp", Address: "0.0.0.0", Port: 22, By: "sshd"},
{Protocol: "tcp", Address: "0.0.0.0", Port: 8080, By: "hello-web", Published: true,
ContainerPort: 80},
{Protocol: "tcp", Address: "0.0.0.0", Port: 5000, By: "predecessor-registry",
Published: true, ContainerPort: 5000},
{Protocol: "tcp", Address: "127.0.0.1", Port: 15672, By: "mesh-broker",
Published: true, ContainerPort: 15672},
},
Firewall: "ufw", Held: held, Reachable: reachable,
}); err != nil {
t.Fatal(err)
}
@@ -159,7 +165,9 @@ func TestConvergingPreviewsThenChangesAndAdoptingKeepsWhatWasTaken(t *testing.T)
"tcp/8080 hello-web (published, container port 80)",
"declared by hello-web (from anywhere)",
"WILL CLOSE — no module assigned here declares it",
"ssh is never closed",
// The anchor faces inward and is on the private network: the derived filter admits ssh
// from the mesh only.
"WILL CLOSE to everything outside the private network — ssh stays open from the mesh",
"notes\n replacing the found file /etc/notes.conf (original kept at",
"assigns nftables",
"the found firewall (ufw) is disabled, never flushed",
@@ -253,3 +261,46 @@ func TestTheApiRefusesTheFlipInTheCommandLinesWords(t *testing.T) {
t.Fatalf("a take naming no module got %d", got.Code)
}
}
// novox/hq ADR 0100: the preview says what the derived filter does, rendered as it is rendered. On
// a machine that faces inward, ssh is admitted from the private network only, and a port a module
// admits from the mesh only closes to everything outside it: both are said to close.
func TestThePreviewSaysWhatNarrowsToTheMeshCloses(t *testing.T) {
open, _ := anAdoptedAnchor(t)
ctx := t.Context()
register(t, open, catalogue.Manifest{Module: "store", Version: "1",
Listens: []catalogue.Listening{{Port: 5432, From: catalogue.FromMesh}}})
if _, err := assign(ctx, open, "anchor", "store"); err != nil {
t.Fatal(err)
}
reportsReaching(t, open, []link.Reach{
{Protocol: "tcp", Address: "0.0.0.0", Port: 22, By: "sshd"},
{Protocol: "tcp", Address: "0.0.0.0", Port: 5432, By: "postgres"},
{Protocol: "tcp", Address: "10.77.0.1", Port: 5432, By: "postgres"},
{Protocol: "tcp", Address: "0.0.0.0", Port: 8080, By: "hello-web", Published: true,
ContainerPort: 80},
})
preview, err := converge(ctx, open, "anchor", false, "")
if err != nil {
t.Fatal(err)
}
lines := map[string]string{}
for _, line := range strings.Split(preview, "\n") {
fields := strings.Fields(line)
if len(fields) > 1 && strings.HasPrefix(fields[0], "tcp/") {
lines[fields[0]+" "+fields[1]] += line + "\n"
}
}
if got := lines["tcp/22 sshd"]; !strings.Contains(got, "WILL CLOSE to everything outside "+
"the private network") {
t.Errorf("ssh on an inward machine is not said to close outside the mesh:\n%s", preview)
}
store := lines["tcp/5432 postgres"]
if strings.Count(store, "WILL CLOSE to everything outside the private network") != 1 ||
!strings.Contains(store, "declared by store (from mesh)") {
t.Errorf("the store's narrowing is not said to close, or its mesh address is:\n%s", preview)
}
if got := lines["tcp/8080 hello-web"]; !strings.Contains(got, "declared by hello-web (from anywhere)") {
t.Errorf("a port open to everywhere is not said to stay:\n%s", preview)
}
}
+44 -12
View File
@@ -217,7 +217,9 @@ func converge(ctx context.Context, open *stores, node string, yes bool, filter s
if err != nil {
return "", err
}
preview := previewOf(node, reported, rules, with.Foundation, plan, taken, filter, runs[filter])
derived := derivedFilter{rules: rules, foundation: with.Foundation, mesh: with.Mesh,
outward: plan.PublicDomain != ""}
preview := previewOf(node, reported, derived, plan, taken, filter, runs[filter])
if !yes {
return preview + fmt.Sprintf("\n\nNothing has changed. Run `converge %s --yes` to do it.", node), nil
}
@@ -252,7 +254,7 @@ func converge(ctx context.Context, open *stores, node string, yes bool, filter s
}
// previewOf is what converging a node will change, before it changes it.
func previewOf(node string, reported inventory.Adoption, rules []catalogue.Rule, foundation []int,
func previewOf(node string, reported inventory.Adoption, derived derivedFilter,
plan catalogue.Resolution, taken []string, filter string, filterAssigned bool) string {
var b strings.Builder
fmt.Fprintf(&b, "converging %s\n", node)
@@ -269,7 +271,7 @@ func previewOf(node string, reported inventory.Adoption, rules []catalogue.Rule,
if r.Published {
what += fmt.Sprintf(" (published, container port %d)", r.ContainerPort)
}
fmt.Fprintf(&b, " %-44s %s\n", what, fate(r, rules, foundation))
fmt.Fprintf(&b, " %-44s %s\n", what, derived.fate(r))
}
if len(reported.Reachable) == 0 {
b.WriteString(" nothing reported\n")
@@ -317,26 +319,56 @@ func previewOf(node string, reported inventory.Adoption, rules []catalogue.Rule,
return strings.TrimRight(b.String(), "\n")
}
// derivedFilter is what the filter the flip loads is rendered from, as AsNftables renders it.
type derivedFilter struct {
rules []catalogue.Rule
foundation []int
// mesh is every address on the private network; outward says the machine faces outside.
mesh []string
outward bool
}
// closesOutside is what a narrowing from everywhere to the private network is called: it closes.
const closesOutside = "WILL CLOSE to everything outside the private network"
// fate is what the derived filter does to one reachable thing: which module declares it and from
// where, or that it will close.
func fate(r inventory.Reach, rules []catalogue.Rule, foundation []int) string {
// where, or that it will close — wholly, or to everything outside the private network. Rendered
// exactly as AsNftables admits it, ssh included.
func (d derivedFilter) fate(r inventory.Reach) string {
// Bound to an address on the private network, it was never reachable from outside it, so
// admitting it from the mesh narrows nothing.
onMesh := slices.Contains(d.mesh, strings.Trim(r.Address, "[]"))
if r.Protocol == "tcp" && r.Port == catalogue.SSHPort {
return "stays open — ssh is never closed"
// From everywhere only when the machine faces outward or the mesh has no addresses to
// narrow it to; otherwise from the private network only.
if d.outward || len(d.mesh) == 0 || onMesh {
return "stays open — ssh is never closed"
}
return closesOutside + " — ssh stays open from the mesh, never closed there"
}
for _, port := range foundation {
for _, port := range d.foundation {
if r.Protocol == "tcp" && r.Port == port {
return "stays open — the mesh's own, from anywhere"
}
}
for _, rule := range rules {
for _, rule := range d.rules {
if rule.Port != r.Port || rule.Protocol != r.Protocol {
continue
}
if rule.From == catalogue.FromMachine {
return fmt.Sprintf("WILL CLOSE to the network — declared by %s for this machine only",
strings.Join(rule.Because, ", "))
by := strings.Join(rule.Because, ", ")
switch rule.From {
case catalogue.FromMachine:
return fmt.Sprintf("WILL CLOSE to the network — declared by %s for this machine only", by)
case catalogue.FromMesh:
if len(d.mesh) == 0 {
return fmt.Sprintf("WILL CLOSE — declared by %s from the mesh, and this node "+
"knows no mesh addresses", by)
}
if !onMesh {
return fmt.Sprintf("%s — declared by %s from the mesh only", closesOutside, by)
}
}
return fmt.Sprintf("declared by %s (from %s)", strings.Join(rule.Because, ", "), rule.From)
return fmt.Sprintf("declared by %s (from %s)", by, rule.From)
}
return "WILL CLOSE — no module assigned here declares it"
}