From 3028c7ec4ed21b14e58d5043c22d54c3bbcbb2bc Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 28 Oct 2026 10:00:29 +0100 Subject: [PATCH] A drifted own secret is read from a resource id as a node runs it, its module's name first (only-office.needs-jwt), so the node agent finds what to hand the vault (issue 1126, cause C4; review round 2) delivery.OwnNeedName reads .needs- and the bare needs-; OwnValuesDiffer and the node agent use it. The fixtures carry the id shape captured from novox's shadow answer. --- go/delivery/own_drift.go | 17 ++++++++++++++--- go/delivery/placement_test.go | 23 ++++++++++++++++------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/go/delivery/own_drift.go b/go/delivery/own_drift.go index fac57fa..546a4ee 100644 --- a/go/delivery/own_drift.go +++ b/go/delivery/own_drift.go @@ -27,16 +27,27 @@ type OwnValueDiffers struct { // ownNeedPrefix starts the id of the file a module's own secret is placed in (catalogue.NeedID). var ownNeedPrefix = catalogue.NeedID("") +// OwnNeedName is the name of module's own secret whose file a resource id names, and whether it names one. On a node a +// resource's id carries its module's name (`only-office.needs-jwt`, as a live shadow answer shows it); the bare form a +// declaration composes (`needs-jwt`, catalogue.NeedID) is read too. An id of another module's is not this module's. +func OwnNeedName(module, id string) (string, bool) { + if module != "" { + id = strings.TrimPrefix(id, module+".") + } + name, own := strings.CutPrefix(id, ownNeedPrefix) + return name, own && name != "" && !strings.Contains(name, ".") +} + // OwnValuesDiffer is every own secret a's shown bodies say holds another value composed than running: a body of a -// module's own secret's file (catalogue.NeedID) masked MaskedAnotherValue on either side. Sorted by module and name, each +// module's own secret's file (OwnNeedName: `.needs-` on a node) masked MaskedAnotherValue on either side. Sorted by module and name, each // once. A body the answer did not show (beyond MaxBodies) is not read. func OwnValuesDiffer(a CandidateAnswer) []OwnValueDiffers { seen := map[OwnValueDiffers]bool{} var out []OwnValueDiffers for _, d := range a.Differs { for _, b := range d.Bodies { - name, own := strings.CutPrefix(b.ID, ownNeedPrefix) - if !own || name == "" || !anotherValue(b) { + name, own := OwnNeedName(d.Module, b.ID) + if !own || !anotherValue(b) { continue } o := OwnValueDiffers{Module: d.Module, Name: name} diff --git a/go/delivery/placement_test.go b/go/delivery/placement_test.go index 8f43377..1799d06 100644 --- a/go/delivery/placement_test.go +++ b/go/delivery/placement_test.go @@ -356,19 +356,28 @@ func TestPlacement(t *testing.T) { } // Issue 1126, cause C4: the own secrets whose two sides open to two values are read from the shown bodies, and // every body so masked is counted, own or not. - drift := CompareComposedWith(map[string]Composed{"k": comp(sealedFile("needs-jwt", "box-1", "0600"), - sealedFile("needs-salt", "box-4", "0600"), sealedFile("conf", "box-6", "0600"))}, - []Composed{comp(sealedFile("needs-jwt", "box-2", "0600"), sealedFile("needs-salt", "box-5", "0600"), - sealedFile("conf", "box-7", "0600"))}, + // The ids are as a node runs them, its module's name first (captured from novox's shadow answer, 2026-10-28: + // `only-office.needs-jwt`); another module's need, named here, is not nats's. + drift := CompareComposedWith(map[string]Composed{"k": comp(sealedFile("nats.needs-jwt", "box-1", "0600"), + sealedFile("nats.needs-salt", "box-4", "0600"), sealedFile("nats.conf", "box-6", "0600"), + sealedFile("other.needs-key", "box-8", "0600"))}, + []Composed{comp(sealedFile("nats.needs-jwt", "box-2", "0600"), sealedFile("nats.needs-salt", "box-5", "0600"), + sealedFile("nats.conf", "box-7", "0600"), sealedFile("other.needs-key", "box-9", "0600"))}, func(a, b string) (same, known bool) { return a == "box-4", true }) got := CandidateAnswer{Head: "h", Differs: drift.Differs} if own := OwnValuesDiffer(got); len(own) != 1 || own[0] != (OwnValueDiffers{Module: "nats", Name: "jwt"}) { t.Errorf("the own secrets with another value are %+v, want nats jwt alone", own) } - if n := ValuesDiffer(got); n != 2 { - t.Errorf("%d bodies hold another value, want 2 (jwt and conf)", n) + if n := ValuesDiffer(got); n != 3 { + t.Errorf("%d bodies hold another value, want 3 (jwt, conf and other's key)", n) } - if w := WithValuesDiffer(map[string]CandidateAnswer{"g": got}); w["g"].ValuesDiffer != 2 { + for id, want := range map[string]string{"nats.needs-jwt": "jwt", "needs-jwt": "jwt", "other.needs-jwt": "", + "nats.conf": "", "nats.needs-": ""} { + if name, own := OwnNeedName("nats", id); name != want && (own || want != "") { + t.Errorf("%s names nats's own secret %q (%v), want %q", id, name, own, want) + } + } + if w := WithValuesDiffer(map[string]CandidateAnswer{"g": got}); w["g"].ValuesDiffer != 3 { t.Errorf("the answer carries values_differ %d", w["g"].ValuesDiffer) } if c := CompareComposed(nil, []Composed{{Node: "novox", Resources: []json.RawMessage{}}}); len(c.Removed) != 1 || c.Removed[0] != "node" {