package bootstrap import ( "os" "strings" "testing" "github.com/novox/mesh-host/internal/declaration" ) // The produced bundle carries no well-known credential: the store reads its password from the // file genesis made, every connection string names the made values, and the broker's default // administrator is changed by an action before anything dials it (novox/hq issue 071). func TestTheProducedBundleCarriesNoWellKnownCredential(t *testing.T) { template, err := os.ReadFile("../../examples/foundation-first-node.lock") if err != nil { t.Skip("no example bundle beside this checkout") } r, err := Rewrite(template, "sha256:"+strings.Repeat("ab", 32)) if err != nil { t.Fatal(err) } creds := RootCredentials{Store: "STORE-PW-40-characters-of-random-base64u", Broker: "BROKER-PW-40-characters-of-random-base64"} got, err := RewriteRoot(&r, creds) if err != nil { t.Fatal(err) } text := string(r.Bundle) for _, gone := range []string{`"bootstrap"`, "postgres:bootstrap@", "guest:guest@"} { if strings.Contains(text, gone) { t.Errorf("the produced bundle still says %s", gone) } } if got.StoreURLs < 3 || got.BrokerURLs < 2 { t.Errorf("rewrote %d store and %d broker connections; the template has three and two", got.StoreURLs, got.BrokerURLs) } var store, action bool for _, res := range r.Declaration.Resources { switch x := res.(type) { case *declaration.Container: if x.Name != "mesh-store" { continue } store = true if _, has := x.Env["POSTGRES_PASSWORD"]; has { t.Error("the store still takes its password from its environment") } if x.Env["POSTGRES_PASSWORD_FILE"] != storeSuperuserMount { t.Errorf("the store reads its password from %q", x.Env["POSTGRES_PASSWORD_FILE"]) } if !strings.Contains(strings.Join(x.Volumes, " "), StoreSuperuserFile+":"+storeSuperuserMount) { t.Errorf("the store does not mount %s: %v", StoreSuperuserFile, x.Volumes) } case *declaration.Action: if x.ID != "broker-admin" { continue } action = true if x.In != "mesh-broker" || !strings.Contains(strings.Join(x.Command, " "), "change_password "+BrokerAdminUser+" '"+creds.Broker+"'") { t.Errorf("the broker-admin action is %v in %q", x.Command, x.In) } } } if !store || !action { t.Fatalf("store=%v action=%v", store, action) } // The order matters: the broker's password changes after it answers and before the control // plane, which dials it with the new one, is raised. var readyAt, adminAt, controlAt int for i, res := range r.Declaration.Resources { switch res.Identity() { case "broker-ready": readyAt = i case "broker-admin": adminAt = i case "control-plane": controlAt = i } } if !(readyAt < adminAt && adminAt < controlAt) { t.Errorf("order ready=%d admin=%d control=%d", readyAt, adminAt, controlAt) } } // A template that no longer says what this expects is refused, not half-rewritten. func TestATemplateWithoutTheKnownCredentialsIsRefused(t *testing.T) { template, err := os.ReadFile("../../examples/foundation-first-node.lock") if err != nil { t.Skip("no example bundle beside this checkout") } changed := strings.Replace(string(template), `"POSTGRES_PASSWORD": "bootstrap"`, `"POSTGRES_PASSWORD": "other"`, 1) r, err := Rewrite([]byte(changed), "sha256:"+strings.Repeat("ab", 32)) if err != nil { t.Fatal(err) } if _, err := RewriteRoot(&r, RootCredentials{Store: "x", Broker: "y"}); err == nil { t.Fatal("a template with an unknown store password was rewritten") } } // Made once and kept: a second run reads the same value; a dry run writes nothing. func TestRootSecretsAreKeptAcrossRuns(t *testing.T) { dir := t.TempDir() path := dir + "/superuser.secret" first, made, err := keptOrMade(path, false) if err != nil || !made || len(first) != 40 { t.Fatalf("first: %q made=%v err=%v", first, made, err) } if info, _ := os.Stat(path); info.Mode().Perm() != 0o600 { t.Errorf("mode %v", info.Mode().Perm()) } second, made, err := keptOrMade(path, false) if err != nil || made || second != first { t.Fatalf("second: %q made=%v err=%v", second, made, err) } dry := dir + "/dry.secret" if _, made, err := keptOrMade(dry, true); err != nil || !made { t.Fatal(err) } if _, err := os.Stat(dry); err == nil { t.Fatal("a dry run wrote a secret") } }