//go:build linux && rootrunner package host import ( "bytes" "context" "crypto/sha256" "database/sql" "encoding/hex" "flag" "io" "net" "net/http" "os" "os/exec" "path/filepath" "strings" "testing" "time" "github.com/carlosframework/platform/internal/activator" "github.com/carlosframework/platform/internal/cgroup" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/registry" ) var seedTenantSocket = flag.String("socket", "", "") var seedTenantDB = flag.String("db", "", "") func init() { if os.Getenv("FILES_SEED_TENANT") == "1" { os.Args = append(os.Args, "-test.run=^TestRootFilesSeedTenantPayload$", "-test.timeout=30s") } } // This process is launched from the ordinary selected Build closure after C8. // The surrounding fixture still supplies role authority and stop evidence; // this witness does not stand in for the installed isolated role-launch test. func TestRootFilesSeedTenantPayload(t *testing.T) { if os.Getenv("FILES_SEED_TENANT") != "1" { t.Fatal("invoke through real-origin tenant witness") } for _, path := range strings.Split(os.Getenv("FILES_SEED_DENIED"), ":") { if path != "" { if body, err := os.ReadFile(path); err == nil { _ = os.WriteFile(os.Getenv("FILES_SEED_PAYLOAD")+".isolation-violation", []byte(path), 0600) t.Fatalf("tenant read foreign path %s: %q", path, body) } if f, err := os.OpenFile(path, os.O_WRONLY, 0); err == nil { f.Close() t.Fatalf("tenant opened foreign path for write: %s", path) } } } db, err := sql.Open("sqlite", *seedTenantDB) if err != nil { t.Fatal(err) } defer db.Close() var integrity string if err := db.QueryRow("PRAGMA integrity_check").Scan(&integrity); err != nil || integrity != "ok" { t.Fatal(integrity, err) } body, err := os.ReadFile(os.Getenv("FILES_SEED_PAYLOAD")) expected := os.Getenv("FILES_SEED_EXPECTED") if expected == "" { expected = "real seeded bytes" } if err != nil || string(body) != expected { t.Fatal(string(body), err) } listener, err := net.Listen("unix", *seedTenantSocket) if err != nil { t.Fatal(err) } defer listener.Close() server := &http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write(body) })} defer server.Close() if err := server.Serve(listener); err != nil { t.Fatal(err) } } func rootFilesSeedLiveWitness(t *testing.T, binary string) filesSeedLiveWitness { body, err := os.ReadFile(binary) if err != nil { t.Fatal(err) } digest := sha256.Sum256(body) return filesSeedLiveWitness{configure: func(w *ClaimWakeAssembly) { owners := w.Inputs.(*claimServingOwners) selectLaunch := owners.Sources.Launch owners.Sources.Launch = func(ctx context.Context, rt registry.Route) (ClaimServingLaunch, error) { launch, err := selectLaunch(ctx, rt) if err != nil { return launch, err } launch.Executable = binary launch.ExecutableSHA256 = hex.EncodeToString(digest[:]) launch.Env = append(launch.Env, "FILES_SEED_TENANT=1", "FILES_SEED_PAYLOAD="+filepath.Join(filepath.Dir(rt.DBPath), rt.Host, "payload")) return launch, nil } }, serve: func(t *testing.T, p *servingPreparation, o hostclaim.Observed, start activator.ClaimStart) { socket := provision.NestedSocket(o.Host) if err := os.MkdirAll(filepath.Dir(socket), 0700); err != nil { t.Fatal(err) } cmd := start.Build() cmd.Stdin = os.Stdin var output bytes.Buffer cmd.Stdout = &output cmd.Stderr = &output if err := cmd.Start(); err != nil { t.Fatal(err) } // No readiness or success is fabricated: the selected process owns the socket. defer func() { cmd.Process.Kill(); cmd.Wait(); os.Remove(socket) }() ctx, cancel := context.WithTimeout(t.Context(), 3*time.Second) defer cancel() if err := start.Ready(ctx, cmd); err != nil { cmd.Process.Kill() cmd.Wait() t.Fatalf("tenant readiness: %v %s", err, output.String()) } transport := &http.Transport{DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { return (&net.Dialer{}).DialContext(ctx, "unix", socket) }} defer transport.CloseIdleConnections() response, err := (&http.Client{Transport: transport, Timeout: time.Second}).Get("http://tenant/") if err != nil { t.Fatal(err) } payload, err := io.ReadAll(response.Body) response.Body.Close() if err != nil || response.StatusCode != 200 || string(payload) != "real seeded bytes" { t.Fatal("tenant served wrong restored payload", err, string(payload)) } wrong, _ := os.FindProcess(os.Getpid()) refuse, stop := context.WithTimeout(t.Context(), 40*time.Millisecond) defer stop() if err := start.Ready(refuse, &exec.Cmd{Process: wrong}); err == nil { t.Fatal("readiness accepted controller instead of tenant") } }} } func TestFilesSeedLiveFixtureShape(t *testing.T) { binary, err := os.Executable() if err != nil { t.Fatal(err) } witness := rootFilesSeedLiveWitness(t, binary) f := plan9D5Fixture(t) rt := registry.Route{Host: recoveryHost, DBPath: provision.NestedDBPath("/data", recoveryHost), Socket: provision.NestedSocket(recoveryHost)} f.launch.Args = []string{"--socket", rt.Socket, "--db", rt.DBPath} f.launch.Env = []string{"CARLOS_ADMIN_TOKEN=test"} w := &ClaimWakeAssembly{Inputs: f.owners} witness.configure(w) launch, err := f.owners.Sources.Launch(t.Context(), rt) if err != nil { t.Fatal(err) } role := &ClaimRoleAssembly{Root: fakeLaunchRoot{}, ManagedRoots: map[string]string{"data": "/data"}} in := ClaimLaunchInputs{Limit: cgroup.TierDefault, Roles: []string{"main"}, Identity: launch.Identity, Executable: launch.Executable, ExecutableSHA256: launch.ExecutableSHA256, Args: launch.Args, Env: launch.Env, DBPath: rt.DBPath} if err := role.PreflightLaunch(rt.Host, in); err != nil { t.Fatal(err, launch.Args, launch.Env) } }