//go:build integration package main import ( "strings" "testing" "time" "git.novox.be/novox/mesh-sdk/go/catalogue" "github.com/novox/mesh-controller/internal/broker" "github.com/novox/mesh-controller/internal/inventory" "github.com/novox/mesh-controller/internal/link" ) // A machine's send makes and seals none of the secrets of a module handed to the provisioner there (novox/hq ADR // 0349 §7, §11, design 57 V4, issue 647): its client asks the vault for them. A module not moved, on the same // machine, has its secret made and sealed as before. func aClientModule() catalogue.Manifest { return catalogue.Manifest{Module: "mesh-provisioner-client", Version: "1", Claims: []catalogue.Claim{{Name: broker.NodeProvisionerSeat, Scope: catalogue.ScopeNode}}, OwnSecrets: catalogue.OwnSecrets{broker.ProvisionerSecret: {Path: "/var/lib/mesh-provisioner-client/broker"}}, Resources: []map[string]any{{"id": "client", "type": "process", "name": "mesh-provisioner-client", "run": []any{"./mesh-provisioner-client"}, "user": "mesh-provisioner-client"}}} } func aBot(module string, moved bool) catalogue.Manifest { m := catalogue.Manifest{Module: module, Version: "1", OwnSecrets: catalogue.OwnSecrets{"token": {Path: "/var/lib/" + module + "/token"}}, Resources: []map[string]any{{"id": "conf", "type": "file", "path": "/etc/" + module + "/bot.conf", "content": "token = ${secret:token}\n"}}} if moved { m.Moved = true m.Handed = &catalogue.Handed{Source: "https://templates.example/" + module + ".tar.gz", Digest: "sha256:" + strings.Repeat("e", 64), Templates: map[string]string{"conf": "bot.conf"}} } return m } func TestTheSendSealsNoSecretOfAModuleHandedThere(t *testing.T) { open := aMesh(t) ctx := t.Context() inv := open.inventory client := aClientModule() for _, m := range []catalogue.Manifest{client, aBot("moved-bot", true), aBot("kept-bot", false)} { register(t, open, m) if _, err := assign(ctx, open, "laptop", m.Module); err != nil { t.Fatal(err) } } if err := issueProvisionerClient(ctx, inv, client, "laptop", broker.Broker{Fingerprint: "ab"}, "10.0.0.1:4222"); err != nil { t.Fatal(err) } if _, err := inv.RecordHealth(ctx, inventory.NodeHealth{Node: "laptop", Contract: link.HandedContract, SaidAt: time.Now()}); err != nil { t.Fatal(err) } sent := sentTo(t, open, "laptop") var moved, kept map[string]any for _, r := range sent { switch r["id"] { case "moved-bot.needs-token": moved = r case "kept-bot.needs-token": kept = r } } if moved == nil || moved["sealed"] != nil || moved["owner-at-runtime"] != "owned" { t.Fatalf("the handed module's secret file is sent as %v: placed, with no value", moved) } if kept == nil || kept["sealed"] == nil { t.Fatalf("the module not moved has its secret sent as %v: sealed, as before", kept) } if _, held, err := inv.ModuleSecretIfIssued(ctx, "laptop", "moved-bot", "token"); err != nil || held { t.Fatalf("the send made the handed module's secret in the controller's store (held %v, %v)", held, err) } for _, r := range sent { if r["id"] == "moved-bot.conf" && (r["content"] != nil || r["sealed"] != nil) { t.Fatalf("the handed module's templated file carries text or a value: %v", r) } } }