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:
@@ -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.
|
// 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) {
|
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()
|
t.Helper()
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
body, err := composed(t, open, "anchor").Body()
|
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{
|
if err := (link.Enrolment{Inventory: open.inventory}).Heard(ctx, link.Report{
|
||||||
Node: "anchor", Applied: []string{"hello-web.x"}, Declared: digestOf(body),
|
Node: "anchor", Applied: []string{"hello-web.x"}, Declared: digestOf(body),
|
||||||
Firewall: "ufw", Held: held,
|
Firewall: "ufw", Held: held, Reachable: reachable,
|
||||||
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},
|
|
||||||
},
|
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -159,7 +165,9 @@ func TestConvergingPreviewsThenChangesAndAdoptingKeepsWhatWasTaken(t *testing.T)
|
|||||||
"tcp/8080 hello-web (published, container port 80)",
|
"tcp/8080 hello-web (published, container port 80)",
|
||||||
"declared by hello-web (from anywhere)",
|
"declared by hello-web (from anywhere)",
|
||||||
"WILL CLOSE — no module assigned here declares it",
|
"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",
|
"notes\n replacing the found file /etc/notes.conf (original kept at",
|
||||||
"assigns nftables",
|
"assigns nftables",
|
||||||
"the found firewall (ufw) is disabled, never flushed",
|
"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)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -217,7 +217,9 @@ func converge(ctx context.Context, open *stores, node string, yes bool, filter s
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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 {
|
if !yes {
|
||||||
return preview + fmt.Sprintf("\n\nNothing has changed. Run `converge %s --yes` to do it.", node), nil
|
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.
|
// 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 {
|
plan catalogue.Resolution, taken []string, filter string, filterAssigned bool) string {
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
fmt.Fprintf(&b, "converging %s\n", node)
|
fmt.Fprintf(&b, "converging %s\n", node)
|
||||||
@@ -269,7 +271,7 @@ func previewOf(node string, reported inventory.Adoption, rules []catalogue.Rule,
|
|||||||
if r.Published {
|
if r.Published {
|
||||||
what += fmt.Sprintf(" (published, container port %d)", r.ContainerPort)
|
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 {
|
if len(reported.Reachable) == 0 {
|
||||||
b.WriteString(" nothing reported\n")
|
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")
|
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
|
// fate is what the derived filter does to one reachable thing: which module declares it and from
|
||||||
// where, or that it will close.
|
// where, or that it will close — wholly, or to everything outside the private network. Rendered
|
||||||
func fate(r inventory.Reach, rules []catalogue.Rule, foundation []int) string {
|
// 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 {
|
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 {
|
if r.Protocol == "tcp" && r.Port == port {
|
||||||
return "stays open — the mesh's own, from anywhere"
|
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 {
|
if rule.Port != r.Port || rule.Protocol != r.Protocol {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if rule.From == catalogue.FromMachine {
|
by := strings.Join(rule.Because, ", ")
|
||||||
return fmt.Sprintf("WILL CLOSE to the network — declared by %s for this machine only",
|
switch rule.From {
|
||||||
strings.Join(rule.Because, ", "))
|
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"
|
return "WILL CLOSE — no module assigned here declares it"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user