Refuse a provision's value unless the vault's credential name and generation are the ones its reference names (issue 1954 L6-E1; ADR 0570 clauses 1, 6)

The value written is the one the provision names: a1-47 makes Reference.ID the vault's credential name. A mismatch in
either refuses the write, naming both. Test: a row in TestAWriteAndAnApplyOfTheSameFileSerialise.
This commit is contained in:
2026-10-29 22:37:44 +01:00
parent 6b32e07164
commit 5497d362dd
2 changed files with 14 additions and 4 deletions
+9 -1
View File
@@ -50,6 +50,7 @@ func TestAWriteAndAnApplyOfTheSameFileSerialise(t *testing.T) {
// The provision m reads, as mesh-provisioner keeps it, and the vault's value of it, sealed to this machine (faked:
// the opener reads the box's "v").
provision := map[string]string{}
vaultName := "provision/postgres/db"
secrets := provisionSecrets{node: "n1",
read: func(_ context.Context, key string) ([]byte, bool, error) {
v, ok := provision[key]
@@ -60,7 +61,7 @@ func TestAWriteAndAnApplyOfTheSameFileSerialise(t *testing.T) {
var a struct{ Credentials []map[string]any }
_ = json.Unmarshal(raw, &a)
c := a.Credentials[0]
c["generation"], c["sealed"] = 2, map[string]string{"v": "s3cret"}
c["name"], c["generation"], c["sealed"] = vaultName, 2, map[string]string{"v": "s3cret"}
return json.Marshal(map[string]any{"node": "n1", "credentials": []any{c}})
},
open: func(sealed string) ([]byte, error) {
@@ -149,9 +150,12 @@ func TestAWriteAndAnApplyOfTheSameFileSerialise(t *testing.T) {
content string
want string // the file after; "" keeps what was there
mode os.FileMode
vault string // the vault's credential name, when not the one the provision names
}{
{name: "a provision not provided is refused", state: provisions.Asked, content: "password=${secret:db}\n"},
{name: "provided with no reference is refused", state: provisions.Provided, content: "password=${secret:db}\n"},
{name: "the vault's value is another than the provision names", state: provisions.Provided, ref: true,
vault: "pair/n2/db/n1/m", content: "password=${secret:db}\n"},
{name: "provided: filled at the write, 0600", state: provisions.Provided, ref: true,
content: "password=${secret:db}\n", want: "password=s3cret\n", mode: 0o600},
{name: "no placeholder is written as it came, as the declaration says", content: "plain\n", want: "plain\n", mode: 0o644},
@@ -160,6 +164,10 @@ func TestAWriteAndAnApplyOfTheSameFileSerialise(t *testing.T) {
if tc.state != "" {
provision["n1.m.db"] = keyed(tc.state, tc.ref)
}
vaultName = "provision/postgres/db"
if tc.vault != "" {
vaultName = tc.vault
}
a := e.Write(context.Background(), uid, filling.Request{Path: path, Content: []byte(tc.content)})
got, _ := os.ReadFile(path)
if tc.want == "" {
+5 -3
View File
@@ -108,6 +108,7 @@ func (p provisionSecrets) value(ctx context.Context, module, provision, local st
Node string `json:"node"`
Credentials []struct {
asked
Name string `json:"name"`
Generation int `json:"generation"`
Sealed json.RawMessage `json:"sealed"`
} `json:"credentials"`
@@ -122,9 +123,10 @@ func (p provisionSecrets) value(ctx context.Context, module, provision, local st
if c.asked != ask {
continue
}
if c.Generation < pr.Reference.Generation {
return nil, fmt.Errorf("the vault holds %s at generation %d and the provision names %d: not written until it has it",
key, c.Generation, pr.Reference.Generation)
// The value the provision names, by the vault's credential name and its generation (clause 6): another is refused.
if c.Name != pr.Reference.ID || c.Generation != pr.Reference.Generation {
return nil, fmt.Errorf("the vault answered %s generation %d for %s, and the provision names %s generation %d: "+
"nothing is written until they agree", c.Name, c.Generation, key, pr.Reference.ID, pr.Reference.Generation)
}
value, err := p.open(string(c.Sealed))
if err != nil {