package overlay import ( "strings" "testing" "github.com/novox/mesh-control/internal/catalogue" ) func networkOf(t *testing.T, nodes []Node) *Generator { t.Helper() made, err := From(nodes, "10.42.0.0/16", "/var/lib/mesh-host/overlay.key") if err != nil { t.Fatal(err) } return made } // A hub is dialled by every node at other sites, and needs its port open. A machine that is not a // hub dials out and needs nothing open at all. // // They are the same module, which is why a static field in a manifest cannot say it — and the // machine it gets wrong is the one facing the public internet, which is the machine that most // needs filtering. func TestOnlyAMachineThatCanBeDialledOpensTheOverlaysPort(t *testing.T) { network := networkOf(t, []Node{ {Name: "anchor", Key: "a", Endpoint: "198.51.100.10:51820", Site: "one", Hub: true}, {Name: "laptop", Key: "b", Site: "one"}, }) opens, err := network.Listens("anchor") if err != nil { t.Fatal(err) } if len(opens) != 1 { t.Fatalf("the machine every other one dials opens %d ports", len(opens)) } if opens[0].Port != 51820 || opens[0].At() != "udp" { t.Fatalf("the port is not the one the interface listens on: %+v", opens[0]) } // From everywhere, and deliberately: a node at another site is not on the private network // until this port lets it on, so restricting it to the mesh would be a rule that can never be // satisfied by the thing it exists for. if opens[0].From != catalogue.FromEverywhere { t.Fatalf("the way onto the private network is restricted to the private network: %+v", opens[0]) } if opens[0].Why == "" { t.Fatal("a port is opened and nothing says why, which is what makes a rule set unreadable") } // And the machine nothing dials opens nothing. Not harmless to get wrong: a rule with no // reason is one somebody later has to work out the reason for. quiet, err := network.Listens("laptop") if err != nil { t.Fatal(err) } if len(quiet) != 0 { t.Fatalf("a machine nothing dials opened %d port(s)", len(quiet)) } } // The port comes from the endpoint, which is where the interface takes its ListenPort from. One // source, so a rule set cannot open a port the interface is not on. func TestTheOpenedPortIsTheOneTheInterfaceListensOn(t *testing.T) { network := networkOf(t, []Node{ {Name: "anchor", Key: "a", Endpoint: "198.51.100.10:60000", Site: "one", Hub: true}, }) opens, err := network.Listens("anchor") if err != nil { t.Fatal(err) } if len(opens) != 1 || opens[0].Port != 60000 { t.Fatalf("the rule set would open a port the interface is not on: %+v", opens) } written, err := Declaration(Node{Name: "anchor", Key: "a", Address: "10.42.0.1", Endpoint: "198.51.100.10:60000", Hub: true}, nil, "/k") if err != nil { t.Fatal(err) } if !strings.Contains(string(written), "ListenPort = 60000") { t.Fatalf("the interface and the rule set disagree about the port:\n%s", written) } } // An endpoint with no port is refused rather than treated as a machine that opens nothing. // // A generator that cannot say what a machine opens is not one that says it opens nothing, and // closing a port on the evidence of a failure to look is how a machine is severed by a fault // somewhere else entirely. func TestAnEndpointWithNoPortIsRefusedRatherThanTakenAsSilence(t *testing.T) { network := networkOf(t, []Node{ {Name: "anchor", Key: "a", Endpoint: "198.51.100.10", Site: "one", Hub: true}, }) if _, err := network.Listens("anchor"); err == nil { t.Fatal("a machine whose port could not be read was treated as opening nothing") } } // A machine the network has never heard of opens nothing, and that is an answer rather than an // error: a node assigned the module before it is placed is in exactly that state. func TestAMachineNotOnTheNetworkOpensNothing(t *testing.T) { network := networkOf(t, []Node{ {Name: "anchor", Key: "a", Endpoint: "198.51.100.10:51820", Site: "one", Hub: true}, }) opens, err := network.Listens("a-stranger") if err != nil { t.Fatal(err) } if len(opens) != 0 { t.Fatalf("a machine not on the network opened %d port(s)", len(opens)) } }