//go:build linux && rootrunner package host import ( "encoding/json" "os" "os/exec" "path/filepath" "reflect" "testing" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/rootstate" "golang.org/x/sys/unix" ) func TestPlan14GenerationPayloadPhysicalRootRunner(t *testing.T) { const childKey = "PLAN14_E28_PAYLOAD_CHILD" dir := os.Getenv(childKey) if dir == "" { dir = generationRootRunnerDir(t) cmd := exec.CommandContext(t.Context(), "unshare", "--mount", "--pid", "--fork", "--mount-proc", os.Args[0], "-test.run=^TestPlan14GenerationPayloadPhysicalRootRunner$", "-test.v", "-test.timeout=2m") cmd.Dir = dir cmd.Env = append(os.Environ(), childKey+"="+dir) if out, err := cmd.CombinedOutput(); err != nil { t.Fatalf("isolated payload census: %v\n%s", err, out) } else { t.Logf("%s", out) } return } if os.Geteuid() != 0 { t.Fatal("designated runner must execute as root") } t.Setenv("CARLOS_OPT_DIR", dir) t.Setenv("CARLOS_DATA_DIR", filepath.Join(dir, "data")) for _, path := range []string{filepath.Join(dir, "versions"), dataDir()} { if err := os.Mkdir(path, 0755); err != nil { t.Fatal(err) } } pins, certificate := generationNamespacePinFixture(t, dir) path := filepath.Join(dataDir(), "retained") if err := os.WriteFile(path, []byte("predecessor payload"), 0600); err != nil { t.Fatal(err) } var st unix.Stat_t if err := unix.Stat(path, &st); err != nil { t.Fatal(err) } // An empty current installation still carries its predecessor's payload. // These are root-written fixtures, not migration or rebind authorization. in := hostguard.RootInstallation{Host: "retained.example", Incarnation: "inc", Stamp: 5, Account: "bab", App: "shop", Box: "runner", Enrollment: "en", Migration: 1, InventoryRevision: 1, Empty: true} in.Record = provision.RecordKey(in.Account, in.App, in.Host) fences, err := rootstate.Open(filepath.Join(dir, "fences"), false, false) if err != nil { t.Fatal(err) } raw, err := json.Marshal(map[string]any{ "Schema": 1, "Installation": in, "Retired": []hostguard.Captured{{Incarnation: in.Incarnation, Stamp: 2}}, "State": map[string]any{"v": 1, "host": in.Host, "incarnation": in.Incarnation, "active_stamp": 5, "retired_stamps": []uint64{2}}, }) if err == nil { err = fences.Write(in.Host+".json", raw) } fences.Close() if err != nil { t.Fatal(err) } journalPath := filepath.Join(dir, "journals.db") journal, err := hostbox.OpenLaunchJournal(journalPath) if err != nil { t.Fatal(err) } service, err := hostbox.NewJournalService(journal) if err != nil { journal.Close() t.Fatal(err) } for _, stamp := range []uint64{2, 5} { payload := hostbox.StopInventory{Complete: true, Host: in.Host, Incarnation: in.Incarnation, InstallationStamp: stamp, Box: in.Box, Enrollment: in.Enrollment, DirectoryRevision: "1"} if stamp == 2 { payload.Entries = []hostbox.StopInventoryEntry{{Identity: inventoryIdentityFixture(t, dataDir(), "retained"), Kind: "path", ArtifactID: "retained", ManagedRoot: "data", RelativePath: "retained", Ownership: "owner", Materialization: "materialized", Device: uint64(st.Dev), Inode: st.Ino, Type: unix.S_IFREG}} } if err := service.Inventory.WriteStopInventory(t.Context(), payload); err != nil { journal.Close() t.Fatal(err) } } journal.Close() read := func() generationInventory { t.Helper() v, err := generationInventoryAt(t.Context(), filepath.Join(dir, "census"), filepath.Join(dir, "fences"), journalPath) if err != nil { t.Fatal(err) } return v } before := read() accept := func() { t.Helper() if got, err := pins.StagedPins(t.Context(), dir); err != nil || len(got) != 0 || !certificate().Complete { t.Fatalf("exact retained payload: %v %v", got, err) } } accept() for _, attack := range []string{"missing", "replacement", "symlink", "bind"} { t.Run(attack, func(t *testing.T) { initial := certificate() if attack == "bind" { other := filepath.Join(dataDir(), "other") if err := os.WriteFile(other, []byte("different inode"), 0600); err != nil { t.Fatal(err) } if err := unix.Mount(other, path, "", unix.MS_BIND, ""); err != nil { t.Fatal(err) } t.Cleanup(func() { if err := unix.Unmount(path, 0); err != nil { t.Error(err) } }) } else { if err := os.Rename(path, path+".old"); err != nil { t.Fatal(err) } t.Cleanup(func() { if attack != "missing" { if err := os.Remove(path); err != nil { t.Error(err) } } if err := os.Rename(path+".old", path); err != nil { t.Error(err) } }) if attack == "replacement" { err = os.WriteFile(path, []byte("replacement"), 0600) } else if attack == "symlink" { err = os.Symlink(path+".old", path) } if err != nil { t.Fatal(err) } } if got, err := pins.StagedPins(t.Context(), dir); err == nil || got != nil { t.Fatalf("changed predecessor payload certified: %v %v", got, err) } if c := certificate(); c.Complete || c.Revision <= initial.Revision { t.Fatal("stale completeness survived physical refusal") } if !reflect.DeepEqual(before, read()) { t.Fatal("physical refusal changed retained authority") } }) denied := certificate() accept() if certificate().Revision <= denied.Revision { t.Fatal("restored payload reused an old certificate") } } for _, kind := range []string{"route", "timer"} { t.Run("unfinished-"+kind, func(t *testing.T) { journal, err := hostbox.OpenLaunchJournal(journalPath) if err != nil { t.Fatal(err) } defer journal.Close() service, err := hostbox.NewJournalService(journal) if err != nil { t.Fatal(err) } payload := before.Payloads[0].Inventory if payload.InstallationStamp != 2 { t.Fatal("expected retained predecessor") } entry := hostbox.StopInventoryEntry{Kind: kind, ArtifactID: "registry-payload", ManagedRoot: "registry", RelativePath: in.Host, RowIdentity: "row", Ownership: "owner", Materialization: "materialized"} if kind == "timer" { entry.RelativePath += "/backup" } payload.Entries = append(append([]hostbox.StopInventoryEntry(nil), payload.Entries...), entry) write := func() { t.Helper() if err := service.Inventory.WriteStopInventory(t.Context(), payload); err != nil { t.Fatal(err) } } write() accept() initial := certificate() payload.Entries[len(payload.Entries)-1].Materialization = "intended" write() pending := read() if got, err := pins.StagedPins(t.Context(), dir); err == nil || got != nil { t.Fatalf("unfinished predecessor %s certified: %v %v", kind, got, err) } if c := certificate(); c.Complete || c.Revision <= initial.Revision { t.Fatal("unfinished registry payload retained certificate") } if !reflect.DeepEqual(pending, read()) { t.Fatal("refusal changed the inventory") } payload.Entries[len(payload.Entries)-1].Materialization = "materialized" write() accept() payload.Entries = payload.Entries[:len(payload.Entries)-1] write() }) } }