//go:build linux && rootrunner package host import ( "context" "crypto/ed25519" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "net" "net/http" "net/url" "os" "os/exec" "path/filepath" "strings" "testing" "time" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" execlease "github.com/carlosframework/platform/internal/hostexec" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/instancectl" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/registry" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/rollout" "github.com/carlosframework/platform/internal/rootstate" ) func plan14InstalledTenant() { if os.Getenv("PLAN14_TENANT_FAIL") == "1" { os.Exit(1) } socket := os.Getenv("PLAN14_TENANT_SOCKET") if socket == "" { os.Exit(2) } network := "unix" if addr := os.Getenv("PLAN14_TENANT_ADDR"); addr != "" { network, socket = "tcp", addr } if network == "unix" { // The payload owns its socket, like an ordinary Unix HTTP server. A // process killed at exit may leave that socket pathname behind. if st, err := os.Lstat(socket); err == nil { if st.Mode()&os.ModeSocket == 0 || os.Remove(socket) != nil { os.Exit(1) } } else if !os.IsNotExist(err) { os.Exit(1) } } ln, err := net.Listen(network, socket) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } if err := http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if target := r.URL.Query().Get("dial"); target != "" { conn, err := net.DialTimeout("tcp", target, 200*time.Millisecond) if err != nil { w.WriteHeader(http.StatusForbidden) return } conn.Close() w.WriteHeader(http.StatusNoContent) return } w.Write([]byte("running")) })); err != nil { os.Exit(1) } } func TestPlan14InstalledClaimLaunchRootRunner(t *testing.T) { plan14InstalledClaimLaunch(t, "") } func TestPlan14NamedClaimLaunchRootRunner(t *testing.T) { for _, unit := range []string{"plan14-named.service", "plan14-socket.service", "backing"} { t.Run(unit, func(t *testing.T) { plan14InstalledClaimLaunch(t, unit) }) } } func TestPlan14RootRuntimeInstalledCompletion(t *testing.T) { plan14InstalledClaimLaunch(t, "", true) } func TestPlan14RootRuntimeInstalledFailure(t *testing.T) { plan14InstalledClaimLaunch(t, "", false) } // This witness permits loopback HTTP between the runner and its named TCP // payloads. The payloads make no remote network requests. func TestPlan14BoundRouteProgramRootRunner(t *testing.T) { for _, unit := range []string{"tcp", "socket-program", "backing-program"} { t.Run(unit, func(t *testing.T) { plan14InstalledClaimLaunch(t, unit) }) } } func TestPlan14MaterializedBoundRouteRootRunner(t *testing.T) { for _, unit := range []string{"tcp", "socket-program", "backing-program"} { t.Run(unit, func(t *testing.T) { plan14InstalledClaimLaunch(t, "materialized:"+unit) }) } } func TestPlan14MigratedBoundRouteRootRunner(t *testing.T) { for _, unit := range []string{"tcp", "socket-program", "backing-program"} { t.Run(unit, func(t *testing.T) { plan14InstalledClaimLaunch(t, "migrated:materialized:"+unit) }) } } func TestPlan14BoundNetworkEdgeRootRunner(t *testing.T) { for _, unit := range []string{"tcp", "socket-program", "backing-program"} { t.Run(unit, func(t *testing.T) { plan14InstalledClaimLaunch(t, "isolation:materialized:"+unit) }) } } func TestPlan14ConsoleLibraryRootRunner(t *testing.T) { plan14InstalledClaimLaunch(t, "console:materialized:tcp") } func TestPlan14NamedStartupDefersReplacementRootRunner(t *testing.T) { for _, mode := range []string{"tcp", "socket-program", "backing-program"} { t.Run(mode, func(t *testing.T) { plan14InstalledClaimLaunch(t, "defer:migrated:materialized:"+mode) }) } } func TestPlan14OrdinaryUnitAuthorizationRootRunner(t *testing.T) { plan14InstalledClaimLaunch(t, "ordinary-verified") } func plan14InstalledClaimLaunch(t *testing.T, boundUnit string, runtimeUpdate ...bool) { verifiedOrdinary := boundUnit == "ordinary-verified" if verifiedOrdinary { boundUnit = "" } deferReplacement := strings.HasPrefix(boundUnit, "defer:") boundUnit = strings.TrimPrefix(boundUnit, "defer:") realConsole := strings.HasPrefix(boundUnit, "console:") boundUnit = strings.TrimPrefix(boundUnit, "console:") isolation := strings.HasPrefix(boundUnit, "isolation:") boundUnit = strings.TrimPrefix(boundUnit, "isolation:") startup := strings.HasPrefix(boundUnit, "startup:") boundUnit = strings.TrimPrefix(boundUnit, "startup:") combined := strings.HasPrefix(boundUnit, "combined:") boundUnit = strings.TrimPrefix(boundUnit, "combined:") migrated := strings.HasPrefix(boundUnit, "migrated:") boundUnit = strings.TrimPrefix(boundUnit, "migrated:") materialized := strings.HasPrefix(boundUnit, "materialized:") boundUnit = strings.TrimPrefix(boundUnit, "materialized:") programMode := boundUnit == "tcp" || boundUnit == "socket-program" || boundUnit == "backing-program" tcpMode := boundUnit == "tcp" || boundUnit == "backing-program" if boundUnit == "tcp" { boundUnit = "plan14-tcp.service" } if boundUnit == "socket-program" { boundUnit = "plan14-socket.service" } if boundUnit == "backing-program" { boundUnit = "backing" } if dir := os.Getenv("PLAN14_INSTALLED_RETIRE_CHILD"); dir != "" { plan14InstalledRetire(t, dir) return } dir := generationRootRunnerDir(t) t.Setenv("TMPDIR", dir) uid, gid := generationRunnerRegistryUser(t) ctl := func(ctx context.Context, args ...string) (string, error) { out, err := exec.CommandContext(ctx, "systemctl", args...).CombinedOutput() return strings.TrimSpace(string(out)), err } // These fixture paths must be absent. Install only this runner's units and // filesystem roots; retain the deployed unit's hardening and launch entrypoint. for _, p := range []string{"/data", "/opt/carlos", "/run/carlos", "/run/carlos-control", "/etc/carlos"} { if err := os.Mkdir(p, 0755); err != nil { t.Fatal("fixture root already exists", p, err) } p := p t.Cleanup(func() { if err := os.RemoveAll(p); err != nil { t.Error(err) } }) } binary, err := os.Executable() if err != nil { t.Fatal(err) } body, err := os.ReadFile(binary) if err != nil { t.Fatal(err) } if err := os.WriteFile(instancectl.SystemBinary, body, 0755); err != nil { t.Fatal(err) } f, _, rec := generationLaunchFixture(t) t.Setenv("CARLOS_DATA_DIR", "/data") t.Setenv("CARLOS_OPT_DIR", "/opt/carlos") host := f.target.Host var endpoint string if tcpMode { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } endpoint = ln.Addr().String() ln.Close() } socketEndpoint := filepath.Join(provision.InstanceRunDir(host), "app.sock") if materialized && !tcpMode { socketEndpoint = "/run/carlos/woodstar-test.sock" } program := claimUnitProgram{Schema: 1, Host: host, Args: []string{"plan14-tenant"}, Env: []string{"PLAN14_TENANT_SOCKET=" + socketEndpoint}} if tcpMode { program.Env = append(program.Env, "PLAN14_TENANT_ADDR="+endpoint) } var checkService func(*http.Client) if realConsole { program, checkService = plan14ConsoleProgram(t, host, endpoint) } healthURL := "http://tenant/" programRaw, _ := json.Marshal(program) programSum := sha256.Sum256(programRaw) if boundUnit == "backing" { boundUnit = "carlos-app@" + host + ".service" } for _, p := range []string{provision.InstanceDir("/data", host), provision.InstanceRunDir(host), "/run/carlos-control/exe", "/run/carlos/logs"} { if err := os.MkdirAll(p, 0755); err != nil { t.Fatal(err) } } for _, p := range []string{provision.InstanceDir("/data", host), provision.InstanceRunDir(host)} { if err := os.Chown(p, uid, gid); err != nil { t.Fatal(err) } } if materialized || verifiedOrdinary { plan14MaterializedDependencies(t, ctl) } if verifiedOrdinary { path := "/etc/systemd/system/carlos-edge.service" if _, err := os.Lstat(path); !os.IsNotExist(err) { t.Fatal("existing edge", err) } if err := os.WriteFile(path, []byte("[Service]\nType=oneshot\nExecStart=/usr/bin/true\nRemainAfterExit=yes\n"), 0644); err != nil { t.Fatal(err) } t.Cleanup(func() { ctl(context.Background(), "stop", "carlos-edge.service") os.Remove(path) ctl(context.Background(), "daemon-reload") }) } template, err := unitsFS.ReadFile("units/carlos-instance@.service") if err != nil { t.Fatal(err) } var lines []string for _, line := range strings.Split(string(template), "\n") { key, _, _ := strings.Cut(line, "=") switch key { case "BindsTo", "Requires", "After": continue case "StandardOutput": line = "StandardOutput=journal" case "ExecStart": if programMode { line = "ExecStart=/usr/bin/false" } } lines = append(lines, line) } if tcpMode { lines = append(lines, "[Service]", "IPAddressAllow=localhost") } unit := instancectl.UnitName(instancectl.RoleRun, host) unitPath := "/run/systemd/system/carlos-instance@.service" if verifiedOrdinary { unitPath = "/etc/systemd/system/carlos-instance@.service" lines = strings.Split(string(template), "\n") } if boundUnit != "" { unit = boundUnit unitPath = filepath.Join("/run/systemd/system", unit) for i := range lines { lines[i] = strings.ReplaceAll(strings.ReplaceAll(lines[i], "%i", provision.InstanceID(host)), "%I", provision.InstanceID(host)) } f.req.Reservation.Group.RelativePath = instancectl.BoundClaimUnitControlGroup(host, unit) } if materialized { unitPath = filepath.Join("/etc/systemd/system", unit) } legacyDefinition := []byte("[Service]\nType=simple\nUser=carlos\nKillMode=control-group\nExecStart=/bin/sh -c 'sleep infinity & wait'\n") if migrated && !tcpMode { if err := os.Chown("/run/carlos", uid, gid); err != nil { t.Fatal(err) } legacyDefinition = []byte("[Service]\nType=simple\nUser=carlos\nKillMode=control-group\nEnvironment=PLAN14_TENANT_SOCKET=" + socketEndpoint + "\nExecStart=/opt/carlos/carlos plan14-tenant\n") } if migrated { if err := os.WriteFile(unitPath, legacyDefinition, 0644); err != nil { t.Fatal(err) } } if !materialized { out, err := os.OpenFile(unitPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) if err != nil { t.Fatal(err) } if _, err := out.WriteString(strings.Join(lines, "\n")); err != nil { t.Fatal(err) } out.Close() } t.Cleanup(func() { ctl(context.Background(), "stop", unit) ctl(context.Background(), "stop", instancectl.SliceName(host)) ctl(context.Background(), "reset-failed", unit) os.Remove(unitPath) os.RemoveAll(filepath.Join("/run/systemd/system", unit+".d")) os.RemoveAll(filepath.Join("/run/systemd/system", instancectl.SliceName(host)+".d")) ctl(context.Background(), "daemon-reload") }) if out, err := ctl(t.Context(), "daemon-reload"); err != nil { t.Fatal(out, err) } if migrated { if out, err := ctl(t.Context(), "start", unit); err != nil { t.Fatal(out, err) } if !tcpMode { deadline := time.Now().Add(5 * time.Second) for { conn, err := net.DialTimeout("unix", socketEndpoint, time.Second) if err == nil { conn.Close() break } if time.Now().After(deadline) { t.Fatal("legacy socket did not serve", err) } time.Sleep(20 * time.Millisecond) } } } // Use a real controller lifetime, which must be joined before exact release. controller := exec.Command("/bin/sleep", "240") if err := controller.Start(); err != nil { t.Fatal(err) } joined := false join := func() { if !joined { controller.Process.Kill() controller.Wait() joined = true } } t.Cleanup(join) f.peer.PID = controller.Process.Pid f.peer.ProcessStart, err = claimLaunchProcessStart("/proc", f.peer.PID) if err != nil { t.Fatal(err) } launch, err := f.journal.Read(t.Context(), f.target) if err != nil { t.Fatal(err) } boot, err := os.ReadFile("/proc/sys/kernel/random/boot_id") if err != nil { t.Fatal(err) } f.target.BootID, f.peer.BootID = strings.TrimSpace(string(boot)), strings.TrimSpace(string(boot)) f.req.Reservation.Target = f.target launch.Target = f.target f.claims.set(func(c *hostclaim.Claim) { c.Holder.WakeRetry.Attempt.BootID = f.peer.BootID }) launch.ControllerPID, launch.ProcessStart = f.peer.PID, f.peer.ProcessStart // Open a fresh journal with the controller identity production will check. journal, err := hostbox.OpenLaunchJournal(filepath.Join(dir, "launch.db")) if err != nil { t.Fatal(err) } t.Cleanup(func() { journal.Close() }) if err := journal.Prelaunch(t.Context(), launch); err != nil { t.Fatal(err) } f.journal, f.root.Journal = journal, journal state, err := rootstate.Open(filepath.Join(dir, "fences"), true, false) if err != nil { t.Fatal(err) } defer state.Close() old, err := f.root.Guard.Root.Installation(t.Context(), host, hostguard.Captured{Incarnation: f.target.Incarnation, Stamp: f.target.Stamp}) if err != nil { t.Fatal(err) } f.root.Guard.Root = hostguard.NewRootAuthority(state) if err := f.root.Guard.EstablishRoot(t.Context(), old, func(*hostclaim.Claim) error { return nil }); err != nil { t.Fatal(err) } if boundUnit != "" { bindings, err := rootstate.Open(filepath.Join(dir, "unit-bindings"), true, false) if err != nil { t.Fatal(err) } defer bindings.Close() shape := claimUnitShape{Host: host, Account: old.Account, App: old.App, Kind: "instance", Unit: boundUnit, Socket: provision.NestedSocket(host), DBPath: provision.NestedDBPath("/data", host)} if strings.HasPrefix(boundUnit, "carlos-app@") { shape.Backing = "unit" } if migrated { shape.PredecessorDigest = claimUnitDigest(legacyDefinition) } if programMode { shape.Program = hex.EncodeToString(programSum[:]) if materialized && !tcpMode { shape.Socket = socketEndpoint } if tcpMode { shape.Addr, shape.Socket = endpoint, "" } if err := os.MkdirAll("/etc/carlos/claim-units", 0700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join("/etc/carlos/claim-units", host+".json"), programRaw, 0600); err != nil { t.Fatal(err) } programPath := filepath.Join("/etc/carlos/claim-units", host+".json") if err := os.Chmod(programPath, 0666); err != nil { t.Fatal(err) } if _, err := readClaimUnitProgram(shape); err == nil { t.Fatal("service-writable program accepted") } if err := os.Chmod(programPath, 0600); err != nil { t.Fatal(err) } f.root.unitProgram = readClaimUnitProgram } f.root.unitBindings = &claimUnitBindings{state: bindings, installation: func(context.Context, string) (hostguard.RootInstallation, error) { return old, nil }, canonical: func(context.Context, string) (claimUnitShape, error) { return shape, nil }} if _, err := f.root.unitBindings.establish(t.Context(), host); err != nil { t.Fatal(err) } } f.root.Clock = execlease.SystemClock{} f.root.DataRoot = "/data" f.root.ReadyBudget = 10 * time.Second f.root.ReadyPoll = 20 * time.Millisecond f.root.Units = instancectl.ClaimUnits{BoundUnit: boundUnit, BoundHost: func() string { if boundUnit != "" { return host } return "" }(), Systemctl: ctl, CgroupRoot: "/sys/fs/cgroup", ExeRoot: "/run/carlos-control/exe", UnitRoot: "/run/systemd/system", DataRoot: "/data", WaitBudget: 20 * time.Second} if verifiedOrdinary { oldOpen, oldMaterial, oldShapes := openClaimUnitBindings, openClaimUnitMaterializations, productionClaimUnitShapes defer func() { openClaimUnitBindings, openClaimUnitMaterializations, productionClaimUnitShapes = oldOpen, oldMaterial, oldShapes }() openClaimUnitBindings = func() (rootstate.Store, error) { return rootstate.Open(filepath.Join(dir, "ordinary-bindings"), true, false) } openClaimUnitMaterializations = func() (rootstate.Store, error) { return rootstate.Open(filepath.Join(dir, "ordinary-materials"), true, false) } productionClaimUnitShapes = func() (map[string]claimUnitShape, error) { return map[string]claimUnitShape{}, nil } wrapped := ClaimRootWithLaunch{Launch: f.root} wrapped.Root.Stop.Coordinator.Kernel = claimUnitStopKernel{Units: f.root.Units} assembly := &ClaimAssembly{} if err := bindProductionClaimUnits(t.Context(), &wrapped, assembly); err != nil { t.Fatal(err) } t.Cleanup(func() { assembly.Close(context.Background()) }) dispatch := f.root.Units.(claimUnitDispatch) plan14BoundOverrideRefusals(t, dispatch, host, unit, ctl) if _, err := dispatch.forStart(t.Context(), host); err != nil { t.Fatalf("ordinary authorized configuration: %#v", err) } } var material *claimUnitMaterializer if materialized { ms, err := rootstate.Open(filepath.Join(dir, "unit-materializations"), true, false) if err != nil { t.Fatal(err) } defer ms.Close() files := claimUnitFiles{dir: "/etc/systemd/system"} material = &claimUnitMaterializer{idle: func(host string) error { return f.root.Units.Gone(t.Context(), host, "") }, state: ms, read: files.read, create: files.create, replace: files.replace} binding, err := f.root.unitBindings.read(host) if err != nil { t.Fatal(err) } if migrated { kernel := claimPredecessorKernel{ctl: ctl, group: claimPredecessorGroup, boot: claimUnitBoot} material.predecessor, material.stopPredecessor = kernel.capture, kernel.stop material.socketCapture, material.socketPark = captureProductionClaimUnitSocket, parkProductionClaimUnitSocket if !tcpMode { predecessor, err := kernel.capture(t.Context(), binding) if err != nil { t.Fatal(err) } predecessor.Group = "system.slice/unrelated.service" if _, err := captureProductionClaimUnitSocket(t.Context(), binding.Shape, predecessor); err == nil { t.Fatal("socket accepted a foreign predecessor cgroup") } } } if deferReplacement { plan14ProductionStartupDefersReplacement(t, f.root, material, binding, ctl) return } if err := material.install(t.Context(), binding); err != nil { debug, _ := ctl(t.Context(), "show", unit, "-p", "ActiveState,InvocationID,ControlGroup,FragmentPath,DropInPaths") t.Fatalf("migration/materialization: %v; unit: %s", err, debug) } if err := projectClaimBoundSocket(binding.Shape); err != nil { t.Fatal(err) } if _, err := ctl(t.Context(), "daemon-reload"); err != nil { t.Fatal(err) } } if programMode { base := f.root.Units.(instancectl.ClaimUnits) base.BoundHost, base.BoundUnit = "", "" binding, err := f.root.unitBindings.read(host) if err != nil { t.Fatal(err) } f.root.Units = claimUnitDispatch{base: base, bindings: f.root.unitBindings, shapes: func() (map[string]claimUnitShape, error) { return map[string]claimUnitShape{host: binding.Shape}, nil }, definition: func(s claimUnitShape) error { if material != nil { if err := material.verify(binding); err != nil { return err } if err := claimEffectiveUnit(t.Context(), s, ctl); err != nil { t.Logf("effective unit refused: %#v", err) return err } return nil } // Dependencies and output are runner adapters; all isolation settings // and the permanent no-reservation refusal remain in the unit. raw, err := os.ReadFile(unitPath) if err != nil { return err } if string(raw) != strings.Join(lines, "\n") { return os.ErrPermission } return nil }} } if materialized { plan14BoundOverrideRefusals(t, f.root.Units.(claimUnitDispatch), host, unit, ctl) f.root.boundReady = func(ctx context.Context, host string, started instancectl.ClaimUnitStarted) error { b, err := f.root.unitBindings.check(ctx, host, nil) if err != nil { return err } return claimBoundUnitReady(ctx, started, b.Shape) } } f.root.Kernel = linuxClaimLaunchKernel{OptRoot: "/opt/carlos", CgroupRoot: "/sys/fs/cgroup", ProcRoot: "/proc"} store := release.NewDirStore(filepath.Join(dir, "deployment")) rec.Schema = 1 plan14PutRecord(t, store, rec) pub, priv, err := ed25519.GenerateKey(nil) if err != nil { t.Fatal(err) } runnerBody := body for _, v := range []string{"v1", "v3"} { if err := release.StageArtifact("/opt/carlos", rec.Account, v, rec.App, body); err != nil { t.Fatal(err) } m := release.Manifest{App: rec.App, Version: v, Kind: "binary", Artifacts: map[string]release.Artifact{release.AdoptPlatform(): {}}} if err := release.Ship(t.Context(), store, rec.Account, m, map[string][]byte{release.AdoptPlatform(): body}); err != nil { t.Fatal(err) } if err := release.Promote(t.Context(), store, priv, rec.Account, rec.App, "canary/console", v); err != nil { t.Fatal(err) } } path := release.VersionPath("/opt/carlos", rec.Account, "v1", rec.App) file, err := rootstate.OpenTrustedFile(path) if err != nil { t.Fatal(err) } id, err := rootstate.Identity(file) file.Close() if err != nil { t.Fatal(err) } digest := sha256.Sum256(body) f.req.Executable = path f.req.Reservation.Executable = hostbox.LaunchFile{Device: id.Device, Inode: id.Inode} f.req.Spec.Executable = path f.req.Spec.Digest = hex.EncodeToString(digest[:]) f.req.Spec.Args = []string{"--socket", filepath.Join(provision.InstanceRunDir(host), "app.sock"), "--db", provision.NestedDBPath("/data", host)} f.req.Spec.Env = []string{"CARLOS_ADMIN_TOKEN=test", "PLAN14_TENANT_SOCKET=" + filepath.Join(provision.InstanceRunDir(host), "app.sock")} if programMode { f.req.Spec.Args, f.req.Spec.Env = program.Args, program.Env f.req.Spec.Identity.Config = hex.EncodeToString(programSum[:]) if materialized { b, err := f.root.unitBindings.read(host) if err != nil { t.Fatal(err) } route := b.Shape.route() out, err := f.root.Serve(t.Context(), f.peer, hostbox.RoleLaunchRequest{V: hostbox.RoleLaunchV, Op: hostbox.RoleLaunchInputs, Route: &route}) if err != nil || out.Inputs == nil { t.Fatal("bound admission inputs", err) } f.req.Spec.Args, f.req.Spec.Env = out.Inputs.Args, out.Inputs.Env } } if len(runtimeUpdate) > 0 && !runtimeUpdate[0] { f.req.Spec.Env = append(f.req.Spec.Env, "PLAN14_TENANT_FAIL=1") f.root.ReadyBudget = 100 * time.Millisecond } inputs, err := f.req.Spec.Hash() if err != nil { t.Fatal(err) } f.req.Reservation.Inputs = inputs f.claims.set(func(c *hostclaim.Claim) { c.Holder.WakeRetry.Attempt.LaunchInputs = inputs }) f.root.generations = &rootGenerationSource{store: store, optDir: "/opt/carlos", pub: func() (ed25519.PublicKey, error) { return pub, nil }, open: func() (rootstate.Store, error) { return rootstate.Open(filepath.Join(dir, "generations"), true, false) }, openFile: rootstate.OpenTrustedFile, persistent: rootstate.PersistentIdentity} var updateSaved rootUpdateRecord var runtimeBook *rollout.RootFailures if len(runtimeUpdate) > 0 { state, err := rootstate.Open(filepath.Join(dir, "failures"), true, false) if err != nil { t.Fatal(err) } defer state.Close() book, err := rollout.InitializeRootFailures(state, "box/en/1", nil) if err != nil { t.Fatal(err) } updateSaved = rootUpdateRecord{Schema: 1, Installation: "fixture-installation", Route: registry.Route{Host: host, Account: rec.Account, App: rec.App, Socket: filepath.Join(provision.InstanceRunDir(host), "app.sock")}, Consumed: &release.Consumed{Resolved: &release.ResolvedRequest{ID: 7, Target: "v1"}}} runtimeBook = book source := rootUpdateSource{state: state, book: book, ready: func(rt registry.Route) error { return rootUpdateReady(t.Context(), rt) }} f.root.generations.beginUpdate = func(ctx context.Context, _ *rootGenerationSource, b release.GenerationBinding) (func(string, error, func() error) error, func() error, error) { finish, err := source.beginRuntime(&updateSaved, func() error { raw, _ := json.Marshal(updateSaved); return state.Write("settings.json", raw) }, b.Version, b.Digest, b.Attempt) return finish, func() error { return nil }, err } } lock := &release.ReleaseExclusion{Open: func() (rootstate.Store, error) { return rootstate.Open(filepath.Join(dir, "release"), true, false) }} checkSibling := func() {} if programMode { checkSibling = plan14BoundSibling(t, runnerBody, ctl, uid, gid, lock) checkSibling() } if err := f.req.Validate(); err != nil { t.Fatal("request", err) } if _, err := f.root.launchSpec(f.req); err != nil { t.Fatal("launch spec", err) } if err := f.root.Kernel.Verify(f.req.Executable, f.req.Reservation.Executable); err != nil { t.Fatal("kernel build", err) } var response hostbox.RoleLaunchResponse err = lock.With(t.Context(), func(ctx context.Context) error { var err error response, err = f.root.Serve(ctx, f.peer, f.req) return err }) if len(runtimeUpdate) > 0 && !runtimeUpdate[0] { if err == nil || runtimeBook == nil || !runtimeBook.HeldDown(rec.Account, rec.App, "v1", host) || updateSaved.Build.State == nil || !updateSaved.Build.State.CompletedFailed || updateSaved.Build.State.Completed != 7 { t.Fatal("installed failure lacked durable root refusal", err, updateSaved) } return } if err != nil { logs, _ := exec.Command("journalctl", "-u", unit, "--no-pager", "-n", "80").CombinedOutput() output, _ := os.ReadFile(provision.LogSocket(host, "run")) t.Fatalf("production launch: %v\n%s\noutput: %s", err, logs, output) } if len(runtimeUpdate) > 0 && (updateSaved.Build.State == nil || updateSaved.Build.State.Completed != 7 || updateSaved.Build.State.CompletedFailed || updateSaved.Served["v1"].Invocation != response.Grant.UnitInvocationID) { t.Fatal("root installed completion missing", updateSaved) } transport := &http.Transport{DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { if tcpMode { return (&net.Dialer{}).DialContext(ctx, "tcp", endpoint) } return (&net.Dialer{}).DialContext(ctx, "unix", socketEndpoint) }} defer transport.CloseIdleConnections() client := &http.Client{Transport: transport, Timeout: 3 * time.Second} resp, err := client.Get(healthURL) if err != nil { t.Fatal("installed tenant did not serve", err) } resp.Body.Close() transport.CloseIdleConnections() if resp.StatusCode != 200 { t.Fatal(resp.Status) } if checkService != nil { checkService(client) } if isolation || realConsole { if tcpMode { siblingTransport := &http.Transport{DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { return (&net.Dialer{}).DialContext(ctx, "unix", filepath.Join(provision.InstanceRunDir("sibling.example"), "app.sock")) }} siblingClient := &http.Client{Transport: siblingTransport, Timeout: 3 * time.Second} response, err := siblingClient.Get("http://sibling/?dial=" + url.QueryEscape(endpoint)) if err != nil { t.Fatal("sibling isolation probe", err) } response.Body.Close() siblingTransport.CloseIdleConnections() if response.StatusCode != http.StatusForbidden { t.Fatal("sibling reached bound TCP upstream", response.StatusCode) } } for _, name := range func() []string { if realConsole { return nil } return []string{"console", "sibling"} }() { listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } conn, err := net.DialTimeout("tcp", listener.Addr().String(), time.Second) if err != nil { t.Fatal("control listener unavailable", err) } conn.Close() for _, address := range []string{listener.Addr().String(), "[::ffff:127.0.0.1]:" + strings.Split(listener.Addr().String(), ":")[1]} { response, err := client.Get("http://tenant/?dial=" + url.QueryEscape(address)) if err != nil { t.Fatal(err) } response.Body.Close() if response.StatusCode != http.StatusForbidden { t.Fatal("bound tenant reached", name, address, response.StatusCode) } } listener.Close() } before, err := ctl(t.Context(), "show", "-p", "InvocationID", "--value", unit) if err != nil { t.Fatal(err) } if _, err := ctl(t.Context(), "restart", "carlos-edge.service"); err != nil { t.Fatal(err) } after, err := ctl(t.Context(), "show", "-p", "InvocationID", "--value", unit) if err != nil || before == "" || after != before { t.Fatal("edge restart changed bound invocation", before, after, err) } response, err := client.Get(healthURL) if err != nil { t.Fatal("edge restart stopped bound service", err) } response.Body.Close() if response.StatusCode != http.StatusOK { t.Fatal(response.Status) } } checkSibling() if materialized { b, err := f.root.unitBindings.read(host) if err != nil { t.Fatal(err) } unrelated := instancectl.ClaimUnitStarted{Unit: response.Grant.Unit, MainPID: response.Grant.PID, ControlGroup: instancectl.ClaimUnitControlGroup("unrelated.example")} if b.Shape.Socket != "" { unrelated.MainPID = os.Getpid() } if err := claimBoundUnitReady(t.Context(), unrelated, b.Shape); err == nil { t.Fatal("readiness accepted unrelated process or group") } } generations, err := rootstate.Open(filepath.Join(dir, "generations"), false, false) if err != nil { t.Fatal(err) } defer generations.Close() ledger := &release.RootGenerations{Store: generations} records, err := ledger.Reservations(t.Context()) if err != nil || len(records) != 1 { t.Fatal(records, err) } v1 := records[0] if v1.Process.PID != response.Grant.PID || v1.Process.Containment != 1 || v1.Process.TenantPID == 0 { t.Fatal("missing production process evidence", v1) } pending := v1.Binding pending.Version = "v3" pending.Attempt = "pending-v3" pending.Invocation = "pending-v3" pending.Path = release.VersionPath("/opt/carlos", rec.Account, "v3", rec.App) pending.Tree = filepath.Dir(pending.Path) for _, v := range []struct { path string id *rootstate.FileIdentity }{{pending.Path, &pending.Executable}, {pending.Tree, &pending.TreeIdentity}} { file, err := os.Open(v.path) if err != nil { t.Fatal(err) } *v.id, err = rootstate.Identity(file) file.Close() if err != nil { t.Fatal(err) } } if err := lock.With(t.Context(), func(ctx context.Context) error { _, err := ledger.Reserve(pending); return err }); err != nil { t.Fatal(err) } retire := generationRetirement{guard: f.root.Guard, journal: journal, kernel: productionGenerationAbsence} if err := lock.With(t.Context(), func(ctx context.Context) error { return retire.retire(ctx, ledger, v1) }); err != nil { t.Fatal(err) } still, err := ledger.Read(v1.Binding) if err != nil || still.Retirement != "" { t.Fatal("live pin retired", err) } stop := f.req stop.Op = hostbox.RoleLaunchStop stop.Spec, stop.Executable, stop.MemoryMax = nil, "", 0 if _, err := f.root.Serve(t.Context(), f.peer, stop); err != nil { t.Fatal("production stop", err) } if programMode { ctl(t.Context(), "start", unit) time.Sleep(100 * time.Millisecond) state, err := ctl(t.Context(), "show", "-p", "MainPID", "--value", unit) if err != nil || state != "0" { t.Fatal("unreserved restart executed", state, err) } ctl(t.Context(), "stop", unit) next := *f.req.Reservation next.ID, next.InvocationID = "attempt-2:main", "inv-2" f.req.Reservation = &next f.claims.set(func(c *hostclaim.Claim) { c.Holder.WakeRetry.AttemptsReserved++ c.Holder.WakeRetry.Attempt.ID = "attempt-2" c.Holder.WakeRetry.Attempt.InvocationID = "inv-2" }) var restarted hostbox.RoleLaunchResponse if err := lock.With(t.Context(), func(ctx context.Context) error { var err error restarted, err = f.root.Serve(ctx, f.peer, f.req) return err }); err != nil { logs, _ := exec.Command("journalctl", "-u", unit, "--no-pager", "-n", "100").CombinedOutput() state, _ := ctl(t.Context(), "show", "-p", "ActiveState,SubState,InvocationID,ControlGroup,ExecStart,Result", unit) t.Fatalf("reserved restart: %v\n%s\n%s", err, state, logs) } if restarted.Grant.UnitInvocationID == response.Grant.UnitInvocationID { t.Fatal("restart reused invocation") } resp, err := client.Get(healthURL) if err != nil { t.Fatal("restarted endpoint", err) } resp.Body.Close() transport.CloseIdleConnections() if resp.StatusCode != 200 { t.Fatal(resp.Status) } if checkService != nil { checkService(client) } stop.Reservation = &next if _, err := f.root.Serve(t.Context(), f.peer, stop); err != nil { t.Fatal("restarted stop", err) } } checkSibling() if err := journal.CloseWriters(t.Context(), f.target); err != nil { t.Fatal(err) } if err := lock.With(t.Context(), func(ctx context.Context) error { return retire.retire(ctx, ledger, v1) }); err == nil { t.Fatal("living controller released pin") } join() raw, _ := json.Marshal(f.target) if err := os.WriteFile(filepath.Join(dir, "target.json"), raw, 0600); err != nil { t.Fatal(err) } // Isolate the census from unrelated runner daemons, as the generation suite // does. The installed process and its controller were stopped and joined in // the outer namespace first; the child uses the retained root records. cmd := exec.CommandContext(t.Context(), "unshare", "--mount", "--pid", "--fork", "--mount-proc", binary, "-test.run=^TestPlan14InstalledClaimLaunchRootRunner$", "-test.v", "-test.timeout=2m") cmd.Env = append(os.Environ(), "PLAN14_INSTALLED_RETIRE_CHILD="+dir) if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("exact pin release: %v\n%s", err, output) } else { t.Logf("%s", output) } checkSibling() if material != nil { retireUnit := func() error { return retireClaimBoundUnit(t.Context(), f.target, f.root.unitBindings, *material, f.root.Units.Gone) } verifyCombined := func() {} if combined { retireUnit, verifyCombined = plan14CombinedBoundRetirement(t, dir, f, material, startup) } original, err := os.ReadFile(unitPath) if err != nil { t.Fatal(err) } saved := unitPath + ".plan14-original" if _, err := os.Lstat(saved); !os.IsNotExist(err) { t.Fatal("fixture predecessor exists", err) } if err := os.Rename(unitPath, saved); err != nil { t.Fatal(err) } t.Cleanup(func() { os.Remove(saved) }) files := claimUnitFiles{dir: filepath.Dir(unitPath)} if err := files.create(unit, original); err != nil { t.Fatal(err) } if err := retireUnit(); err == nil { t.Fatal("same-byte successor retired") } after, err := os.ReadFile(unitPath) if err != nil || string(after) != string(original) { t.Fatal("successor changed", err) } if err := os.Remove(unitPath); err != nil { t.Fatal(err) } if err := os.Rename(saved, unitPath); err != nil { t.Fatal(err) } if err := retireUnit(); err != nil { t.Fatal("unit retirement", err) } verifyCombined() if _, err := ctl(t.Context(), "daemon-reload"); err != nil { t.Fatal(err) } if _, err := ctl(t.Context(), "start", unit); err == nil { t.Fatal("retired unit accepted start") } binding, err := f.root.unitBindings.read(host) if err != nil || !binding.Retired { t.Fatal(binding, err) } for _, leaf := range []string{"launch.json", "permit.json", "exec.sock"} { if _, err := os.Lstat(filepath.Join("/run/carlos-control/exe/.claim", provision.InstanceID(host), leaf)); !os.IsNotExist(err) { t.Fatal("retired environment remains", leaf, err) } } // The root installation/claim source remains a fixture here. The durable // file transition and old-owner refusal use the real production adapters. f.root.unitBindings.handover = material.handover old.Stamp++ successor, err := f.root.unitBindings.establish(t.Context(), host) if err != nil { t.Fatal("successor binding", err) } if err := material.verify(successor); err != nil { t.Fatal("successor definition", err) } if err := retireClaimBoundUnit(t.Context(), f.target, f.root.unitBindings, *material, f.root.Units.Gone); err == nil { t.Fatal("old owner retired successor") } checkSibling() } } func plan14BoundOverrideRefusals(t *testing.T, dispatch claimUnitDispatch, host, unit string, ctl func(context.Context, ...string) (string, error)) { t.Helper() dir := filepath.Join("/etc/systemd/system", unit+".d") if err := os.Mkdir(dir, 0755); err != nil { t.Fatal(err) } t.Cleanup(func() { os.RemoveAll(dir) }) for _, attack := range []struct{ name, body string }{ {"90-operator.conf", "[Service]\nExecStart=\nExecStart=/usr/bin/true\n"}, {"90-user.conf", "[Service]\nUser=root\n"}, {"10-carlos-capabilities.conf", "[Service]\nCapabilityBoundingSet=CAP_SYS_ADMIN\n"}, {"90-mount.conf", "[Service]\nBindPaths=/etc:/tenant-secrets\n"}, } { path := filepath.Join(dir, attack.name) if err := os.WriteFile(path, []byte(attack.body), 0644); err != nil { t.Fatal(err) } if _, err := ctl(t.Context(), "daemon-reload"); err != nil { t.Fatal(err) } if _, err := dispatch.forStart(t.Context(), host); err == nil { t.Fatal("effective drop-in accepted", attack.name) } if err := os.Remove(path); err != nil { t.Fatal(err) } } if _, err := ctl(t.Context(), "daemon-reload"); err != nil { t.Fatal(err) } } func plan14InstalledRetire(t *testing.T, dir string) { state, err := rootstate.Open(filepath.Join(dir, "fences"), false, false) if err != nil { t.Fatal(err) } defer state.Close() journal, err := openGenerationLaunchInventory(filepath.Join(dir, "launch.db")) if err != nil { t.Fatal(err) } defer journal.Close() generations, err := rootstate.Open(filepath.Join(dir, "generations"), false, false) if err != nil { t.Fatal(err) } defer generations.Close() ledger := &release.RootGenerations{Store: generations} records, err := ledger.Reservations(t.Context()) if err != nil || len(records) < 2 { t.Fatal(records, err) } var v1s []release.GenerationReservation var v3 release.GenerationReservation for _, r := range records { if r.Binding.Version == "v1" { v1s = append(v1s, r) } else { v3 = r } } if err := os.Mkdir(filepath.Join(dir, "retire-locks"), 0700); err != nil { t.Fatal(err) } verifier := generationRetirement{guard: hostguard.Guard{DataDir: "/data", LockDir: filepath.Join(dir, "retire-locks"), Root: hostguard.NewRootAuthority(state)}, journal: journal, kernel: productionGenerationAbsence} lock := &release.ReleaseExclusion{Open: func() (rootstate.Store, error) { return rootstate.Open(filepath.Join(dir, "release"), false, false) }} for _, v1 := range v1s { if err := lock.With(t.Context(), func(ctx context.Context) error { return verifier.retire(ctx, ledger, v1) }); err != nil { t.Fatal(err) } released, err := ledger.Read(v1.Binding) if err != nil || released.Retirement == "" { t.Fatal("exact pin not released", released, err) } } pending, err := ledger.Read(v3.Binding) if err != nil || pending != v3 { t.Fatal("pending counterexample changed", pending, err) } }