Replace a value given by hand like one the mesh made (hq ADR 0228)
A given own secret the module reads at start is held by nobody but that module, so the mesh need not read it to replace it: secret rotate now works on it, and a value given through secret accept is replaced on its own after the module's first good start under the mesh. Only a value an outside party issues (own-secrets "issued-by": "outside") or one the module applies stays as given, refused with the reason.
This commit is contained in:
@@ -373,7 +373,8 @@ func (i *Inventory) SecretForModule(ctx context.Context, node, module, name stri
|
||||
on conflict (node, module, name) do update set
|
||||
sealed = excluded.sealed, node_key = excluded.node_key,
|
||||
origin = excluded.origin, made_at = now(),
|
||||
operator_sealed = excluded.operator_sealed, operator_key = excluded.operator_key`,
|
||||
operator_sealed = excluded.operator_sealed, operator_key = excluded.operator_key,
|
||||
replace_after_start = null`,
|
||||
record.ID, module, name, made.ForConsumer, key, forOperator, operatorKey); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -390,49 +391,61 @@ func (i *Inventory) SecretForModule(ctx context.Context, node, module, name stri
|
||||
// Sealed on the way in and the plaintext discarded, exactly as a generated one is — so the only
|
||||
// difference between the two is where the value came from.
|
||||
func (i *Inventory) AcceptSecretForModule(ctx context.Context, node, module, name, value string) error {
|
||||
_, err := i.acceptOwn(ctx, node, module, name, value, false)
|
||||
return err
|
||||
}
|
||||
|
||||
// acceptOwn is AcceptSecretForModule, and — with untilStart, for a value a person gave to a secret
|
||||
// the mesh may make — marks it to be replaced after the module's first good start (novox/hq ADR
|
||||
// 0228). It answers whether it was so marked.
|
||||
func (i *Inventory) acceptOwn(ctx context.Context, node, module, name, value string, untilStart bool) (bool, error) {
|
||||
// Refused for a name the module does not declare. A value stored under a name nothing reads
|
||||
// is a delivery that changed nothing and reported success — the shape of failure the mesh
|
||||
// is built to refuse (novox/hq 04-ISSUES/078).
|
||||
m, err := i.declared(ctx, module)
|
||||
if err != nil {
|
||||
return err
|
||||
return false, err
|
||||
}
|
||||
if _, own := m.OwnSecrets[name]; !own {
|
||||
return fmt.Errorf("%s does not declare %q as an own secret; %s — a secret it requires from a provider is accepted with `--provider <node> [--local <name>]`, the value the running service already uses (novox/hq ADR 0163)", module, name, declaresOwn(m))
|
||||
own, declared := m.OwnSecrets[name]
|
||||
if !declared {
|
||||
return false, fmt.Errorf("%s does not declare %q as an own secret; %s — a secret it requires from a provider is accepted with `--provider <node> [--local <name>]`, the value the running service already uses (novox/hq ADR 0163)", module, name, declaresOwn(m))
|
||||
}
|
||||
untilStart = untilStart && own.MeshMayMake()
|
||||
key, err := i.SealingKeyOf(ctx, node)
|
||||
if err != nil {
|
||||
return err
|
||||
return false, err
|
||||
}
|
||||
if key == "" {
|
||||
return fmt.Errorf(
|
||||
return false, fmt.Errorf(
|
||||
"%s has no sealing key, so nothing can be sealed to it — it joins again to get one",
|
||||
node)
|
||||
}
|
||||
record, err := i.NodeByName(ctx, node)
|
||||
if err != nil {
|
||||
return err
|
||||
return false, err
|
||||
}
|
||||
|
||||
sealed, err := secrets.Accept(value, key, key)
|
||||
if err != nil {
|
||||
return err
|
||||
return false, err
|
||||
}
|
||||
// And to the operator, when the mesh has one: a value a person supplied is the one a person
|
||||
// most needs to get back, since the mesh cannot make another (novox/hq ADR 0085, amended).
|
||||
forOperator, operatorKey, err := i.operatorSeal(ctx, value)
|
||||
if err != nil {
|
||||
return err
|
||||
return false, err
|
||||
}
|
||||
_, err = i.store.Pool().Exec(ctx,
|
||||
`insert into module_secret (node, module, name, sealed, node_key, origin, operator_sealed, operator_key)
|
||||
values ($1, $2, $3, $4, $5, 'accepted', $6, $7)
|
||||
`insert into module_secret (node, module, name, sealed, node_key, origin, operator_sealed, operator_key,
|
||||
replace_after_start)
|
||||
values ($1, $2, $3, $4, $5, 'accepted', $6, $7, case when $8 then now() end)
|
||||
on conflict (node, module, name) do update set
|
||||
sealed = excluded.sealed, node_key = excluded.node_key,
|
||||
origin = excluded.origin, made_at = now(),
|
||||
operator_sealed = excluded.operator_sealed, operator_key = excluded.operator_key`,
|
||||
record.ID, module, name, sealed.ForConsumer, key, forOperator, operatorKey)
|
||||
return err
|
||||
operator_sealed = excluded.operator_sealed, operator_key = excluded.operator_key,
|
||||
replace_after_start = excluded.replace_after_start`,
|
||||
record.ID, module, name, sealed.ForConsumer, key, forOperator, operatorKey, untilStart)
|
||||
return untilStart, err
|
||||
}
|
||||
|
||||
// Holder is one end-to-end credential: who gets it and who must create it.
|
||||
@@ -537,8 +550,12 @@ func (e ErrNotRotatable) Error() string { return e.Why }
|
||||
// backend that takes it once would, rotated this way, leave the backend on the old value and the
|
||||
// module reading the new one — the fault issue 179 was. That form is staged, which the mesh does
|
||||
// not build yet, and is refused by name. A secret whose manifest says neither is refused with the
|
||||
// word to write; a secret given to the mesh rather than made by it is refused as 0113 says: the
|
||||
// mesh will not replace what it cannot read.
|
||||
// word to write.
|
||||
//
|
||||
// **A value given to the mesh is replaced like one it made** (novox/hq ADR 0228, extending 0113):
|
||||
// the old value is not read, and need not be — a secret the module reads at start is held by
|
||||
// nobody but that module, so a fresh one sent to it is the whole change. Refused only for a value
|
||||
// an outside party issued (`"issued-by": "outside"`): no value the mesh makes would work there.
|
||||
func (i *Inventory) RotateModuleSecret(ctx context.Context, node, module, name string) error {
|
||||
m, err := i.declared(ctx, module)
|
||||
if err != nil {
|
||||
@@ -548,6 +565,17 @@ func (i *Inventory) RotateModuleSecret(ctx context.Context, node, module, name s
|
||||
if !declared {
|
||||
return fmt.Errorf("%s does not declare %q as an own secret; %s — a secret it requires from a provider is accepted with `--provider <node> [--local <name>]`, the value the running service already uses (novox/hq ADR 0163)", module, name, declaresOwn(m))
|
||||
}
|
||||
record, err := i.NodeByName(ctx, node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if own.IssuedBy == catalogue.IssuedOutside {
|
||||
return ErrNotRotatable{Why: fmt.Sprintf(
|
||||
"%s's %q is issued by a party outside the mesh — its definition says \"issued-by\": "+
|
||||
"\"outside\" — so no value the mesh makes would work in its place (ADR 0228)%s. Have its "+
|
||||
"issuer make a new one, then `secret accept %s %s %s --from <file>`",
|
||||
module, name, i.givenAgo(ctx, record.ID, module, name), node, module, name)}
|
||||
}
|
||||
switch own.Taken {
|
||||
case catalogue.TakenAtStart:
|
||||
case catalogue.TakenApplied:
|
||||
@@ -564,10 +592,6 @@ func (i *Inventory) RotateModuleSecret(ctx context.Context, node, module, name s
|
||||
"starts, or \"applied\" when its own code applies it",
|
||||
module, name, name)}
|
||||
}
|
||||
record, err := i.NodeByName(ctx, node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, err := i.SealingKeyOf(ctx, node)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -585,16 +609,16 @@ func (i *Inventory) RotateModuleSecret(ctx context.Context, node, module, name s
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if origin == OriginAccepted {
|
||||
return ErrNotRotatable{Why: fmt.Sprintf(
|
||||
"%s on %s holds %q as a value given to the mesh, not made by it, and the mesh will not "+
|
||||
"replace what it cannot read (ADR 0113). Change it where it lives, then `secret accept "+
|
||||
"%s %s %s` with the new value",
|
||||
module, node, name, node, module, name)}
|
||||
}
|
||||
return i.remakeOwn(ctx, record.ID, key, m, module, name)
|
||||
}
|
||||
|
||||
// remakeOwn puts a value the mesh makes in a module's own secret, whatever it held: sealed to the
|
||||
// machine and the operator, origin made, and no longer awaiting a replacement. A secret that is a
|
||||
// provider's one credential (ADR 0158) is remade for every holder at once.
|
||||
func (i *Inventory) remakeOwn(ctx context.Context, nodeID any, key string, m catalogue.Manifest, module, name string) error {
|
||||
if len(m.ProvisionsSharing(name)) > 0 {
|
||||
// Shared with every consumer of those provisions (ADR 0158): one new value, sealed to all.
|
||||
return i.remakeShared(ctx, record.ID, key, module, name, "", nil, "", "", "")
|
||||
return i.remakeShared(ctx, nodeID, key, module, name, "", nil, "", "", "")
|
||||
}
|
||||
operator, err := i.OperatorKey(ctx)
|
||||
if err != nil {
|
||||
@@ -607,9 +631,9 @@ func (i *Inventory) RotateModuleSecret(ctx context.Context, node, module, name s
|
||||
forOperator, operatorKey := operatorColumns(operator, blob)
|
||||
_, err = i.store.Pool().Exec(ctx,
|
||||
`update module_secret set sealed = $4, node_key = $5, origin = 'made', made_at = now(),
|
||||
operator_sealed = $6, operator_key = $7
|
||||
operator_sealed = $6, operator_key = $7, replace_after_start = null
|
||||
where node = $1 and module = $2 and name = $3`,
|
||||
record.ID, module, name, made.ForConsumer, key, forOperator, operatorKey)
|
||||
nodeID, module, name, made.ForConsumer, key, forOperator, operatorKey)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -725,7 +749,7 @@ func (i *Inventory) remakeShared(ctx context.Context, providerID any, providerKe
|
||||
on conflict (node, module, name) do update set
|
||||
sealed = excluded.sealed, node_key = excluded.node_key, origin = 'made', made_at = now(),
|
||||
operator_sealed = excluded.operator_sealed, operator_key = excluded.operator_key,
|
||||
generation = excluded.generation`,
|
||||
generation = excluded.generation, replace_after_start = null`,
|
||||
providerID, providerModule, own, ownSealed, providerKey, forOperator, operatorKey, generation); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user