//go:build linux && rootrunner package host import ( "context" "errors" "fmt" "os" "os/exec" "path/filepath" "reflect" "strings" "syscall" "testing" "time" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/rootstate" ) func TestPlan14GenerationInstalledUnitRootRunner(t *testing.T) { dir := generationRootRunnerDir(t) pid1, err := os.ReadFile("/proc/1/comm") if err != nil || strings.TrimSpace(string(pid1)) != "systemd" { t.Fatal("designated runner requires systemd as PID 1") } for _, path := range []string{"/run/carlos-control", "/run/carlos", "/var/lib/carlos-claims", "/var/lib/carlos-release", rootReleasePrivate} { private := path == "/var/lib/carlos-claims" || path == rootReleasePrivate mode := os.FileMode(0755) if private { mode = 0700 } if err := os.Mkdir(path, mode); err == nil { t.Cleanup(func() { if err := os.Remove(path); err != nil { t.Error(err) } }) } else if !errors.Is(err, os.ErrExist) { t.Fatal(err) } state, err := rootstate.Open(path, false, !private) if err != nil { t.Fatal(err) } state.Close() } authority := filepath.Join(rootReleasePrivate, filepath.Base(dir)) state, err := rootstate.Open(authority, true, false) if err != nil { t.Fatal(err) } state.Close() t.Cleanup(func() { if err := os.RemoveAll(authority); err != nil { t.Error(err) } }) for _, name := range []string{"data", "opt"} { if err := os.Mkdir(filepath.Join(dir, name), 0755); err != nil { t.Fatal(err) } } binary, err := os.Executable() if err != nil { t.Fatal(err) } body, err := os.ReadFile(binary) if err != nil { t.Fatal(err) } installed := filepath.Join(dir, "runner.test") if err := os.WriteFile(installed, body, 0755); err != nil { t.Fatal(err) } raw, err := unitsFS.ReadFile("units/" + claimControlUnit) if err != nil { t.Fatal(err) } ctl := func(t *testing.T, args ...string) string { t.Helper() ctx, cancel := context.WithTimeout(t.Context(), 45*time.Second) defer cancel() out, err := exec.CommandContext(ctx, "systemctl", args...).CombinedOutput() if err != nil { t.Fatalf("systemctl %v: %v\n%s", args, err, out) } return strings.TrimSpace(string(out)) } for _, mode := range []string{"off", "on"} { t.Run(mode, func(t *testing.T) { unit := filepath.Base(dir) + "-" + mode + ".service" content := claimUnitContent(claimControlUnit, raw, claimUnitSettings{enabled: mode == "on", data: filepath.Join(dir, "data"), opt: filepath.Join(dir, "opt")}) var lines []string for _, line := range strings.Split(string(content), "\n") { if strings.HasPrefix(line, "Requires=") || strings.HasPrefix(line, "After=") || strings.HasPrefix(line, "ExecStart=") || strings.HasPrefix(line, "EnvironmentFile=") { continue } lines = append(lines, line) } content = []byte(strings.Join(lines, "\n") + fmt.Sprintf("\nType=oneshot\nTimeoutStartSec=45s\nExecStart=%s -test.run=^TestPlan14GenerationUnitPayload$ -test.v -test.timeout=30s\nEnvironment=PLAN14_GENERATION_UNIT_MODE=%s PLAN14_GENERATION_UNIT_STORE=%s\nIPAddressDeny=any\n", installed, mode, authority)) path := filepath.Join("/run/systemd/system", unit) file, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) if err != nil { t.Fatal(err) } t.Cleanup(func() { for _, args := range [][]string{{"stop", unit}, {"reset-failed", unit}} { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) out, err := exec.CommandContext(ctx, "systemctl", args...).CombinedOutput() cancel() if err != nil && args[0] == "stop" { t.Errorf("cleanup unit: %v %s", err, out) } } if err := os.Remove(path); err != nil { t.Error(err) } ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() if out, err := exec.CommandContext(ctx, "systemctl", "daemon-reload").CombinedOutput(); err != nil { t.Errorf("cleanup reload: %v %s", err, out) } }) _, writeErr := file.Write(content) if err := errors.Join(writeErr, file.Close()); err != nil { t.Fatal(err) } ctl(t, "daemon-reload") ctl(t, "start", unit) if ctl(t, "show", unit, "--property=Result", "--value") != "success" || ctl(t, "show", unit, "--property=ExecMainStatus", "--value") != "0" { t.Fatal("installed unit did not complete its payload") } }) } } func TestPlan14GenerationUnitPayload(t *testing.T) { mode, path := os.Getenv("PLAN14_GENERATION_UNIT_MODE"), os.Getenv("PLAN14_GENERATION_UNIT_STORE") if path == "" || (mode != "off" && mode != "on" && mode != "service") { t.Fatal("invoke through TestPlan14GenerationInstalledUnitRootRunner") } if mode == "service" { if os.Geteuid() != 65534 { t.Fatal("service witness did not drop credentials") } if _, err := os.ReadDir(path); !errors.Is(err, os.ErrPermission) { t.Fatalf("service listed private generations: %v", err) } if err := os.WriteFile(filepath.Join(path, "forged.json"), nil, 0600); !errors.Is(err, os.ErrPermission) { t.Fatalf("service wrote private generations: %v", err) } return } if os.Geteuid() != 0 { t.Fatal("installed control unit must run as root") } if mode == "off" { if err := os.WriteFile(filepath.Join(path, "unexpected.json"), nil, 0600); !errors.Is(err, syscall.EROFS) { t.Fatalf("claims-off private generation write: %v", err) } return } state, err := rootstate.Open(path, false, false) if err != nil { t.Fatal(err) } defer state.Close() boot, err := (generationKernel{proc: "/proc"}).boot() if err != nil { t.Fatal(err) } start, err := claimConstructionProcessStart() if err != nil { t.Fatal(err) } binary, err := os.Executable() if err != nil { t.Fatal(err) } identity := func(path string) rootstate.FileIdentity { t.Helper() file, err := os.Open(path) if err != nil { t.Fatal(err) } defer file.Close() id, err := rootstate.Identity(file) if err != nil { t.Fatal(err) } return id } // Authorization and launch facts are fixtures. This tests installed access. binding := release.GenerationBinding{ Binding: release.HistoryBinding{Host: "unit.example", Account: "bab", App: "shop"}, Target: release.GenerationOwner{Host: "unit.example", Incarnation: "inc", Stamp: 1, Session: 1, Box: "runner", Enrollment: "en", BootID: boot, ControllerID: "controller"}, Installation: "fixture", Attempt: "main", Inputs: "fixture", Invocation: "fixture", Record: "fixture", RecordETag: "fixture", ControllerPID: os.Getpid(), ControllerStart: start, Version: "v1", Digest: strings.Repeat("a", 64), Path: binary, Tree: filepath.Dir(binary), Executable: identity(binary), TreeIdentity: identity(filepath.Dir(binary)), } g := &release.RootGenerations{Store: state} reserved, err := g.Reserve(binding) if err != nil { t.Fatal(err) } cmd := exec.CommandContext(t.Context(), binary, "-test.run=^TestPlan14GenerationUnitPayload$", "-test.v", "-test.timeout=10s") cmd.Dir = filepath.Dir(binary) cmd.Env = []string{"PLAN14_GENERATION_UNIT_MODE=service", "PLAN14_GENERATION_UNIT_STORE=" + path} cmd.SysProcAttr = &syscall.SysProcAttr{Credential: &syscall.Credential{Uid: 65534, Gid: 65534, Groups: []uint32{65534}}} if out, err := cmd.CombinedOutput(); err != nil { t.Fatalf("service-UID witness: %v\n%s", err, out) } state.Close() state, err = rootstate.Open(path, false, false) if err != nil { t.Fatal(err) } defer state.Close() got, err := (&release.RootGenerations{Store: state}).Read(binding) if err != nil || !reflect.DeepEqual(got, reserved) { t.Fatalf("reopened generation changed: %+v %v", got, err) } }