package resolution import ( "strings" "testing" mcat "git.novox.be/novox/mesh-sdk/go/catalogue" ) // anchored is a machine on the private network, which is where a name worth certifying comes from. func anchored() mcat.Node { n := workstation() n.At = "workstation.internal" return n } // authority is a module that must put its own machine's name into a file it writes. func authority() mcat.Manifest { return mcat.Manifest{ Module: "authority", Version: "1", Provides: mcat.FromAnywhere("acme-ca"), Serves: map[string]map[string]any{"acme-ca": {}}, Resources: []map[string]any{{ "id": "init", "type": "file", "path": "/var/lib/authority/init.env", "content": "NAMES=${machine:at},${machine:name},localhost\n", }}, } } func TestAModuleNamesTheMachineItWasAssignedTo(t *testing.T) { got, err := Resolve(shelf(authority()), []string{"authority"}, anchored(), mcat.World{}) if err != nil { t.Fatal(err) } content := "" for _, r := range mustDeclare(t, got) { if strings.HasSuffix(plainly(r["id"]), "init") { content = plainly(r["content"]) } } if content == "" { t.Fatal("the authority's file was not declared") } if strings.Contains(content, "${machine:") { t.Fatalf("the placeholder was written into the file as a value: %q", content) } // The machine's own name on the private network — the one a consumer is handed as `at`, and so // the one a certificate has to carry. if !strings.Contains(content, "workstation.internal") { t.Errorf("the file does not name the address consumers reach it at: %q", content) } } // A machine not on the private network has no such address, and a module asking to be reached at // one is named here rather than left to fail later as a certificate nobody can verify. func TestAnAddressAMachineDoesNotHaveIsRefused(t *testing.T) { got, err := Resolve(shelf(authority()), []string{"authority"}, workstation(), mcat.World{}) if err != nil { t.Fatal(err) } if _, err := Declaration(got, mcat.Rendering{}); err == nil { t.Fatal("an address the machine does not have was substituted rather than refused") } else if !strings.Contains(err.Error(), "at") { t.Errorf("the refusal does not name what was asked for: %v", err) } } // A module that must give software the machine's ADDRESS rather than its name — a resolver pointing // the container runtime at itself, whose list of resolvers cannot be a name — says // ${machine:address}, and gets what the machine's name resolves to on the private network: the same // address every other machine's hosts file carries for it. func TestAModuleNamesTheAddressBehindItsMachinesName(t *testing.T) { pointing := mcat.Manifest{Module: "pointing", Version: "1", Resources: []map[string]any{{ "id": "runtime", "type": "file", "path": "/etc/runtime.json", "content": `{"dns":["${machine:address}"]}`, }}} got, err := Resolve(shelf(pointing), []string{"pointing"}, anchored(), mcat.World{}) if err != nil { t.Fatal(err) } out, err := Declaration(got, mcat.Rendering{Names: map[string]string{ "workstation.internal": "10.42.0.7", "anchor.internal": "10.42.0.1"}}) if err != nil { t.Fatal(err) } if content := plainly(out[0]["content"]); content != `{"dns":["10.42.0.7"]}` { t.Fatalf("the machine's address was not the one its name resolves to: %q", content) } // Off the network there is no such address, and the refusal says what the machine does have — // rather than a placeholder written into the runtime's file and read as an address. got, err = Resolve(shelf(pointing), []string{"pointing"}, workstation(), mcat.World{}) if err != nil { t.Fatal(err) } if _, err := Declaration(got, mcat.Rendering{}); err == nil || !strings.Contains(err.Error(), "${machine:address}") { t.Fatalf("a machine off the network was given an address, or refused for another reason: %v", err) } } // The mesh's private range is offered as ${machine:mesh-range}, so a module names it rather than // hardcoding a value it cannot know (novox/hq ADR 0112) — the fail2ban ignoreip is the case. func TestAModuleNamesTheMeshRange(t *testing.T) { facts := machineFacts(mcat.Resolution{Node: "anchor", At: "anchor.internal"}, map[string]string{"anchor.internal": "10.10.0.1"}, "10.10.0.0/24") if facts["mesh-range"] != "10.10.0.0/24" { t.Fatalf("the mesh range is not a machine fact: %v", facts) } res := map[string]any{"type": "file", "id": "jail", "content": "ignoreip = 127.0.0.1/8 ${machine:mesh-range}\n"} if err := machineInto(res, facts, "fail2ban"); err != nil { t.Fatal(err) } if got := res["content"].(string); !strings.Contains(got, "10.10.0.0/24") || strings.Contains(got, "${machine:") { t.Fatalf("the mesh range was not written in: %q", got) } // A mesh with no range gives no such fact, and a file that names it is refused rather than // left with a literal placeholder in it. none := machineFacts(mcat.Resolution{Node: "anchor"}, nil, "") if _, has := none["mesh-range"]; has { t.Fatal("a mesh with no range still offered one") } }