carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux && rootrunner
2
3package host
4
5import (
6 "bufio"
7 "errors"
8 "fmt"
9 "os"
10 "os/exec"
11 "path/filepath"
12 "strconv"
13 "strings"
14 "syscall"
15 "testing"
16
17 "github.com/carlosframework/platform/internal/release"
18 "golang.org/x/sys/unix"
19)
20
21func TestPlan14GenerationDescriptorTransferRootRunner(t *testing.T) {
22 const modeKey = "PLAN14_E44_TRANSFER"
23 path := os.Getenv("PLAN14_E44_TRANSFER_PATH")
24 switch os.Getenv(modeKey) {
25 case "mount":
26 if err := unix.Mount("tmpfs", filepath.Dir(path), "tmpfs", 0, "mode=0755"); err != nil {
27 t.Fatal(err)
28 }
29 defer unix.Unmount(filepath.Dir(path), 0)
30 cmd := exec.Command(os.Args[0], "-test.run=^TestPlan14GenerationDescriptorTransferRootRunner$", "-test.timeout=30s")
31 cmd.Env = append(os.Environ(), modeKey+"=receive")
32 cmd.Dir = "/"
33 cmd.ExtraFiles = []*os.File{os.NewFile(3, "transfer")}
34 cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
35 cmd.SysProcAttr = &syscall.SysProcAttr{Credential: &syscall.Credential{Uid: 65534, Gid: 65534, Groups: []uint32{65534}}}
36 if err := cmd.Run(); err != nil {
37 t.Fatal(err)
38 }
39 return
40 case "receive":
41 if os.Geteuid() != 65534 {
42 t.Fatal("receiver must be unprivileged")
43 }
44 if _, err := os.ReadFile(path); !errors.Is(err, os.ErrNotExist) {
45 t.Fatal("staged path not masked", err)
46 }
47 fmt.Printf("READY %d\n", os.Getpid())
48 data, control := make([]byte, 1), make([]byte, unix.CmsgSpace(4))
49 _, n, _, _, err := unix.Recvmsg(3, data, control, 0)
50 if err != nil {
51 t.Fatal(err)
52 }
53 messages, err := unix.ParseSocketControlMessage(control[:n])
54 if err != nil || len(messages) != 1 {
55 t.Fatal("rights message", err)
56 }
57 fds, err := unix.ParseUnixRights(&messages[0])
58 if err != nil || len(fds) != 1 {
59 t.Fatal("rights descriptor", err)
60 }
61 file := os.NewFile(uintptr(fds[0]), "unreserved")
62 defer file.Close()
63 body := make([]byte, 10)
64 if n, err := file.Read(body); err != nil || string(body[:n]) != "unreserved" {
65 t.Fatal("received staged bytes", string(body), err)
66 }
67 fmt.Printf("HELD %d\n", fds[0])
68 if _, err := unix.Read(3, data); err != nil {
69 t.Fatal(err)
70 }
71 return
72 case "send":
73 if os.Geteuid() != 65534 {
74 t.Fatal("sender must be unprivileged")
75 }
76 file, err := os.Open(path)
77 if err != nil {
78 t.Fatal(err)
79 }
80 defer file.Close()
81 if err := unix.Sendmsg(3, []byte{1}, unix.UnixRights(int(file.Fd())), nil, 0); err != nil {
82 t.Fatal(err)
83 }
84 return
85 }
86 dir := generationRootRunnerDir(t)
87 body, err := os.ReadFile(os.Args[0])
88 if err != nil {
89 t.Fatal(err)
90 }
91 binary := filepath.Join(dir, "transfer.test")
92 if err := os.WriteFile(binary, body, 0755); err != nil {
93 t.Fatal(err)
94 }
95 path = filepath.Join(dir, "versions", "unreserved")
96 if err := os.Mkdir(filepath.Dir(path), 0755); err != nil {
97 t.Fatal(err)
98 }
99 if err := os.WriteFile(path, []byte("unreserved"), 0644); err != nil {
100 t.Fatal(err)
101 }
102 sockets, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
103 if err != nil {
104 t.Fatal(err)
105 }
106 parent, child := os.NewFile(uintptr(sockets[0]), "parent"), os.NewFile(uintptr(sockets[1]), "child")
107 defer parent.Close()
108 defer child.Close()
109 cmd := exec.CommandContext(t.Context(), "unshare", "--mount", binary, "-test.run=^TestPlan14GenerationDescriptorTransferRootRunner$", "-test.timeout=45s")
110 cmd.Dir = dir
111 cmd.Env = append(os.Environ(), modeKey+"=mount", "PLAN14_E44_TRANSFER_PATH="+path)
112 cmd.ExtraFiles = []*os.File{child}
113 cmd.Stderr = os.Stderr
114 output, err := cmd.StdoutPipe()
115 if err != nil {
116 t.Fatal(err)
117 }
118 if err := cmd.Start(); err != nil {
119 t.Fatal(err)
120 }
121 defer func() { cmd.Process.Kill(); cmd.Wait() }()
122 scan := bufio.NewScanner(output)
123 number := func(prefix string) int {
124 t.Helper()
125 for scan.Scan() {
126 if s, ok := strings.CutPrefix(scan.Text(), prefix); ok {
127 n, err := strconv.Atoi(s)
128 if err != nil {
129 t.Fatal(err)
130 }
131 return n
132 }
133 t.Log(scan.Text())
134 }
135 t.Fatal("missing child observation", prefix, scan.Err())
136 return 0
137 }
138 pid := number("READY ")
139 sender := exec.CommandContext(t.Context(), binary, "-test.run=^TestPlan14GenerationDescriptorTransferRootRunner$", "-test.timeout=15s")
140 sender.Dir = dir
141 sender.Env = append(os.Environ(), modeKey+"=send", "PLAN14_E44_TRANSFER_PATH="+path)
142 sender.ExtraFiles = []*os.File{parent}
143 sender.SysProcAttr = &syscall.SysProcAttr{Credential: &syscall.Credential{Uid: 65534, Gid: 65534, Groups: []uint32{65534}}}
144 if out, err := sender.CombinedOutput(); err != nil {
145 t.Fatalf("unprivileged sender: %v %s", err, out)
146 }
147 fd := number("HELD ")
148 if err := generationLinkRef(fmt.Sprintf("/proc/%d/fd/%d", pid, fd), release.GenerationBinding{Path: path, Tree: filepath.Dir(path)}); err == nil {
149 t.Fatal("transferred staged descriptor was missed")
150 }
151 t.Setenv("CARLOS_OPT_DIR", dir)
152 if _, _, err := productionGenerationKernelCensus(t.Context()); err == nil {
153 t.Fatal("uncontained descriptor recipient certified")
154 }
155 if _, err := parent.Write([]byte{1}); err != nil {
156 t.Fatal(err)
157 }
158 for scan.Scan() {
159 t.Log(scan.Text())
160 }
161 if err := cmd.Wait(); err != nil {
162 t.Fatal(err)
163 }
164}
165