Re-make an own secret sealed to a key the machine lost, and name the validator's mesh-sdk pin in a check

This commit is contained in:
2026-10-20 11:33:38 +00:00
parent 33b285ce09
commit f87590b0f9
4 changed files with 104 additions and 6 deletions
+3 -1
View File
@@ -204,7 +204,9 @@ func prepareSend(ctx context.Context, open *stores, node string) ([]string, erro
if n.SealingKey != "" {
held := map[string]bool{}
for _, r := range in.OwnSecrets {
if r.Node == node {
// Held only when sealed to the key the machine has now, or given by a person (which is not the mesh's
// to make again): a made row under an old key is re-made, as SecretForModule did.
if r.Node == node && (r.SealedTo == n.SealingKey || r.Origin == inventory.OriginAccepted) {
held[r.Module+"/"+r.Name] = true
}
}
@@ -195,3 +195,41 @@ func TestAFinishedSendMakesNothingTheSecondTime(t *testing.T) {
}
}
}
// Review round 1, finding 1: a machine that rejoined with a new sealing key keeps its made rows sealed to the old key. A
// send re-makes them for the new key, as SecretForModule did on main, and the file naming the secret is not refused.
func TestASendReMakesAnOwnSecretSealedToAKeyTheMachineNoLongerHas(t *testing.T) {
open := aMesh(t)
ctx := t.Context()
inv := open.inventory
register(t, open, aBot("bot", false))
if _, err := assign(ctx, open, "laptop", "bot"); err != nil {
t.Fatal(err)
}
if _, err := composeSend(ctx, open, "laptop"); err != nil {
t.Fatalf("the first send is refused: %v", err)
}
if err := inv.RecordSealingKey(ctx, nodeID(t, open, "laptop"), aPublicKey(t)); err != nil {
t.Fatal(err)
}
if _, err := composeSend(ctx, open, "laptop"); err != nil {
t.Fatalf("a machine that rejoined cannot be sent to: %v", err)
}
in, err := readComposeInput(ctx, open)
if err != nil {
t.Fatal(err)
}
n, _ := nodeOf(in.Input, "laptop")
found := false
for _, r := range in.OwnSecrets {
if r.Node == "laptop" && r.Module == "bot" && r.Name == "token" {
found = true
if r.SealedTo != n.SealingKey {
t.Fatalf("the own secret is still sealed to a key the machine no longer has")
}
}
}
if !found {
t.Fatal("the own secret has no row")
}
}
+37 -5
View File
@@ -1194,7 +1194,7 @@ func judgeFor(ctx context.Context, labelled, run Runner, spec CheckSpec, root, t
"go", "build", "-o", bin, "./cmd/mesh-controller")...); err != nil {
return "", "the change's controller, which judges itself, does not build: " + firstLine(err.Error()), nil
}
say("check", "judged by the change's own controller")
say("check", "judged by the change's own controller, with %s", validatorAt(tree))
return bin, "", nil
}
bin, _, err := judge(ctx, labelled, run, root, inToolchain, say)
@@ -1637,6 +1637,37 @@ func dialable(ctx context.Context, address string) error {
return fmt.Errorf("nothing took a connection at %s", address)
}
// sdkPin is the mesh-sdk version the controller in dir takes (its go.mod's go requirement), or "" when it names none.
func sdkPin(dir string) string {
body, err := os.ReadFile(filepath.Join(dir, "go.mod"))
if err != nil {
return ""
}
for _, line := range strings.Split(string(body), "\n") {
f := strings.Fields(strings.TrimPrefix(strings.TrimSpace(line), "require "))
if len(f) >= 2 && f[0] == "git.novox.be/novox/mesh-sdk/go" {
return f[1]
}
}
return ""
}
// validatorAt names the validator of the controller in dir as mesh-sdk's at that controller's pin (issue 1391).
func validatorAt(dir string) string {
if v := sdkPin(dir); v != "" {
return "mesh-sdk's validator at " + v
}
return "mesh-sdk's validator at the controller's pin"
}
// pinNote is the version of that pin, for a sentence that already says "the pin of": ", " and the version, or nothing.
func pinNote(dir string) string {
if v := sdkPin(dir); v != "" {
return " (" + v + ")"
}
return ""
}
// judge builds the controller that judges a change: the one the mesh runs, beside the check as
// mesh-controller — or, when that one predates the merge gate, the controller's main, said. It answers
// the binary and the directory it was built from.
@@ -1658,7 +1689,7 @@ func judge(ctx context.Context, run, plain Runner, root string, inToolchain func
return "", "", fmt.Errorf("the controller the mesh runs does not build: %w", err)
}
if hasGate() {
say("check", "judged by the controller the mesh runs")
say("check", "judged by the controller the mesh runs, with %s", validatorAt(filepath.Join(root, "mesh-controller")))
return bin, "mesh-controller", nil
}
}
@@ -1668,7 +1699,8 @@ func judge(ctx context.Context, run, plain Runner, root string, inToolchain func
if err := build("mesh-controller-main"); err != nil {
return "", "", fmt.Errorf("the controller's main does not build: %w", err)
}
say("check", "judged by the controller's main: the one the mesh runs predates the merge gate")
say("check", "judged by the controller's main: the one the mesh runs predates the merge gate, with %s",
validatorAt(filepath.Join(root, "mesh-controller-main")))
return bin, "mesh-controller-main", nil
}
@@ -1783,7 +1815,7 @@ func judgeFromTheGroup(ctx context.Context, labelled, run Runner, spec CheckSpec
// its pin. A mesh-host member brings no validator (issue 1391), and its tree need hold none.
source = groupDir(spec, root, tree, "mesh-controller")
from = "the group's own controller, merge " + headAt(ctx, run, source)
validator = "mesh-sdk's validator at the pin of the group's controller"
validator = "mesh-sdk's validator at the pin of the group's controller" + pinNote(source)
inputs = append(inputs, "group member "+spec.Repo+" "+judged.Head+" onto "+judged.Trunk)
for _, m := range judged.Members {
inputs = append(inputs, "group member "+m.Repo+" "+m.Head+" onto "+m.Trunk)
@@ -1791,7 +1823,7 @@ func judgeFromTheGroup(ctx context.Context, labelled, run Runner, spec CheckSpec
case spec.Judge != "self" && controller != nil:
source = filepath.Join(root, "mesh-controller")
from = merge("mesh-controller", controller)
validator = "mesh-sdk's validator at the pin of " + merge("mesh-controller", controller)
validator = "mesh-sdk's validator at the pin of " + merge("mesh-controller", controller) + pinNote(source)
default:
return "", "", false, nil
}
+26
View File
@@ -0,0 +1,26 @@
package builder
import (
"os"
"path/filepath"
"testing"
)
// Issue 1391, AC5: what a check says names the validator as mesh-sdk's at the controller's pin, with the version from the
// judge's own go.mod.
func TestTheValidatorIsNamedAsMeshSDKsAtTheControllersPin(t *testing.T) {
dir := t.TempDir()
mod := "module example.test/c\n\ngo 1.25\n\nrequire (\n\tgit.novox.be/novox/mesh-sdk/go v0.1.14-0.20261020085642-ab0c16ddad26\n)\n"
if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte(mod), 0o644); err != nil {
t.Fatal(err)
}
if got, want := validatorAt(dir), "mesh-sdk's validator at v0.1.14-0.20261020085642-ab0c16ddad26"; got != want {
t.Fatalf("the check says %q, not %q", got, want)
}
if got, want := pinNote(dir), " (v0.1.14-0.20261020085642-ab0c16ddad26)"; got != want {
t.Fatalf("the pin note is %q, not %q", got, want)
}
if got := validatorAt(t.TempDir()); got != "mesh-sdk's validator at the controller's pin" {
t.Fatalf("a controller with no go.mod is said as %q", got)
}
}