diff --git a/modules/mesh-vault/cmd/mesh-vault/accounts.go b/modules/mesh-vault/cmd/mesh-vault/accounts.go index c9f74094..1accbc27 100644 --- a/modules/mesh-vault/cmd/mesh-vault/accounts.go +++ b/modules/mesh-vault/cmd/mesh-vault/accounts.go @@ -275,15 +275,19 @@ func (v *Vault) HashOwed() { names = append(names, n) } sort.Strings(names) + still := map[string]string{} for _, n := range names { if _, held, err := v.Store.Read(n); err == nil && !held { v.hashes.paid(n) continue } if _, err := v.HashAccount(n, false); err != nil { - v.log("the hash of %s is still owed: %v", n, err) + still[n] = err.Error() } } + if len(still) > 0 { + v.log("%d of %d owed bus-account hashes are still owed, tried again at the next pass: %v", len(still), len(names), still) + } } // HashUser is the tool account_hash: the covered account whose value names user, hashed again now. @@ -308,10 +312,13 @@ func (v *Vault) HashUser(user, caller string) (Hashed, error) { h, err := v.HashAccount(r.Name, true) if err == nil { v.log("hashed %s again (%s, generation %d), asked by %s", user, r.Name, h.Generation, someoneOr(caller, "a tool")) + } else { + v.log("did not hash %s again (%s), asked by %s: %v", user, r.Name, someoneOr(caller, "a tool"), err) } return h, err } } + v.log("did not hash %s again, asked by %s: the vault holds no bus account of it", user, someoneOr(caller, "a tool")) return Hashed{}, fmt.Errorf("the vault holds no bus account of %s: an account it holds no value for is adopted, "+ "never hashed here", user) } diff --git a/modules/mesh-vault/cmd/mesh-vault/intake.go b/modules/mesh-vault/cmd/mesh-vault/intake.go index 329b2c8e..33e91639 100644 --- a/modules/mesh-vault/cmd/mesh-vault/intake.go +++ b/modules/mesh-vault/cmd/mesh-vault/intake.go @@ -32,7 +32,6 @@ import ( "errors" "fmt" "regexp" - "slices" "strings" "time" @@ -197,15 +196,27 @@ func zero(b []byte) { // CoveredSecrets are the names, in a module's own secrets, of the bus accounts the controller mints for a core module // or a module marked moved and hands to the vault (novox/hq ADR 0374, issue 787): the module's account on the bus -// (`broker`) and, for the machine's provisioner client, its own (`provisioner-broker`). -var CoveredSecrets = []string{"broker", "provisioner-broker"} +// (`broker`), for the machine's provisioner client its own (`provisioner-broker`), and for each process of its bus +// declarations its own (`broker-`, ADR 0436 §2), named here by ProcessSecretPrefix. +var CoveredSecrets = []string{"broker", "provisioner-broker", ProcessSecretPrefix + ""} -// Covered says whether secret is a covered account: a module's own secret named one of CoveredSecrets. The vault -// cannot tell core or moved from a module's manifest; the controller, which can, is the only caller that may hand -// one in, and the name keeps what it hands to these. +// ProcessSecretPrefix starts a process's own bus account's secret, `broker-` (the controller's +// processSecretName). +const ProcessSecretPrefix = "broker-" + +// Covered says whether secret is a covered account: a module's own secret named `broker`, `provisioner-broker` or +// `broker-`. The vault cannot tell core or moved from a module's manifest; the controller, which can, is the +// only caller that may hand one in, and the name keeps what it hands to these. func Covered(secret string) bool { parts := strings.Split(secret, "/") - return len(parts) == 4 && parts[0] == "own" && slices.Contains(CoveredSecrets, parts[3]) + if len(parts) != 4 || parts[0] != "own" { + return false + } + name := parts[3] + if id, process := strings.CutPrefix(name, ProcessSecretPrefix); process { + return id != "" + } + return name == "broker" || name == "provisioner-broker" } // AcceptOpen opens a call to take a value given at the controller's terminal for secret, which the terminal diff --git a/modules/mesh-vault/cmd/mesh-vault/issued_account_test.go b/modules/mesh-vault/cmd/mesh-vault/issued_account_test.go index a54b9d2d..ef6a1de5 100644 --- a/modules/mesh-vault/cmd/mesh-vault/issued_account_test.go +++ b/modules/mesh-vault/cmd/mesh-vault/issued_account_test.go @@ -181,6 +181,20 @@ func TestAHandedBusAccountIsHashedOncePerGeneration(t *testing.T) { } return err }, "pw-3", 3, 6, 1}, + {"a process's own account (ADR 0436 §2) is covered and hashed", func() error { + _, err := handIssued(t, v, ControllerCaller, "own/ace/mesh-build-go/broker-worker", + `{"node":"ace","module":"mesh-build-go","user":"ace.mesh-build-go.worker","password":"pw-w"}`) + if !acc.hashOf("ace.mesh-build-go.worker", "pw-w", 1) { + return errors.New("ace.mesh-build-go.worker is not hashed") + } + if _, rerr := v.Rotate(ControllerCaller, "own/ace/mesh-build-go/broker-worker", "jochen", "x"); rerr == nil { + return errors.New("the vault rotated a process's covered account") + } + if Covered("own/ace/mesh-build-go/broker-") || Covered("own/ace/mesh-build-go/brokers") { + return errors.New("a name that is no process account is covered") + } + return err + }, "pw-3", 3, 7, 1}, } { before := len(v.bus.events) if err := step.do(); err != nil {