package overlay import ( "encoding/json" "strings" "testing" ) func declarationFor(t *testing.T, node Node, peers []Peer) (string, []map[string]any) { t.Helper() raw, err := Declaration(node, peers, "") if err != nil { t.Fatal(err) } var d struct { Declaration int `json:"declaration"` Resources []map[string]any `json:"resources"` } if err := json.Unmarshal(raw, &d); err != nil { t.Fatal(err) } if d.Declaration != 1 { t.Fatalf("declaration version %d", d.Declaration) } for _, r := range d.Resources { if r["type"] == "file" { return r["content"].(string), d.Resources } } t.Fatal("the declaration has no configuration file in it") return "", nil } func TestNoPrivateKeyEverTravels(t *testing.T) { // The property the whole design rests on: the node generated its keypair and kept the private // half, so the mesh composes a configuration for a node it cannot impersonate. A private key // appearing here would mean the control plane had one — and a copy of its database would then // be every node's network identity. config, _ := declarationFor(t, Node{Name: "laptop", Key: "PUB", Address: "10.42.0.2"}, []Peer{{Name: "anchor", Key: "HUB", Allowed: "10.42.0.0/16", Endpoint: "198.51.100.1:51820"}}) if strings.Contains(config, "PrivateKey") { t.Error("the configuration carries a PrivateKey line; the mesh must never hold one") } if !strings.Contains(config, "private-key "+DefaultKeyPath) { t.Error("the configuration does not point at the key file the node wrote, so the " + "interface would come up with no key at all") } } func TestTheDeclarationUsesOnlyShapesTheHostAlreadyHas(t *testing.T) { // Connectivity needs nothing new from tier 0, and that is worth holding: the host does not // know what a private network is, and should not learn. _, resources := declarationFor(t, Node{Name: "laptop", Key: "PUB", Address: "10.42.0.2"}, nil) allowed := map[string]bool{"package": true, "file": true, "service": true, "directory": true, "container": true, "action": true} for _, r := range resources { if !allowed[r["type"].(string)] { t.Errorf("the overlay declaration uses %q, which the host does not have", r["type"]) } } } func TestTheInterfaceComesBackAfterAReboot(t *testing.T) { // A node whose overlay only exists while something is watching is not a node that survives // being switched off and on — and it would come back unreachable, which is the worst way to // come back. _, resources := declarationFor(t, Node{Name: "laptop", Key: "PUB", Address: "10.42.0.2"}, nil) for _, r := range resources { if r["type"] == "service" { if r["boot"] != "enabled" { t.Error("the overlay interface is not enabled at boot") } if r["state"] != "running" { t.Error("the overlay interface is not asked to be running") } return } } t.Error("nothing in the declaration brings the interface up") } func TestTheConfigurationIsNotWorldReadable(t *testing.T) { // It lists every peer's key and endpoint, which is a map of the mesh. Not secret the way a // private key is, and not something to leave readable on a machine somebody else also uses. _, resources := declarationFor(t, Node{Name: "laptop", Key: "PUB", Address: "10.42.0.2"}, nil) for _, r := range resources { if r["type"] == "file" && r["mode"] != "0600" { t.Errorf("the peer list is mode %v", r["mode"]) } } } func TestAPeerThatCannotBeDialledSaysSo(t *testing.T) { // A [Peer] with no Endpoint is correct and looks like a mistake. Saying why stops somebody // helpfully adding one that cannot work. config, _ := declarationFor(t, Node{Name: "anchor", Key: "HUB", Address: "10.42.0.1", Endpoint: "198.51.100.1:51820", Hub: true}, []Peer{{Name: "laptop", Key: "PUB", Allowed: "10.42.0.2/32", Why: "routes through this hub"}}) if strings.Contains(config, "Endpoint =") { t.Error("an endpoint was written for a peer that has none") } if !strings.Contains(config, "cannot be dialled") { t.Error("the file does not say why that peer has no endpoint") } } func TestTheFileSaysNotToEditIt(t *testing.T) { // It is replaced whenever the graph changes. An edit survives until then and vanishes, which // is worse than never being applied — the machine works, then stops, and nothing changed // that anybody remembers. config, _ := declarationFor(t, Node{Name: "laptop", Key: "PUB", Address: "10.42.0.2"}, nil) if !strings.Contains(config, "Do not edit") { t.Error("a generated file does not say it is generated") } } func TestANodeWithNoAddressIsRefused(t *testing.T) { // Rather than a configuration with a blank address, which wg-quick would reject on the // machine, at boot, where the failure is much harder to see. if _, err := Declaration(Node{Name: "laptop", Key: "PUB"}, nil, ""); err == nil { t.Fatal("a node with no overlay address was given a configuration") } } func TestOnlyAReachableNodeListens(t *testing.T) { // A ListenPort on a node nothing can dial is a port open for no reason. config, _ := declarationFor(t, Node{Name: "laptop", Key: "PUB", Address: "10.42.0.2"}, nil) if strings.Contains(config, "ListenPort") { t.Error("a node that cannot be dialled was told to listen") } config, _ = declarationFor(t, Node{Name: "anchor", Key: "HUB", Address: "10.42.0.1", Endpoint: "198.51.100.1:51820"}, nil) if !strings.Contains(config, "ListenPort = 51820") { t.Error("a reachable node does not listen on the port its endpoint names") } }