//go:build linux && rootrunner package host import ( "os" "path/filepath" "testing" "time" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/rollout" "github.com/carlosframework/platform/internal/rootstate" ) func TestPlan14SwapFailureHostileRootRunner(t *testing.T) { if os.Geteuid() != 0 { t.Fatal("root required") } dir, err := os.MkdirTemp("/var/lib", "carlos-e3-") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) s, err := rootstate.Open(dir, false, false) if err != nil { t.Fatal(err) } defer s.Close() b, err := rollout.InitializeRootFailures(s, "box/en/1", nil) if err != nil { t.Fatal(err) } rec := rollout.FailureRecord{Account: "bab", App: "km", Build: "v1", Host: "h.example.com", HeldDown: []string{"h.example.com"}} if _, err = b.Record(rec); err != nil { t.Fatal(err) } if err = os.Chmod(dir, 0777); err != nil { t.Fatal(err) } if bad, err := rootstate.Open(dir, false, false); err == nil { bad.Close() t.Fatal("hostile ancestor admitted") } if err = os.Chmod(dir, 0700); err != nil { t.Fatal(err) } if err = os.Chown(dir, 65534, 65534); err != nil { t.Fatal(err) } if bad, err := rootstate.Open(dir, false, false); err == nil { bad.Close() t.Fatal("foreign owner admitted") } if err = os.Chown(dir, 0, 0); err != nil { t.Fatal(err) } renamed := dir + "-retained" if err = os.Rename(dir, renamed); err != nil { t.Fatal(err) } defer os.RemoveAll(renamed) if err = os.Mkdir(dir, 0700); err != nil { t.Fatal(err) } b, err = rollout.OpenRootFailures(s, "box/en/1") if err != nil { t.Fatal(err) } testRootSettingsPass(t, b, "combined") if !b.HeldDown("bab", "km", "v1", rec.Host) { t.Fatal("rename dropped denial") } if _, err = os.Stat(filepath.Join(dir, "book.json")); !os.IsNotExist(err) { t.Fatal("replacement acquired authority", err) } seed, public, _ := release.GenerateKey() priv, _ := release.ParseSigningKey(seed, "test") pub, _ := release.ParsePublicKey(public, "test") wire, err := release.SignHostRetry(release.RootSigner(priv), "bab", "km", rec.Host, "v1", b.ID(), b.PublishedEpoch("bab", "km", "v1", rec.Host), time.Now()) if err != nil { t.Fatal(err) } failing := true fault := rootstate.WithWriteFault(s, func(name, stage string, op func() error) error { if stage == "parent-sync" && failing { return os.ErrPermission } return op() }) b, err = rollout.OpenRootFailures(fault, "box/en/1") if err == nil { t.Fatal("owed directory sync admitted") } failing = false b, err = rollout.OpenRootFailures(fault, "box/en/1") if err != nil { t.Fatal(err) } failing = true if err = b.VerifiedRetry(pub, wire, "bab", "km", rec.Host, time.Now()); err == nil { t.Fatal("visible retry reported durable") } if !b.SaveOwed() { t.Fatal("failure disappeared") } if _, err = rollout.OpenRootFailures(fault, "box/en/1"); err == nil { t.Fatal("retry granted while sync still fails") } failing = false b, err = rollout.OpenRootFailures(fault, "box/en/1") if err != nil { t.Fatal(err) } if b.HeldDown("bab", "km", "v1", rec.Host) { t.Fatal("durably recovered retry not applied") } } func TestPlan14SwapFailureNoCensusRootRunner(t *testing.T) { if os.Geteuid() != 0 { t.Fatal("root required") } for _, path := range []string{"/var/lib/carlos-claims/authority/execution/census.json", rollout.RootFailuresDir} { if _, err := os.Lstat(path); !os.IsNotExist(err) { t.Fatal("fixture requires absent authority", path, err) } } p, err := productionRootUpdates(t.TempDir())(t.Context(), nil, nil) if err != nil || p != nil { t.Fatal("no-census branch changed", p, err) } if _, err := os.Lstat(rollout.RootFailuresDir); !os.IsNotExist(err) { t.Fatal("no-census pass created authority", err) } TestUpdatesPassOptionReadsTheBookEveryPass(t) }