diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-10-06 22:23:20 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-10-06 23:09:19 +0900 |
| commit | a9e2749f6654d0aa07b274a45c9177d10323f80a (patch) | |
| tree | 291f5b23a67af036cbe5d374b3d3fb0240fc438c /test/sandbox | |
| parent | 19f36491f2e2a5029ac396c10408d653cad6c81b (diff) | |
internal/testsuite: move from test
This structure is a lot less clumsy than the old nix-centric layout.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'test/sandbox')
| -rw-r--r-- | test/sandbox/main.go | 409 | ||||
| -rw-r--r-- | test/sandbox/seccomp.patch | 18 | ||||
| -rw-r--r-- | test/sandbox/testdata/device.go | 134 | ||||
| -rw-r--r-- | test/sandbox/testdata/mapuid.go | 124 | ||||
| -rw-r--r-- | test/sandbox/testdata/pdlike.go | 141 | ||||
| -rw-r--r-- | test/sandbox/testdata/simple.go | 140 | ||||
| -rw-r--r-- | test/sandbox/testdata/sum.go | 22 | ||||
| -rw-r--r-- | test/sandbox/testdata/sum_amd64.go | 9 | ||||
| -rw-r--r-- | test/sandbox/testdata/sum_arm64.go | 9 | ||||
| -rw-r--r-- | test/sandbox/testdata/testdata.go | 125 | ||||
| -rw-r--r-- | test/sandbox/testdata/tty.go | 145 | ||||
| -rw-r--r-- | test/sandbox/tester/main.go | 224 |
12 files changed, 0 insertions, 1500 deletions
diff --git a/test/sandbox/main.go b/test/sandbox/main.go deleted file mode 100644 index 4961c04f..00000000 --- a/test/sandbox/main.go +++ /dev/null @@ -1,409 +0,0 @@ -//go:build testsuite - -// The sandbox test program runs cmd/hakurei with configurations simulating -// several common workloads and inspects the resulting container states. -package main - -import ( - "bytes" - "context" - "encoding/json" - "io" - "log" - "os" - "os/exec" - "path/filepath" - "slices" - "strconv" - "strings" - "sync" - "sync/atomic" - "syscall" - - "hakurei.app/check" - "hakurei.app/fhs" - "hakurei.app/hst" - "hakurei.app/internal/store" - - "hakurei.app/test/internal/testsuite" - "hakurei.app/test/sandbox/testdata" -) - -// mustScanFor continuously scans the proc filesystem and calls f for each entry -// visited. -func mustScanFor(f func(ps *testsuite.StatScanner) bool) int { - var ps testsuite.StatScanner - - for ps.Scan() { - if f(&ps) { - break - } - } - if err := ps.Err(); err != nil { - log.Fatal(err) - } - return ps.Stat().PID -} - -// mustStart starts a hakurei container and returns the pid of a process within -// the container. This process must be terminated by the caller. -func mustStart( - ctx context.Context, - serial uint64, - cred *syscall.Credential, - files ...*os.File, -) (pid int, done <-chan error) { - _serial := strconv.FormatUint(serial, 10) - _, done = testsuite.MustStartWith( - ctx, cred, nil, files, - "hakurei", "exec", - "sleep", "infinity", _serial, - ) - - var stat syscall.Stat_t - pid = mustScanFor(func(s *testsuite.StatScanner) bool { - select { - case err := <-done: - if err == nil { - log.Fatal("test process terminated unexpectedly") - } - log.Fatal(err) - default: - break - } - - if s.Stat().Comm != "sleep" { - return false - } - - if args, err := s.Stat().Args(); err != nil { - if testsuite.IsNotExist(err) { - return false - } - log.Fatal(err) - } else if !slices.Equal(args, []string{ - "sleep", - "infinity", - _serial, - }) { - return false - } - - if err := s.Stat().Stat(&stat); err != nil { - if testsuite.IsNotExist(err) { - return false - } - log.Fatal(err) - } - - id := hst.ToUser[uint32](0, 0) - if stat.Uid != id || stat.Gid != id { - return false - } - - return true - }) - return -} - -func main() { - go testsuite.ReceiveSignals() - - // the signal handler does not wait for termination - ctx := context.Background() - - cred := syscall.Credential{Uid: 1000, Gid: 100} - if err := os.MkdirAll("/opt/test-helper/bin", 0755); err != nil { - log.Fatal(err) - } - - var testToolDone <-chan error - { - cmd := exec.Command( - "go", "build", - "-o", "/opt/test-helper/bin", - "-tags=tester", - "-trimpath", - "./test/sandbox/tester", - ) - cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr - testToolDone = testsuite.MustStart(cmd) - } - - var wg sync.WaitGroup - defer wg.Wait() - - var serial atomic.Uint64 - newSerial := func() uint64 { serial.Add(1); return serial.Load() } - - testsuite.MustRun( - &cred, nil, - "hakurei", "exec", "capsh", "--print", - ) - wg.Go(func() { - defer log.Println("validated capabilities/securebits in user namespace") - - testsuite.MustRun( - &cred, nil, - "hakurei", "exec", "capsh", "--has-no-new-privs", - ) - - for _, p := range []byte{'a', 'b', 'i', 'p'} { - testsuite.MustFail( - &cred, nil, - "hakurei", "exec", "capsh", "--has-"+string(p)+"=CAP_SYS_ADMIN", - ) - } - testsuite.MustFail( - &cred, nil, - "hakurei", "exec", "umount", "-R", "/dev", - ) - }) - - wg.Go(func() { - defer log.Println("validated pd seccomp outcome") - - c, cancel := context.WithCancel(ctx) - defer cancel() - - pid, done := mustStart(c, newSerial(), &cred) - testsuite.MustCheckFilter(pid, testdata.SumPD) - if err := testsuite.FilterTerminated(<-done); err != nil { - log.Fatal(err) - } - }) - - wg.Go(func() { - defer log.Println("validated fd leak") - - c, cancel := context.WithCancel(ctx) - defer cancel() - - pid, done := mustStart(c, newSerial(), &cred, os.Stdin, os.Stdout, os.Stderr) - prefix := filepath.Join(fhs.Proc, strconv.Itoa(pid), "fd") - - var fail bool - if entries, err := os.ReadDir(prefix); err != nil { - log.Fatal(err.Error()) - } else { - for _, ent := range entries { - var fd int - if fd, err = strconv.Atoi(ent.Name()); err != nil { - log.Fatal(err.Error()) - } - - // skip standard streams - if fd <= 2 { - continue - } - fail = true - - var d string - if d, err = os.Readlink(filepath.Join( - prefix, - ent.Name(), - )); err != nil { - log.Fatal(err.Error()) - } - log.Printf("extra fd %d -> %s", fd, d) - } - } - if fail { - log.Fatal("file descriptors leaked") - } - - if err := syscall.Kill(pid, syscall.SIGTERM); err != nil { - log.Fatalf("cannot terminate anchor: %v", err) - } else if err = testsuite.FilterTerminated(<-done); err != nil { - log.Fatal(err) - } - }) - - if err := os.MkdirAll(testsuite.XDGRuntimeDir, 0700); err != nil { - log.Fatal(err) - } else if err = os.Chown(testsuite.XDGRuntimeDir, 1000, 1000); err != nil { - log.Fatal(err) - } - - var swg sync.WaitGroup - defer swg.Wait() - dbusEnv := testsuite.MustStartSessionBus(&cred) - testsuite.MustStartSway(&swg, &cred, dbusEnv) - defer testsuite.TerminateSway(&cred) - testsuite.MustStartPipeWire(&cred, dbusEnv) - - if err := <-testToolDone; err != nil { - log.Fatal(err) - } - log.Println("created test helper") - - s := store.New(check.MustAbs("/tmp/hakurei.0/state")) - for name, tc := range testdata.All() { - wg.Go(func() { - cmd := exec.Command( - "script", "/dev/null", - "-E", "always", - "-qec", - "hakurei run "+ - "--identifier-fd=5"+ - " 4 1>&3", - ) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Pdeathsig: syscall.SIGTERM, - Credential: &cred, - } - var output bytes.Buffer - cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, &output, &output - cmd.Env = []string{ - "PATH=" + os.Getenv("PATH"), - "TERM=xterm", - testsuite.XDGRuntimeEnv, - testsuite.WaylandEnv, - "DISPLAY=:0", - dbusEnv, - } - - var err error - var notify, _notify, _conf, conf, ident, _ident *os.File - if notify, _notify, err = os.Pipe(); err != nil { - log.Fatal(err) - } - cmd.ExtraFiles = append(cmd.ExtraFiles, _notify) - if _conf, conf, err = os.Pipe(); err != nil { - log.Fatal(err) - } - cmd.ExtraFiles = append(cmd.ExtraFiles, _conf) - if ident, _ident, err = os.Pipe(); err != nil { - log.Fatal(err) - } - cmd.ExtraFiles = append(cmd.ExtraFiles, _ident) - - done := testsuite.MustStart(cmd) - wg.Go(func() { - _err := <-done - log.Printf("completed test case %s\n%s", name, output.String()) - if _err != nil { - log.Fatalf("test case %s: %v", name, _err) - } - }) - - if err = json.NewEncoder(conf).Encode(&tc.Hakurei); err != nil { - log.Fatal(err) - } else if err = conf.Close(); err != nil { - log.Fatal(err) - } - - var id hst.ID - if _, err = io.ReadFull(ident, id[:]); err != nil { - log.Fatal(err) - } else if err = ident.Close(); err != nil { - log.Fatal(err) - } - - if _, err = io.ReadFull(notify, make([]byte, 8)); err != nil { - log.Fatal(err) - } else if err = notify.Close(); err != nil { - log.Fatal(err) - } - - var ( - ok bool - p hst.State - ) - entries, copyError := s.All() - for entry := range entries { - if entry.ID == id { - ok = true - if _, err = entry.Load(&p, nil); err != nil { - log.Fatal(err) - } - break - } - } - if err = copyError(); err != nil { - log.Fatal(err) - } - if !ok { - log.Fatalf("instance %s is not present in store", id) - } - - var stat syscall.Stat_t - pid := mustScanFor(func(ps *testsuite.StatScanner) bool { - select { - case err = <-done: - if err == nil { - log.Fatal("test process terminated unexpectedly") - } - log.Fatal(err) - default: - break - } - - if ps.Stat().Comm != "test-helper" { - return false - } - - var args []string - if args, err = ps.Stat().Args(); err != nil { - if testsuite.IsNotExist(err) { - return false - } - log.Fatal(err) - } else if !slices.Equal(args, tc.Hakurei.Container.Args) { - return false - } - - if err = ps.Stat().Stat(&stat); err != nil { - if testsuite.IsNotExist(err) { - return false - } - log.Fatal(err) - } - - uid := hst.ToUser[uint32](0, uint32(tc.Hakurei.Identity)) - if stat.Uid != uid || stat.Gid != uid { - return false - } - - var t []byte - if t, err = os.ReadFile(filepath.Join( - fhs.Proc, - strconv.Itoa(ps.Stat().PPID), - "stat", - )); err != nil { - if testsuite.IsNotExist(err) { - return false - } - log.Fatal(err) - } - - var _stat testsuite.Stat - if err = _stat.UnmarshalText(t); err != nil { - log.Fatal(err) - } - if _stat.PPID != p.ShimPID { - return false - } - - return true - }) - - testsuite.MustCheckFilter( - pid, - tc.Sum, - ) - }) - } - - wg.Wait() - - if dents, err := os.ReadDir("/tmp"); err != nil { - log.Fatal(err) - } else { - for _, dent := range dents { - if name := dent.Name(); strings.HasPrefix(name, ".hakurei-shim-") { - log.Fatalf("leftover shim work dir %q", name) - } - } - } -} diff --git a/test/sandbox/seccomp.patch b/test/sandbox/seccomp.patch deleted file mode 100644 index ddabc71e..00000000 --- a/test/sandbox/seccomp.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git a/kernel/seccomp.c b/kernel/seccomp.c -index 25f62867a16d..7b63ccc8daf4 100644 ---- a/kernel/seccomp.c -+++ b/kernel/seccomp.c -@@ -2216,8 +2216,12 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off, - struct seccomp_filter *filter; - struct sock_fprog_kern *fprog; - long ret; -+ struct user_namespace *user_ns = current_user_ns(); - -- if (!capable(CAP_SYS_ADMIN) || -+ if (in_userns(user_ns, task_cred_xxx(task, user_ns))) { -+ if (!ns_capable(user_ns, CAP_SYS_ADMIN)) -+ return -EACCES; -+ } else if (!capable(CAP_SYS_ADMIN) || - current->seccomp.mode != SECCOMP_MODE_DISABLED) { - return -EACCES; - } diff --git a/test/sandbox/testdata/device.go b/test/sandbox/testdata/device.go deleted file mode 100644 index 89feb819..00000000 --- a/test/sandbox/testdata/device.go +++ /dev/null @@ -1,134 +0,0 @@ -//go:build testsuite || tester - -package testdata - -import ( - "os" - "syscall" - - "hakurei.app/fhs" - "hakurei.app/hst" - "hakurei.app/test/internal/mountinfo" - "hakurei.app/test/internal/testsuite" -) - -var _ = TestCase{ - Hakurei: hst.Config{ - ID: "app.hakurei.sample.device", - Enablements: new(hst.EWayland | hst.EPipeWire | hst.EDBus | hst.EX11), - Identity: 4, - - Container: &hst.ContainerConfig{ - Hostname: "hakurei-sample-device", - - Filesystem: []hst.FilesystemConfigJSON{ - fcLinker, - fcLib, - fcTestHelper, - }, - - Username: "u0_a4", - Shell: fhs.AbsUsrBin.Append("bash"), - Home: hst.AbsPrivateTmp, - Path: absTestHelper, - Args: []string{"tester", "device"}, - - Flags: hst.FDevice | hst.FShareTmpdir, - }, - }, - - // 0, PresetStrict - Sum: sumSimple, - - Env: []string{ - "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus", - "DISPLAY=unix:/tmp/.X11-unix/X0", - "HOME=/.hakurei", - "SHELL=/usr/bin/bash", - "TERM=xterm", - "USER=u0_a4", - "WAYLAND_DISPLAY=wayland-0", - "XDG_RUNTIME_DIR=/run/user/65534", - "XDG_SESSION_CLASS=user", - "XDG_SESSION_TYPE=wayland", - "PULSE_SERVER=unix:/run/user/65534/pulse/native", - }, - - FS: &testsuite.FS{Dir: dir{ - ".hakurei": {Mode: os.ModeDir | 0755, Dir: dir{ - "test-helper": {Mode: 0755}, - }}, - - // unstable host dev - "dev": {Mode: os.ModeDir | 0755}, - - "etc": {Mode: os.ModeDir | 0755, Dir: dir{ - "passwd": {Mode: 0600, - Data: new("u0_a4:x:65534:65534:Hakurei:/.hakurei:/usr/bin/bash\n")}, - "group": {Mode: 0600, - Data: new("hakurei:x:65534:\n")}, - }}, - - "lib64": {Mode: os.ModeDir | 0755, Dir: dir{ - "ld-linux-x86-64.so.2": {Mode: os.ModeSymlink | 0777}, - }}, - - "run": {Mode: os.ModeDir | 0755, Dir: dir{ - "user": {Mode: os.ModeDir | 0755, Dir: dir{ - "65534": {Mode: os.ModeDir | 0700, Dir: dir{ - "bus": {Mode: os.ModeSocket | 0775}, - "wayland-0": {Mode: os.ModeSocket | 070}, - "pulse": {Mode: os.ModeDir | 0700, Dir: dir{ - "native": {Mode: os.ModeSocket | 0777}, - }}, - }}, - }}, - }}, - - "tmp": {Mode: os.ModeDir | 0770, Dir: dir{ - ".X11-unix": {Mode: os.ModeDir | 0755, Dir: dir{ - "X0": {Mode: os.ModeSocket | 0775}, - }}, - }}, - - "lib": {Mode: os.ModeDir | 0755}, - "proc": {Mode: os.ModeDir | 0555}, - }}, - - Mount: []*mountinfo.Entry{ - r("/sysroot", "/", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110003,gid=110003,inode64"), - r("/", "/proc", "rw,nosuid,nodev,noexec,relatime", "proc", "proc", "rw"), - r("/", "/.hakurei", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,size=4k,mode=755,uid=110003,gid=110003,inode64"), - - // host /dev in testing environment - r("/", "/dev", "rw,nosuid", "tmpfs", "tmpfs", ignore), - r("/", "/dev/pts", "rw,nosuid,noexec,relatime", "devpts", "devpts", "rw,gid=100004,mode=620,ptmxmode=666"), - r("/", "/dev/mqueue", "rw,nosuid,nodev,noexec,relatime", "mqueue", "mqueue", "rw"), - r("/kvm", "/dev/kvm", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/fuse", "/dev/fuse", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/", "/dev/shm", "rw,nosuid,nodev,noexec,relatime", "tmpfs", "shm", ignore), - r("/null", "/dev/null", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/zero", "/dev/zero", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/full", "/dev/full", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/tty", "/dev/tty", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/random", "/dev/random", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/urandom", "/dev/urandom", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - - r("/", "/dev/shm", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,uid=110003,gid=110003,inode64"), - r("/", "/run/user", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,size=16384k,mode=755,uid=110003,gid=110003,inode64"), - r("/tmp/hakurei.0/tmpdir/4", "/tmp", "rw,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r(ignore, "/etc/passwd", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110003,gid=110003,inode64"), - r(ignore, "/etc/group", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110003,gid=110003,inode64"), - r(ignore, "/run/user/65534/wayland-0", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/tmp/.X11-unix", "/tmp/.X11-unix", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r(ignore, "/run/user/65534/bus", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/usr/lib", "/lib", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/opt/test-helper/bin/tester", "/.hakurei/test-helper", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r(ignore, "/run/user/65534/pulse/native", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - }, - - Seccomp: true, - - TrySocket: "/tmp/.X11-unix/X0", - ErrnoAbstract: syscall.ECONNREFUSED, -}.register("device") diff --git a/test/sandbox/testdata/mapuid.go b/test/sandbox/testdata/mapuid.go deleted file mode 100644 index 8dee7b7f..00000000 --- a/test/sandbox/testdata/mapuid.go +++ /dev/null @@ -1,124 +0,0 @@ -//go:build testsuite || tester - -package testdata - -import ( - "os" - "syscall" - - "hakurei.app/fhs" - "hakurei.app/hst" - "hakurei.app/test/internal/mountinfo" - "hakurei.app/test/internal/testsuite" -) - -var _ = TestCase{ - Hakurei: hst.Config{ - ID: "app.hakurei.sample.mapuid", - Enablements: new(hst.EWayland | hst.EPipeWire | hst.EDBus), - Identity: 3, - - Container: &hst.ContainerConfig{ - Hostname: "hakurei-sample-mapuid", - - Filesystem: []hst.FilesystemConfigJSON{ - fcLinker, - fcLib, - fcTestHelper, - }, - - Username: "u0_a3", - Shell: fhs.AbsUsrBin.Append("bash"), - Home: hst.AbsPrivateTmp, - Path: absTestHelper, - Args: []string{"tester", "mapuid"}, - - Flags: hst.FMapRealUID | hst.FShareRuntime | hst.FShareTmpdir, - }, - }, - - // 0, PresetStrict - Sum: sumSimple, - - Env: []string{ - "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus", - "HOME=/.hakurei", - "SHELL=/usr/bin/bash", - "TERM=xterm", - "USER=u0_a3", - "WAYLAND_DISPLAY=wayland-0", - "XDG_RUNTIME_DIR=/run/user/1000", - "XDG_SESSION_CLASS=user", - "XDG_SESSION_TYPE=wayland", - "PULSE_SERVER=unix:/run/user/1000/pulse/native", - }, - - FS: &testsuite.FS{Dir: dir{ - ".hakurei": {Mode: os.ModeDir | 0755, Dir: dir{ - "test-helper": {Mode: 0755}, - }}, - - // unstable host dev - "dev": {Mode: os.ModeDir | 0755}, - - "etc": {Mode: os.ModeDir | 0755, Dir: dir{ - "passwd": {Mode: 0600, - Data: new("u0_a3:x:1000:100:Hakurei:/.hakurei:/usr/bin/bash\n")}, - "group": {Mode: 0600, - Data: new("hakurei:x:100:\n")}, - }}, - - "lib64": {Mode: os.ModeDir | 0755, Dir: dir{ - "ld-linux-x86-64.so.2": {Mode: os.ModeSymlink | 0777}, - }}, - - "run": {Mode: os.ModeDir | 0755, Dir: dir{ - "user": {Mode: os.ModeDir | 0755, Dir: dir{ - "1000": {Mode: os.ModeDir | 0770, Dir: dir{ - "bus": {Mode: os.ModeSocket | 0775}, - "wayland-0": {Mode: os.ModeSocket | 070}, - "pulse": {Mode: os.ModeDir | 0700, Dir: dir{ - "native": {Mode: os.ModeSocket | 0777}, - }}, - }}, - }}, - }}, - - "tmp": {Mode: os.ModeDir | 0770, Dir: dir{}}, - - "lib": {Mode: os.ModeDir | 0755}, - "proc": {Mode: os.ModeDir | 0555}, - }}, - - Mount: []*mountinfo.Entry{ - r("/sysroot", "/", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110002,gid=110002,inode64"), - r("/", "/proc", "rw,nosuid,nodev,noexec,relatime", "proc", "proc", "rw"), - r("/", "/.hakurei", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,size=4k,mode=755,uid=110002,gid=110002,inode64"), - r("/", "/dev", "ro,nosuid,nodev,relatime", "tmpfs", "devtmpfs", "rw,mode=755,uid=110002,gid=110002,inode64"), - r("/null", "/dev/null", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/zero", "/dev/zero", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/full", "/dev/full", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/random", "/dev/random", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/urandom", "/dev/urandom", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/tty", "/dev/tty", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/", "/dev/pts", "rw,nosuid,noexec,relatime", "devpts", "devpts", "rw,mode=620,ptmxmode=666"), - r("/", "/dev/mqueue", "rw,nosuid,nodev,noexec,relatime", "mqueue", "mqueue", "rw"), - r("/", "/dev/shm", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,uid=110002,gid=110002,inode64"), - r("/", "/run/user", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,size=16384k,mode=755,uid=110002,gid=110002,inode64"), - r("/tmp/hakurei.0/runtime/3", "/run/user/1000", "rw,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/tmp/hakurei.0/tmpdir/3", "/tmp", "rw,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r(ignore, "/etc/passwd", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110002,gid=110002,inode64"), - r(ignore, "/etc/group", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110002,gid=110002,inode64"), - r(ignore, "/run/user/1000/wayland-0", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r(ignore, "/run/user/1000/bus", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/usr/lib", "/lib", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/opt/test-helper/bin/tester", "/.hakurei/test-helper", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r(ignore, "/run/user/1000/pulse/native", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - }, - - Seccomp: true, - - TrySocket: "/tmp/.X11-unix/X0", - ErrnoAbstract: syscall.ECONNREFUSED, - ErrnoPathname: syscall.ENOENT, -}.register("mapuid") diff --git a/test/sandbox/testdata/pdlike.go b/test/sandbox/testdata/pdlike.go deleted file mode 100644 index 53d8062a..00000000 --- a/test/sandbox/testdata/pdlike.go +++ /dev/null @@ -1,141 +0,0 @@ -//go:build testsuite || tester - -package testdata - -import ( - "os" - "syscall" - - "hakurei.app/fhs" - "hakurei.app/hst" - "hakurei.app/test/internal/mountinfo" - "hakurei.app/test/internal/testsuite" -) - -var _ = TestCase{ - Hakurei: hst.Config{ - ID: "app.hakurei.sample.pdlike", - Enablements: new(hst.EWayland | hst.EPipeWire | hst.EDBus), - Identity: 5, - - Container: &hst.ContainerConfig{ - Hostname: "hakurei-sample-pdlike", - - Filesystem: []hst.FilesystemConfigJSON{ - fcLinker, - fcLib, - fcTestHelper, - }, - - Username: "u0_a5", - Shell: fhs.AbsUsrBin.Append("bash"), - Home: hst.AbsPrivateTmp, - Path: absTestHelper, - Args: []string{"tester", "pdlike"}, - - Flags: hst.FHostNet | hst.FTty | hst.FUserns | hst.FShareRuntime | hst.FShareTmpdir, - }, - }, - - // 0, PresetExt | PresetDenyDevel - Sum: SumPD, - - Env: []string{ - "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus", - "HOME=/.hakurei", - "SHELL=/usr/bin/bash", - "TERM=xterm", - "USER=u0_a5", - "WAYLAND_DISPLAY=wayland-0", - "XDG_RUNTIME_DIR=/run/user/65534", - "XDG_SESSION_CLASS=user", - "XDG_SESSION_TYPE=wayland", - "PULSE_SERVER=unix:/run/user/65534/pulse/native", - }, - - FS: &testsuite.FS{Dir: dir{ - ".hakurei": {Mode: os.ModeDir | 0755, Dir: dir{ - "test-helper": {Mode: 0755}, - }}, - - "dev": {Mode: os.ModeDir | 0755, Dir: dir{ - "core": {Mode: os.ModeSymlink | 0777}, - "fd": {Mode: os.ModeSymlink | 0777}, - "full": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - "mqueue": {Mode: os.ModeDir | os.ModeSticky | 0777, Dir: dir{}}, - "null": {Mode: os.ModeDevice | os.ModeCharDevice | 0666, Data: new("")}, - "ptmx": {Mode: os.ModeSymlink | 0777}, - "pts": {Mode: os.ModeDir | 0755, Dir: dir{ - "ptmx": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - }}, - "random": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - "shm": {Mode: os.ModeDir | os.ModeSticky | 0777, Dir: dir{}}, - "stderr": {Mode: os.ModeSymlink | 0777}, - "stdin": {Mode: os.ModeSymlink | 0777}, - "stdout": {Mode: os.ModeSymlink | 0777}, - "tty": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - "urandom": {Mode: os.ModeDevice | os.ModeCharDevice | 0444}, - "zero": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - }}, - - "etc": {Mode: os.ModeDir | 0755, Dir: dir{ - "passwd": {Mode: 0600, - Data: new("u0_a5:x:65534:65534:Hakurei:/.hakurei:/usr/bin/bash\n")}, - "group": {Mode: 0600, - Data: new("hakurei:x:65534:\n")}, - }}, - - "lib64": {Mode: os.ModeDir | 0755, Dir: dir{ - "ld-linux-x86-64.so.2": {Mode: os.ModeSymlink | 0777}, - }}, - - "run": {Mode: os.ModeDir | 0755, Dir: dir{ - "user": {Mode: os.ModeDir | 0755, Dir: dir{ - "65534": {Mode: os.ModeDir | 0770, Dir: dir{ - "bus": {Mode: os.ModeSocket | 0775}, - "wayland-0": {Mode: os.ModeSocket | 070}, - "pulse": {Mode: os.ModeDir | 0700, Dir: dir{ - "native": {Mode: os.ModeSocket | 0777}, - }}, - }}, - }}, - }}, - - "tmp": {Mode: os.ModeDir | 0770, Dir: dir{}}, - - "lib": {Mode: os.ModeDir | 0755}, - "proc": {Mode: os.ModeDir | 0555}, - }}, - - Mount: []*mountinfo.Entry{ - r("/sysroot", "/", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110004,gid=110004,inode64"), - r("/", "/proc", "rw,nosuid,nodev,noexec,relatime", "proc", "proc", "rw"), - r("/", "/.hakurei", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,size=4k,mode=755,uid=110004,gid=110004,inode64"), - r("/", "/dev", "ro,nosuid,nodev,relatime", "tmpfs", "devtmpfs", "rw,mode=755,uid=110004,gid=110004,inode64"), - r("/null", "/dev/null", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/zero", "/dev/zero", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/full", "/dev/full", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/random", "/dev/random", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/urandom", "/dev/urandom", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/tty", "/dev/tty", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/", "/dev/pts", "rw,nosuid,noexec,relatime", "devpts", "devpts", "rw,mode=620,ptmxmode=666"), - r("/", "/dev/mqueue", "rw,nosuid,nodev,noexec,relatime", "mqueue", "mqueue", "rw"), - r("/", "/dev/shm", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,uid=110004,gid=110004,inode64"), - r("/", "/run/user", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,size=16384k,mode=755,uid=110004,gid=110004,inode64"), - r("/tmp/hakurei.0/runtime/5", "/run/user/65534", "rw,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/tmp/hakurei.0/tmpdir/5", "/tmp", "rw,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r(ignore, "/etc/passwd", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110004,gid=110004,inode64"), - r(ignore, "/etc/group", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110004,gid=110004,inode64"), - r(ignore, "/run/user/65534/wayland-0", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r(ignore, "/run/user/65534/bus", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/usr/lib", "/lib", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/opt/test-helper/bin/tester", "/.hakurei/test-helper", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r(ignore, "/run/user/65534/pulse/native", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - }, - - Seccomp: true, - - TrySocket: "/tmp/.X11-unix/X0", - ErrnoAbstract: syscall.EPERM, - ErrnoPathname: syscall.ENOENT, -}.register("pdlike") diff --git a/test/sandbox/testdata/simple.go b/test/sandbox/testdata/simple.go deleted file mode 100644 index c410e626..00000000 --- a/test/sandbox/testdata/simple.go +++ /dev/null @@ -1,140 +0,0 @@ -//go:build testsuite || tester - -package testdata - -import ( - "os" - "syscall" - - "hakurei.app/fhs" - "hakurei.app/hst" - "hakurei.app/test/internal/mountinfo" - "hakurei.app/test/internal/testsuite" -) - -var _ = TestCase{ - Hakurei: hst.Config{ - ID: "app.hakurei.sample.simple", - Enablements: new(hst.EWayland | hst.EPipeWire | hst.EDBus), - Identity: 1, - - Container: &hst.ContainerConfig{ - Hostname: "hakurei-sample-simple", - Env: map[string]string{"HAKUREI_SAMPLE": "1"}, - - Filesystem: []hst.FilesystemConfigJSON{ - fcLinker, - fcLib, - fcTestHelper, - }, - - Username: "u0_a1", - Shell: fhs.AbsUsrBin.Append("bash"), - Home: hst.AbsPrivateTmp, - Path: absTestHelper, - Args: []string{"tester", "simple"}, - }, - }, - - // 0, PresetStrict - Sum: sumSimple, - - Env: []string{ - "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus", - "HAKUREI_SAMPLE=1", - "HOME=/.hakurei", - "SHELL=/usr/bin/bash", - "TERM=xterm", - "USER=u0_a1", - "WAYLAND_DISPLAY=wayland-0", - "XDG_RUNTIME_DIR=/run/user/65534", - "XDG_SESSION_CLASS=user", - "XDG_SESSION_TYPE=wayland", - "PULSE_SERVER=unix:/run/user/65534/pulse/native", - }, - - FS: &testsuite.FS{Dir: dir{ - ".hakurei": {Mode: os.ModeDir | 0755, Dir: dir{ - "test-helper": {Mode: 0755}, - }}, - - "dev": {Mode: os.ModeDir | 0755, Dir: dir{ - "core": {Mode: os.ModeSymlink | 0777}, - "fd": {Mode: os.ModeSymlink | 0777}, - "full": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - "mqueue": {Mode: os.ModeDir | os.ModeSticky | 0777, Dir: dir{}}, - "null": {Mode: os.ModeDevice | os.ModeCharDevice | 0666, Data: new("")}, - "ptmx": {Mode: os.ModeSymlink | 0777}, - "pts": {Mode: os.ModeDir | 0755, Dir: dir{ - "ptmx": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - }}, - "random": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - "shm": {Mode: os.ModeDir | os.ModeSticky | 0777, Dir: dir{}}, - "stderr": {Mode: os.ModeSymlink | 0777}, - "stdin": {Mode: os.ModeSymlink | 0777}, - "stdout": {Mode: os.ModeSymlink | 0777}, - "tty": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - "urandom": {Mode: os.ModeDevice | os.ModeCharDevice | 0444}, - "zero": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - }}, - - "etc": {Mode: os.ModeDir | 0755, Dir: dir{ - "passwd": {Mode: 0600, - Data: new("u0_a1:x:65534:65534:Hakurei:/.hakurei:/usr/bin/bash\n")}, - "group": {Mode: 0600, - Data: new("hakurei:x:65534:\n")}, - }}, - - "lib64": {Mode: os.ModeDir | 0755, Dir: dir{ - "ld-linux-x86-64.so.2": {Mode: os.ModeSymlink | 0777}, - }}, - - "run": {Mode: os.ModeDir | 0755, Dir: dir{ - "user": {Mode: os.ModeDir | 0755, Dir: dir{ - "65534": {Mode: os.ModeDir | 0700, Dir: dir{ - "bus": {Mode: os.ModeSocket | 0775}, - "wayland-0": {Mode: os.ModeSocket | 070}, - "pulse": {Mode: os.ModeDir | 0700, Dir: dir{ - "native": {Mode: os.ModeSocket | 0777}, - }}, - }}, - }}, - }}, - - "tmp": {Mode: os.ModeDir | os.ModeSticky | 0777, Dir: dir{}}, - - "lib": {Mode: os.ModeDir | 0755}, - "proc": {Mode: os.ModeDir | 0555}, - }}, - - Mount: []*mountinfo.Entry{ - r("/sysroot", "/", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110000,gid=110000,inode64"), - r("/", "/proc", "rw,nosuid,nodev,noexec,relatime", "proc", "proc", "rw"), - r("/", "/.hakurei", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,size=4k,mode=755,uid=110000,gid=110000,inode64"), - r("/", "/dev", "ro,nosuid,nodev,relatime", "tmpfs", "devtmpfs", "rw,mode=755,uid=110000,gid=110000,inode64"), - r("/null", "/dev/null", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/zero", "/dev/zero", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/full", "/dev/full", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/random", "/dev/random", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/urandom", "/dev/urandom", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/tty", "/dev/tty", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/", "/dev/pts", "rw,nosuid,noexec,relatime", "devpts", "devpts", "rw,mode=620,ptmxmode=666"), - r("/", "/dev/mqueue", "rw,nosuid,nodev,noexec,relatime", "mqueue", "mqueue", "rw"), - r("/", "/dev/shm", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,uid=110000,gid=110000,inode64"), - r("/", "/run/user", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,size=16384k,mode=755,uid=110000,gid=110000,inode64"), - r("/", "/tmp", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,uid=110000,gid=110000,inode64"), - r(ignore, "/etc/passwd", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110000,gid=110000,inode64"), - r(ignore, "/etc/group", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110000,gid=110000,inode64"), - r(ignore, "/run/user/65534/wayland-0", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r(ignore, "/run/user/65534/bus", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/usr/lib", "/lib", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/opt/test-helper/bin/tester", "/.hakurei/test-helper", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r(ignore, "/run/user/65534/pulse/native", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - }, - - Seccomp: true, - - TrySocket: "/tmp/.X11-unix/X0", - ErrnoAbstract: syscall.ECONNREFUSED, - ErrnoPathname: syscall.ENOENT, -}.register("simple") diff --git a/test/sandbox/testdata/sum.go b/test/sandbox/testdata/sum.go deleted file mode 100644 index e4e8643a..00000000 --- a/test/sandbox/testdata/sum.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build testsuite || tester - -package testdata - -import ( - "crypto/sha512" - "encoding/base64" - "strconv" -) - -// sum decodes s as [base64.StdEncoding] and panics if it is invalid or -// unexpectedly sized. -func sum(s string) [sha512.Size]byte { - p, err := base64.StdEncoding.DecodeString(s) - if err != nil { - panic(err) - } - if len(p) != sha512.Size { - panic("unexpected checksum sized " + strconv.Itoa(len(p))) - } - return ([sha512.Size]byte)(p) -} diff --git a/test/sandbox/testdata/sum_amd64.go b/test/sandbox/testdata/sum_amd64.go deleted file mode 100644 index bd751105..00000000 --- a/test/sandbox/testdata/sum_amd64.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build testsuite || tester - -package testdata - -var ( - SumPD = sum("xpiwgf+Vev4XptlDdFN9N/KmP2+d112nVGVCQHqeMkduvaMxK6d4XX9hhUK8+vJ8on3MLd26hSBp0ovP6MrTmg==") - sumSimple = sum("6IApjfK9Z1HQBA/CG8DtTAD5XcDXulBsJE2LjPaGbbqO9KMylvKHtmzMwdeOlwJll/hMx97BVz4UiWD701zXNQ==") - sumTTY = sum("C3YAdHbByeJdv2dMKf32CaFlanAGPkkydlThtTYK09oG4aPjK/gOlhxVFq2D1Lnn6b3odqk3l+J2J9JVXCWFiw==") -) diff --git a/test/sandbox/testdata/sum_arm64.go b/test/sandbox/testdata/sum_arm64.go deleted file mode 100644 index 1691828f..00000000 --- a/test/sandbox/testdata/sum_arm64.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build testsuite || tester - -package testdata - -var ( - SumPD = sum("QzzpuREoLW3MgCkxn7ebgWtg1aeV7I/JQ0TdAnYU1o8CMWapG7iB+q7u3Sbj2JR04UHlppqX6TuJhMqPFJmZgA==") - sumSimple = sum("eTGFOKPchRMUtr2W8Q1YYayyqn4Ty43gYZ0PanZwnWfwHvP9Z+GVhisC+XEeW3abxNHrT8DfxBpyPInJaKkylw==") - sumTTY = sum("zx9NyHQ2uo7JXSaLZjpjl7sLSlrGTYVX5sxSnYsPb2Xa06krYu0p2F7unG3eEmd1ek0PhgMuikXKG86t+jTPXg==") -) diff --git a/test/sandbox/testdata/testdata.go b/test/sandbox/testdata/testdata.go deleted file mode 100644 index 3418d8ae..00000000 --- a/test/sandbox/testdata/testdata.go +++ /dev/null @@ -1,125 +0,0 @@ -//go:build testsuite || tester - -// Package testdata holds sandbox inspection test cases. -package testdata - -import ( - "crypto/sha512" - "iter" - "log" - "strconv" - "syscall" - - "hakurei.app/check" - "hakurei.app/fhs" - "hakurei.app/hst" - "hakurei.app/test/internal/mountinfo" - "hakurei.app/test/internal/testsuite" -) - -// A TestCase represents a named test case that may be requested by the caller. -type TestCase struct { - // Configuration of the inspected container. - Hakurei hst.Config - // Checksum of expected seccomp filter program. - Sum [sha512.Size]byte - - // Expected environment. Skipped if nil. - Env []string `json:"env,omitempty"` - // Expected root filesystem. Skipped if nil. - FS *testsuite.FS `json:"fs,omitempty"` - // Expected mountinfo records. Skipped if nil. - Mount []*mountinfo.Entry `json:"mount,omitempty"` - // Whether to run seccomp checks. - Seccomp bool `json:"seccomp,omitempty"` - - // Name of pathname and abstract sockets to attempt. - TrySocket string `json:"try_socket,omitempty"` - // Errno to expect attempting to reach the abstract socket. - ErrnoAbstract syscall.Errno `json:"errno_abstract,omitempty"` - // Errno to expect attempting to reach the pathname socket. - ErrnoPathname syscall.Errno `json:"errno_pathname,omitempty"` -} - -// testCases hold all named test cases. -var testCases map[string]TestCase - -// fc returns c wrapped in its JSON adapter. -func fc(c hst.FilesystemConfig) hst.FilesystemConfigJSON { - return hst.FilesystemConfigJSON{ - FilesystemConfig: c, - } -} - -// ignore is the magic string for a mountinfo field to be ignored. -const ignore = "//ignore" - -type dir = map[string]*testsuite.FS - -// r returns the address of a [mountinfo.Entry]. -func r( - root, target, vfsOptstr string, - fsType, source, fsOptstr string, -) *mountinfo.Entry { - return &mountinfo.Entry{ - ID: -1, - Parent: -1, - Root: root, - Target: target, - VfsOptstr: vfsOptstr, - FsType: fsType, - Source: source, - FsOptstr: fsOptstr, - } -} - -var ( - // fcLinker is the dynamic linker symlink. - fcLinker = fc(&hst.FSLink{ - Target: fhs.AbsRoot.Append("lib64", "ld-linux-x86-64.so.2"), - Linkname: "../lib/x86_64-linux-gnu/ld-linux-x86-64.so.2", - }) - // fcLib is the dynamic library bind mount. - fcLib = fc(&hst.FSBind{Source: fhs.AbsRoot.Append("lib")}) - - // absTestHelper is the absolute pathname of the test helper program. - absTestHelper = hst.AbsPrivateTmp.Append("test-helper") - // fcTestHelper is the test helper bind mount. - fcTestHelper = fc(&hst.FSBind{ - Target: absTestHelper, - Source: check.MustAbs("/opt/test-helper/bin/tester"), - }) -) - -// register adds a test case to testCases. -func (c TestCase) register(name string) (_ struct{}) { - if testCases == nil { - testCases = make(map[string]TestCase) - } - - if _, ok := testCases[name]; ok { - panic("attempting to register " + strconv.Quote(name) + " twice") - } - testCases[name] = c - return -} - -// Get returns the named test case, or terminates the program if name is invalid. -func Get(name string) TestCase { - tc, ok := testCases[name] - if !ok { - log.Fatalf("invalid test case %q", name) - } - return tc -} - -// All returns an iterator over all named test cases. -func All() iter.Seq2[string, TestCase] { - return func(yield func(string, TestCase) bool) { - for name, tc := range testCases { - if !yield(name, tc) { - return - } - } - } -} diff --git a/test/sandbox/testdata/tty.go b/test/sandbox/testdata/tty.go deleted file mode 100644 index 4788f484..00000000 --- a/test/sandbox/testdata/tty.go +++ /dev/null @@ -1,145 +0,0 @@ -//go:build testsuite || tester - -package testdata - -import ( - "os" - - "hakurei.app/fhs" - "hakurei.app/hst" - "hakurei.app/test/internal/mountinfo" - "hakurei.app/test/internal/testsuite" -) - -var _ = TestCase{ - Hakurei: hst.Config{ - ID: "app.hakurei.sample.tty", - Enablements: new(hst.EWayland | hst.EPipeWire | hst.EDBus | hst.EX11), - Identity: 2, - - Container: &hst.ContainerConfig{ - Hostname: "hakurei-sample-tty", - - Filesystem: []hst.FilesystemConfigJSON{ - fcLinker, - fcLib, - fcTestHelper, - }, - - Username: "u0_a2", - Shell: fhs.AbsUsrBin.Append("bash"), - Home: hst.AbsPrivateTmp, - Path: absTestHelper, - Args: []string{"tester", "tty"}, - - Flags: hst.FHostNet | hst.FHostAbstract | - hst.FTty | hst.FShareRuntime, - }, - }, - - // 0, PresetExt | PresetDenyNS | PresetDenyDevel - Sum: sumTTY, - - Env: []string{ - "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus", - "DISPLAY=:0", - "HOME=/.hakurei", - "SHELL=/usr/bin/bash", - "TERM=xterm", - "USER=u0_a2", - "WAYLAND_DISPLAY=wayland-0", - "XDG_RUNTIME_DIR=/run/user/65534", - "XDG_SESSION_CLASS=user", - "XDG_SESSION_TYPE=wayland", - "PULSE_SERVER=unix:/run/user/65534/pulse/native", - }, - - FS: &testsuite.FS{Dir: dir{ - ".hakurei": {Mode: os.ModeDir | 0755, Dir: dir{ - "test-helper": {Mode: 0755}, - }}, - - "dev": {Mode: os.ModeDir | 0755, Dir: dir{ - "core": {Mode: os.ModeSymlink | 0777}, - "fd": {Mode: os.ModeSymlink | 0777}, - "full": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - "mqueue": {Mode: os.ModeDir | os.ModeSticky | 0777, Dir: dir{}}, - "null": {Mode: os.ModeDevice | os.ModeCharDevice | 0666, Data: new("")}, - "ptmx": {Mode: os.ModeSymlink | 0777}, - "pts": {Mode: os.ModeDir | 0755, Dir: dir{ - "ptmx": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - }}, - "random": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - "shm": {Mode: os.ModeDir | os.ModeSticky | 0777, Dir: dir{}}, - "stderr": {Mode: os.ModeSymlink | 0777}, - "stdin": {Mode: os.ModeSymlink | 0777}, - "stdout": {Mode: os.ModeSymlink | 0777}, - "tty": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - "urandom": {Mode: os.ModeDevice | os.ModeCharDevice | 0444}, - "zero": {Mode: os.ModeDevice | os.ModeCharDevice | 0666}, - }}, - - "etc": {Mode: os.ModeDir | 0755, Dir: dir{ - "passwd": {Mode: 0600, - Data: new("u0_a2:x:65534:65534:Hakurei:/.hakurei:/usr/bin/bash\n")}, - "group": {Mode: 0600, - Data: new("hakurei:x:65534:\n")}, - }}, - - "lib64": {Mode: os.ModeDir | 0755, Dir: dir{ - "ld-linux-x86-64.so.2": {Mode: os.ModeSymlink | 0777}, - }}, - - "run": {Mode: os.ModeDir | 0755, Dir: dir{ - "user": {Mode: os.ModeDir | 0755, Dir: dir{ - "65534": {Mode: os.ModeDir | 0770, Dir: dir{ - "bus": {Mode: os.ModeSocket | 0775}, - "wayland-0": {Mode: os.ModeSocket | 070}, - "pulse": {Mode: os.ModeDir | 0700, Dir: dir{ - "native": {Mode: os.ModeSocket | 0777}, - }}, - }}, - }}, - }}, - - "tmp": {Mode: os.ModeDir | os.ModeSticky | 0777, Dir: dir{ - ".X11-unix": {Mode: os.ModeDir | 0755, Dir: dir{ - "X0": {Mode: os.ModeSocket | 0775}, - }}, - }}, - - "lib": {Mode: os.ModeDir | 0755}, - "proc": {Mode: os.ModeDir | 0555}, - }}, - - Mount: []*mountinfo.Entry{ - r("/sysroot", "/", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110001,gid=110001,inode64"), - r("/", "/proc", "rw,nosuid,nodev,noexec,relatime", "proc", "proc", "rw"), - r("/", "/.hakurei", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,size=4k,mode=755,uid=110001,gid=110001,inode64"), - r("/", "/dev", "ro,nosuid,nodev,relatime", "tmpfs", "devtmpfs", "rw,mode=755,uid=110001,gid=110001,inode64"), - r("/null", "/dev/null", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/zero", "/dev/zero", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/full", "/dev/full", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/random", "/dev/random", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/urandom", "/dev/urandom", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/tty", "/dev/tty", "rw,nosuid,noexec,relatime", "devtmpfs", "devtmpfs", ignore), - r("/", "/dev/pts", "rw,nosuid,noexec,relatime", "devpts", "devpts", "rw,mode=620,ptmxmode=666"), - r("/", "/dev/mqueue", "rw,nosuid,nodev,noexec,relatime", "mqueue", "mqueue", "rw"), - r("/", "/dev/shm", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,uid=110001,gid=110001,inode64"), - r("/", "/run/user", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,size=16384k,mode=755,uid=110001,gid=110001,inode64"), - r("/tmp/hakurei.0/runtime/2", "/run/user/65534", "rw,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/", "/tmp", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", "rw,uid=110001,gid=110001,inode64"), - r(ignore, "/etc/passwd", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110001,gid=110001,inode64"), - r(ignore, "/etc/group", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", "rw,uid=110001,gid=110001,inode64"), - r(ignore, "/run/user/65534/wayland-0", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/tmp/.X11-unix", "/tmp/.X11-unix", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r(ignore, "/run/user/65534/bus", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/usr/lib", "/lib", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r("/opt/test-helper/bin/tester", "/.hakurei/test-helper", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - r(ignore, "/run/user/65534/pulse/native", "ro,nosuid,nodev,relatime", "overlay", "overlay", ignore), - }, - - Seccomp: true, - - TrySocket: "/tmp/.X11-unix/X0", -}.register("tty") diff --git a/test/sandbox/tester/main.go b/test/sandbox/tester/main.go deleted file mode 100644 index 0782a5e0..00000000 --- a/test/sandbox/tester/main.go +++ /dev/null @@ -1,224 +0,0 @@ -//go:build tester - -// The sandbox tester runs within a cmd/hakurei container and validates its -// state. Since the test environment is relatively predictable, the tester can -// make various assumptions about the host. -package main - -import ( - "errors" - "log" - "net" - "os" - "os/signal" - "path/filepath" - "syscall" - - "hakurei.app/test/internal/mountinfo" - "hakurei.app/test/sandbox/testdata" -) - -//#include <sys/quota.h> -import "C" - -// mustAbs returns s, or terminates the program if s is not absolute. -func mustAbs(s string) string { - if !filepath.IsAbs(s) { - log.Fatalf("%q is not absolute", s) - } - return s -} - -func main() { - log.SetFlags(0) - log.SetPrefix("tester: ") - - if len(os.Args) != 2 { - log.Fatal("tester requires 1 argument") - } - want := testdata.Get(os.Args[1]) - log.SetPrefix("tester: " + os.Args[1] + " ") - - checkWritableDirPaths := []string{ - "/dev/shm", - "/tmp", - os.Getenv("XDG_RUNTIME_DIR"), - } - for _, a := range checkWritableDirPaths { - pathname := filepath.Join(mustAbs(a), ".hakurei-check") - if err := os.WriteFile(pathname, make([]byte, 1<<8), 0600); err != nil { - log.Fatalf("[FAIL] %s", err) - } else if err = os.Remove(pathname); err != nil { - log.Fatalf("[FAIL] %s", err) - } else { - log.Printf("[ OK ] %s is writable", a) - } - } - - if want.Env != nil { - var ( - fail bool - i int - got string - ) - for i, got = range os.Environ() { - if i == len(want.Env) { - log.Fatalf("got more than %d environment variables", len(want.Env)) - } - if got != want.Env[i] { - fail = true - log.Printf("[FAIL] %s", got) - } else { - log.Printf("[ OK ] %s", got) - } - } - - i++ - if i != len(want.Env) { - log.Fatalf("got %d environment variables, want %d", i, len(want.Env)) - } - - if fail { - log.Fatalf("[FAIL] some environment variables did not match") - } - } else { - log.Printf("[SKIP] skipping environ check") - } - - if want.FS != nil { - if err := want.FS.Compare(log.Printf, ".", os.DirFS("/")); err != nil { - log.Fatalf("%v", err) - } - } else { - log.Printf("[SKIP] skipping fs check") - } - - if want.Mount != nil { - var fail bool - - m, err := mountinfo.Open("") - if err != nil { - log.Fatal(err) - } - - i := 0 - var ent mountinfo.Entry - for m.Next() { - m.Copy(&ent) - - if i == len(want.Mount) { - log.Fatalf("got more than %d entries", i) - } - if !ent.EqualWithIgnore(want.Mount[i], "//ignore") { - fail = true - log.Printf("[FAIL] %s", &ent) - } else { - log.Printf("[ OK ] %s", &ent) - } - - i++ - } - if err = m.Err(); err != nil { - log.Fatalf("%v", err) - } - - if i != len(want.Mount) { - log.Fatalf("got %d entries, want %d", i, len(want.Mount)) - } - - if fail { - log.Fatalf("[FAIL] some mount points did not match") - } - } else { - log.Printf("[SKIP] skipping mounts check") - } - - if want.Seccomp { - const NULL = 0 - - for _, tc := range []struct { - name string - errno syscall.Errno - - trap, a1, a2, a3, a4, a5, a6 uintptr - }{ - {"syslog", syscall.EPERM, syscall.SYS_SYSLOG, 0, NULL, NULL, NULL, NULL, NULL}, - {"acct", syscall.EPERM, syscall.SYS_ACCT, 0, NULL, NULL, NULL, NULL, NULL}, - {"quotactl", syscall.EPERM, syscall.SYS_QUOTACTL, C.Q_GETQUOTA, NULL, uintptr(os.Getuid()), NULL, NULL, NULL}, - {"add_key", syscall.EPERM, syscall.SYS_ADD_KEY, NULL, NULL, NULL, NULL, NULL, NULL}, - {"keyctl", syscall.EPERM, syscall.SYS_KEYCTL, NULL, NULL, NULL, NULL, NULL, NULL}, - {"request_key", syscall.EPERM, syscall.SYS_REQUEST_KEY, NULL, NULL, NULL, NULL, NULL, NULL}, - {"move_pages", syscall.EPERM, syscall.SYS_MOVE_PAGES, uintptr(os.Getpid()), NULL, NULL, NULL, NULL, NULL}, - {"mbind", syscall.EPERM, syscall.SYS_MBIND, NULL, NULL, NULL, NULL, NULL, NULL}, - {"get_mempolicy", syscall.EPERM, syscall.SYS_GET_MEMPOLICY, NULL, NULL, NULL, NULL, NULL, NULL}, - {"set_mempolicy", syscall.EPERM, syscall.SYS_SET_MEMPOLICY, NULL, NULL, NULL, NULL, NULL, NULL}, - {"migrate_pages", syscall.EPERM, syscall.SYS_MIGRATE_PAGES, NULL, NULL, NULL, NULL, NULL, NULL}, - } { - if _, _, errno := syscall.Syscall6(tc.trap, tc.a1, tc.a2, tc.a3, tc.a4, tc.a5, tc.a6); errno != tc.errno { - log.Fatalf("[FAIL] %s: %v, want %v", tc.name, errno, tc.errno) - } - log.Printf("[ OK ] %s: %v", tc.name, tc.errno) - } - } else { - log.Printf("[SKIP] skipping seccomp check") - } - - if want.TrySocket != "" { - retry: - abstractConn, abstractErr := net.Dial("unix", "@"+want.TrySocket) - pathnameConn, pathnameErr := net.Dial("unix", want.TrySocket) - ok := true - - if abstractErr == nil { - if err := abstractConn.Close(); err != nil { - ok = false - log.Printf("Close: %v", err) - } - } - if pathnameErr == nil { - if err := pathnameConn.Close(); err != nil { - ok = false - log.Printf("Close: %v", err) - } - } - - if errors.Is( - abstractErr, - syscall.EAGAIN, - ) || errors.Is( - pathnameErr, - syscall.EAGAIN, - ) { - goto retry - } - - abstractWantErr := error(want.ErrnoAbstract) - pathnameWantErr := error(want.ErrnoPathname) - if want.ErrnoAbstract == 0 { - abstractWantErr = nil - } - if want.ErrnoPathname == 0 { - pathnameWantErr = nil - } - - if !errors.Is(abstractErr, abstractWantErr) { - ok = false - log.Printf("abstractErr: %v, want %v", abstractErr, abstractWantErr) - } - if !errors.Is(pathnameErr, pathnameWantErr) { - ok = false - log.Printf("pathnameErr: %v, want %v", pathnameErr, pathnameWantErr) - } - - if !ok { - os.Exit(1) - } - } - - s := make(chan os.Signal, 1) - signal.Notify(s, syscall.SIGTERM) - if _, err := os.Stdout.Write(make([]byte, 8)); err != nil { - log.Fatalf("cannot notify testsuite: %v", err) - } - <-s -} |
