aboutsummaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-10-04 01:05:10 +0900
committerOphestra <cat@gensokyo.uk>2026-10-06 19:41:06 +0900
commita7383510fb05abc98b992240cb76ad4b6c598956 (patch)
tree90881e9d6952e050128f1378d66452c7d733d012 /test
parentb452e1047ccd3e1826da16416430e6638270155b (diff)
test/sandbox: migrate tests
This significantly improves performance, removing overhead of nix, python, and virtualisation. Running this in an unprivileged container required patching the kernel, but since special runner setup was already needed, that was an acceptable tradeoff. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'test')
-rw-r--r--test/flake.nix6
-rw-r--r--test/internal/sandbox/assert.go247
-rw-r--r--test/internal/sandbox/assert_test.go34
-rw-r--r--test/internal/sandbox/seccomp.go46
-rw-r--r--test/internal/testsuite/proc.go23
-rw-r--r--test/internal/testsuite/ptrace.go44
-rw-r--r--test/internal/testsuite/testsuite.go231
-rw-r--r--test/sandbox/case/default.nix97
-rw-r--r--test/sandbox/case/device.nix255
-rw-r--r--test/sandbox/case/mapuid.nix282
-rw-r--r--test/sandbox/case/pd.nix206
-rw-r--r--test/sandbox/case/pdlike.nix277
-rw-r--r--test/sandbox/case/preset.nix274
-rw-r--r--test/sandbox/case/tty.nix288
-rw-r--r--test/sandbox/configuration.nix113
-rw-r--r--test/sandbox/default.nix41
-rw-r--r--test/sandbox/main.go410
-rw-r--r--test/sandbox/seccomp.patch18
-rw-r--r--test/sandbox/test.py88
-rw-r--r--test/sandbox/testdata/device.go134
-rw-r--r--test/sandbox/testdata/mapuid.go124
-rw-r--r--test/sandbox/testdata/pdlike.go141
-rw-r--r--test/sandbox/testdata/simple.go140
-rw-r--r--test/sandbox/testdata/sum.go22
-rw-r--r--test/sandbox/testdata/sum_amd64.go9
-rw-r--r--test/sandbox/testdata/sum_arm64.go9
-rw-r--r--test/sandbox/testdata/testdata.go125
-rw-r--r--test/sandbox/testdata/tty.go145
-rw-r--r--test/sandbox/tester/main.go224
-rw-r--r--test/sandbox/tool/main.go106
-rw-r--r--test/sandbox/tool/package.nix32
31 files changed, 1771 insertions, 2420 deletions
diff --git a/test/flake.nix b/test/flake.nix
index a73ab03b..9b18c9b6 100644
--- a/test/flake.nix
+++ b/test/flake.nix
@@ -46,12 +46,6 @@
inherit system self;
withRace = true;
};
-
- sandbox = callPackage ./sandbox { inherit self; };
- sandbox-race = callPackage ./sandbox {
- inherit self;
- withRace = true;
- };
}
);
diff --git a/test/internal/sandbox/assert.go b/test/internal/sandbox/assert.go
deleted file mode 100644
index 1194befb..00000000
--- a/test/internal/sandbox/assert.go
+++ /dev/null
@@ -1,247 +0,0 @@
-//go:build testtool
-
-// Package sandbox provides utilities for checking sandbox outcome.
-//
-// This package must never be used outside integration tests, there is a much
-// better native implementation of mountinfo in the public sandbox/vfs package.
-// Files in this package are excluded by the build system to prevent accidental
-// misuse.
-package sandbox
-
-import (
- "encoding/json"
- "errors"
- "io/fs"
- "log"
- "net"
- "os"
- "path/filepath"
- "syscall"
-
- "hakurei.app/test/internal/mountinfo"
- "hakurei.app/test/internal/testsuite"
-)
-
-var (
- assert = log.New(os.Stderr, "sandbox: ", 0)
- printfFunc = assert.Printf
- fatalfFunc = assert.Fatalf
-)
-
-func printf(format string, v ...any) { printfFunc(format, v...) }
-func fatalf(format string, v ...any) { fatalfFunc(format, v...) }
-
-type TestCase struct {
- Env []string `json:"env"`
- FS *testsuite.FS `json:"fs"`
- Mount []*mountinfo.Entry `json:"mount"`
- Seccomp bool `json:"seccomp"`
-
- TrySocket string `json:"try_socket,omitempty"`
- SocketAbstract bool `json:"socket_abstract,omitempty"`
- SocketPathname bool `json:"socket_pathname,omitempty"`
-}
-
-type T struct {
- FS fs.FS
-
- MountsPath string
-}
-
-func (t *T) MustCheckFile(wantFilePath string) {
- var want *TestCase
- mustDecode(wantFilePath, &want)
- t.MustCheck(want)
-}
-
-func mustAbs(s string) string {
- if !filepath.IsAbs(s) {
- fatalf("[FAIL] %q is not absolute", s)
- panic("unreachable")
- }
- return s
-}
-
-func (t *T) MustCheck(want *TestCase) {
- 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 {
- fatalf("[FAIL] %s", err)
- } else if err = os.Remove(pathname); err != nil {
- fatalf("[FAIL] %s", err)
- } else {
- 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) {
- fatalf("got more than %d environment variables", len(want.Env))
- }
- if got != want.Env[i] {
- fail = true
- printf("[FAIL] %s", got)
- } else {
- printf("[ OK ] %s", got)
- }
- }
-
- i++
- if i != len(want.Env) {
- fatalf("got %d environment variables, want %d", i, len(want.Env))
- }
-
- if fail {
- fatalf("[FAIL] some environment variables did not match")
- }
- } else {
- printf("[SKIP] skipping environ check")
- }
-
- if want.FS != nil && t.FS != nil {
- if err := want.FS.Compare(printfFunc, ".", t.FS); err != nil {
- fatalf("%v", err)
- }
- } else {
- printf("[SKIP] skipping fs check")
- }
-
- if want.Mount != nil {
- var fail bool
- m := mustParseMountinfo(t.MountsPath)
- i := 0
- var ent mountinfo.Entry
- for m.Next() {
- m.Copy(&ent)
-
- if i == len(want.Mount) {
- fatalf("got more than %d entries", i)
- }
- if !ent.EqualWithIgnore(want.Mount[i], "//ignore") {
- fail = true
- printf("[FAIL] %s", &ent)
- } else {
- printf("[ OK ] %s", &ent)
- }
-
- i++
- }
- if err := m.Err(); err != nil {
- fatalf("%v", err)
- }
-
- if i != len(want.Mount) {
- fatalf("got %d entries, want %d", i, len(want.Mount))
- }
-
- if fail {
- fatalf("[FAIL] some mount points did not match")
- }
- } else {
- printf("[SKIP] skipping mounts check")
- }
-
- if want.Seccomp {
- if trySyscalls() != nil {
- os.Exit(1)
- }
- } else {
- printf("[SKIP] skipping seccomp check")
- }
-
- if want.TrySocket != "" {
- 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)
- }
- }
-
- abstractWantErr := error(syscall.EPERM)
- pathnameWantErr := error(syscall.ENOENT)
- if want.SocketAbstract {
- abstractWantErr = nil
- }
- if want.SocketPathname {
- 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)
- }
- }
-}
-
-func MustCheckFilter(pid int, want string) {
- err := testsuite.CheckFilter(pid, 0, want)
- if err == nil {
- return
- }
-
- e, ok := errors.AsType[*os.SyscallError](err)
- if !ok {
- fatalf("%s", err)
- }
- switch e.Syscall {
- case "PTRACE_ATTACH":
- fatalf("cannot attach to process %d: %v", pid, err)
- case "PTRACE_SECCOMP_GET_FILTER":
- if errors.Is(e.Err, syscall.ENOENT) {
- fatalf("seccomp filter not installed for process %d", pid)
- }
- fatalf("cannot get filter: %v", err)
- default:
- fatalf("cannot check filter: %v", err)
- }
-
- *(*int)(nil) = 0 // not reached
-}
-
-func mustDecode(wantFilePath string, v any) {
- if f, err := os.Open(wantFilePath); err != nil {
- fatalf("cannot open %q: %v", wantFilePath, err)
- } else if err = json.NewDecoder(f).Decode(v); err != nil {
- fatalf("cannot decode %q: %v", wantFilePath, err)
- } else if err = f.Close(); err != nil {
- fatalf("cannot close %q: %v", wantFilePath, err)
- }
-}
-
-func mustParseMountinfo(name string) *mountinfo.Iter {
- m, err := mountinfo.Open(name)
- if err != nil {
- fatalf("%v", err)
- panic("unreachable")
- }
- return m
-}
diff --git a/test/internal/sandbox/assert_test.go b/test/internal/sandbox/assert_test.go
deleted file mode 100644
index 012ae23d..00000000
--- a/test/internal/sandbox/assert_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-//go:build testtool
-
-package sandbox
-
-import (
- "encoding/json"
- "os"
- "path/filepath"
- "testing"
-)
-
-type F func(format string, v ...any)
-
-func SwapPrint(f F) (old F) { old = printfFunc; printfFunc = f; return }
-func SwapFatal(f F) (old F) { old = fatalfFunc; fatalfFunc = f; return }
-
-func MustWantFile(t *testing.T, v any) (wantFile string) {
- wantFile = filepath.Join(t.TempDir(), "want.json")
- if f, err := os.OpenFile(wantFile, os.O_CREATE|os.O_WRONLY, 0400); err != nil {
- t.Fatalf("cannot create %q: %v", wantFile, err)
- } else if err = json.NewEncoder(f).Encode(v); err != nil {
- t.Fatalf("cannot encode to %q: %v", wantFile, err)
- } else if err = f.Close(); err != nil {
- t.Fatalf("cannot close %q: %v", wantFile, err)
- }
-
- t.Cleanup(func() {
- if err := os.Remove(wantFile); err != nil {
- t.Fatalf("cannot remove %q: %v", wantFile, err)
- }
- })
-
- return
-}
diff --git a/test/internal/sandbox/seccomp.go b/test/internal/sandbox/seccomp.go
deleted file mode 100644
index 1d8cd457..00000000
--- a/test/internal/sandbox/seccomp.go
+++ /dev/null
@@ -1,46 +0,0 @@
-//go:build testtool
-
-package sandbox
-
-import (
- "os"
- "syscall"
-)
-
-/*
-#include <sys/quota.h>
-*/
-import "C"
-
-const NULL = 0
-
-func trySyscalls() error {
- testCases := []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},
- }
-
- for _, tc := range testCases {
- if _, _, errno := syscall.Syscall6(tc.trap, tc.a1, tc.a2, tc.a3, tc.a4, tc.a5, tc.a6); errno != tc.errno {
- printf("[FAIL] %s: %v, want %v", tc.name, errno, tc.errno)
- return errno
- }
- printf("[ OK ] %s: %v", tc.name, tc.errno)
- }
-
- return nil
-}
diff --git a/test/internal/testsuite/proc.go b/test/internal/testsuite/proc.go
index f2ad8857..e7ef1aed 100644
--- a/test/internal/testsuite/proc.go
+++ b/test/internal/testsuite/proc.go
@@ -10,6 +10,8 @@ import (
"strings"
"syscall"
"unsafe"
+
+ "hakurei.app/fhs"
)
// Stat represents status information read from /proc/pid/stat.
@@ -144,13 +146,9 @@ type Stat struct {
CGuestTime int
}
-// fhsProc points to a virtual kernel file system exposing the process list and
-// other functionality.
-const fhsProc = "/proc/"
-
// Executable is like [os.Executable], but for the process referred to by s.
func (s *Stat) Executable() (string, error) {
- path, err := os.Readlink(filepath.Join(fhsProc, strconv.Itoa(s.PID), "exe"))
+ path, err := os.Readlink(filepath.Join(fhs.Proc, strconv.Itoa(s.PID), "exe"))
// When the executable has been deleted then Readlink returns a
// path appended with " (deleted)".
@@ -159,7 +157,7 @@ func (s *Stat) Executable() (string, error) {
// Stat populates stat with the proc filesystem entry referred to by s.
func (s *Stat) Stat(stat *syscall.Stat_t) (err error) {
- err = syscall.Stat(filepath.Join(fhsProc, strconv.Itoa(s.PID)), stat)
+ err = syscall.Stat(filepath.Join(fhs.Proc, strconv.Itoa(s.PID)), stat)
if err != nil {
err = os.NewSyscallError("stat", err)
}
@@ -168,7 +166,7 @@ func (s *Stat) Stat(stat *syscall.Stat_t) (err error) {
// Args reads arguments of the process referred to by s.
func (s *Stat) Args() ([]string, error) {
- p, err := os.ReadFile(filepath.Join(fhsProc, strconv.Itoa(s.PID), "cmdline"))
+ p, err := os.ReadFile(filepath.Join(fhs.Proc, strconv.Itoa(s.PID), "cmdline"))
if err != nil {
return nil, err
}
@@ -286,6 +284,11 @@ type StatScanner struct {
err error
}
+// IsNotExist returns whether an error is [os.ErrNotExist] or ESRCH.
+func IsNotExist(err error) bool {
+ return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ESRCH)
+}
+
// Scan reads a process status information entry. It returns false if an
// unrecoverable error is encountered, after which Scan no longer scans new
// entries.
@@ -295,7 +298,7 @@ func (s *StatScanner) Scan() bool {
}
if s.wrapped = s.i == len(s.dents); s.wrapped {
- if s.dents, s.err = os.ReadDir(fhsProc); s.err != nil {
+ if s.dents, s.err = os.ReadDir(fhs.Proc); s.err != nil {
return false
}
s.i = 0
@@ -318,9 +321,9 @@ func (s *StatScanner) Scan() bool {
}
var p []byte
- p, err = os.ReadFile(filepath.Join(fhsProc, dent.Name(), "stat"))
+ p, err = os.ReadFile(filepath.Join(fhs.Proc, dent.Name(), "stat"))
if err != nil {
- if errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ESRCH) {
+ if IsNotExist(err) {
continue
}
s.err = err
diff --git a/test/internal/testsuite/ptrace.go b/test/internal/testsuite/ptrace.go
index 4fcf1508..ccf0900c 100644
--- a/test/internal/testsuite/ptrace.go
+++ b/test/internal/testsuite/ptrace.go
@@ -2,7 +2,7 @@ package testsuite
import (
"crypto/sha512"
- "encoding/hex"
+ "encoding/base64"
"errors"
"fmt"
"os"
@@ -58,10 +58,26 @@ func ptraceAttach(pid int) error {
}
return os.NewSyscallError("wait4", err)
}
- break
- }
+ switch {
+ case status.Stopped():
+ return nil
- return nil
+ case status.Continued():
+ continue
+
+ case status.Signaled():
+ return fmt.Errorf(
+ "tracee terminated by signal %s",
+ status.Signal(),
+ )
+
+ case status.Exited():
+ return fmt.Errorf(
+ "tracee terminated unexpectedly with code %d",
+ status.ExitStatus(),
+ )
+ }
+ }
}
// ptraceDetach detaches from the attached process referred to by pid.
@@ -95,8 +111,8 @@ func getFilter(pid, index int) ([]syscall.SockFilter, error) {
}
// CheckFilter checks the process at pid to have its first filter's contents
-// match the sha512 checksum specified in hexadecimal string representation.
-func CheckFilter(pid, index int, sum string) (err error) {
+// match the specified sha512 checksum.
+func CheckFilter(pid, index int, sum [sha512.Size]byte) (err error) {
if err = ptraceAttach(pid); err != nil {
return
}
@@ -106,15 +122,7 @@ func CheckFilter(pid, index int, sum string) (err error) {
}
}()
- var (
- buf []syscall.SockFilter
- want []byte
- )
-
- if want, err = hex.DecodeString(sum); err != nil {
- return
- }
-
+ var buf []syscall.SockFilter
h := sha512.New()
if buf, err = getFilter(pid, index); err != nil {
return
@@ -125,11 +133,11 @@ func CheckFilter(pid, index int, sum string) (err error) {
))
}
- if got := h.Sum(nil); string(got) != string(want) {
+ if got := h.Sum(nil); string(got) != string(sum[:]) {
return fmt.Errorf(
"bad filter\n\t got: %s\n\twant: %s",
- hex.EncodeToString(got),
- sum,
+ base64.StdEncoding.EncodeToString(got),
+ base64.StdEncoding.EncodeToString(sum[:]),
)
}
return
diff --git a/test/internal/testsuite/testsuite.go b/test/internal/testsuite/testsuite.go
index 6b4cd717..00eb2f92 100644
--- a/test/internal/testsuite/testsuite.go
+++ b/test/internal/testsuite/testsuite.go
@@ -5,12 +5,19 @@
package testsuite
import (
+ "bufio"
+ "context"
+ "crypto/sha512"
+ "errors"
"log"
"os"
"os/exec"
"os/signal"
"os/user"
+ "strconv"
+ "sync"
"syscall"
+ "time"
)
// ReceiveSignals blocks until a termination signal arrives, and terminates.
@@ -39,7 +46,231 @@ func MustRun(command ...string) {
}
}
+// ErrUnexpectedSuccess is returned for processes expected to exit with a
+// non-zero code, but failed to do so.
+var ErrUnexpectedSuccess = errors.New("process unexpectedly exited with code 0")
+
+// MustFail runs command and terminates the testsuite if the program fails to
+// start or exits with code 0.
+func MustFail(command ...string) {
+ cmd := exec.Command(command[0], command[1:]...)
+ cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
+ if err := cmd.Run(); err == nil {
+ log.Fatal(ErrUnexpectedSuccess)
+ } else if e, ok := errors.AsType[*exec.ExitError](err); !ok {
+ log.Fatal(err)
+ } else if !e.Exited() {
+ log.Fatal(e)
+ }
+}
+
// MustRunAs wraps [MustRun] for sudo.
func MustRunAs(username string, command ...string) {
MustRun(append([]string{"sudo", "-u", username}, command...)...)
}
+
+// MustFailAs wraps [MustFail] for sudo.
+func MustFailAs(username string, command ...string) {
+ MustFail(append([]string{"sudo", "-u", username}, command...)...)
+}
+
+// MustStart starts cmd and returns a channel delivering its wait error.
+func MustStart(cmd *exec.Cmd) (done <-chan error) {
+ if err := cmd.Start(); err != nil {
+ log.Fatal(err)
+ }
+ d := make(chan error)
+ go func() { d <- cmd.Wait() }()
+ return d
+}
+
+// MustStartAs wraps [MustStart] for sudo.
+func MustStartAs(
+ ctx context.Context,
+ username string,
+ files []*os.File,
+ command ...string,
+) (proc *os.Process, done <-chan error) {
+ sudoArgs := []string{
+ "-u", username,
+ }
+ if len(files) != 0 {
+ sudoArgs = append(sudoArgs, "-C", strconv.Itoa(len(files)+4))
+ }
+ sudoArgs = append(sudoArgs, "--")
+ cmd := exec.CommandContext(ctx, "sudo", append(sudoArgs, command...)...)
+ cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
+ cmd.ExtraFiles = files
+ cmd.SysProcAttr = &syscall.SysProcAttr{Pdeathsig: syscall.SIGTERM}
+ return cmd.Process, MustStart(cmd)
+}
+
+// MustCheckFilter is like [CheckFilter], but terminates the test suite if a
+// non-nil error is returned. Otherwise, the tracee is terminated after it
+// resumes.
+func MustCheckFilter(pid int, sum [sha512.Size]byte) {
+ // podman installs its own filter
+ if err := CheckFilter(pid, 1, sum); err != nil {
+ log.Fatal(err)
+ } else if err = syscall.Kill(pid, syscall.SIGTERM); err != nil {
+ log.Fatalf("cannot terminate tracee: %v", err)
+ }
+}
+
+// FilterTerminated returns a non-nil error if err is not an [exec.ExitError]
+// describing a process terminated by a syscall.SIGTERM signal.
+func FilterTerminated(err error) error {
+ if err == nil {
+ return ErrUnexpectedSuccess
+ }
+
+ e, ok := errors.AsType[*exec.ExitError](err)
+ if !ok {
+ return err
+ }
+
+ if e.ExitCode() == 0x80+int(syscall.SIGTERM) {
+ return nil
+ }
+ return e
+}
+
+// Poll repeatedly runs command until it succeeds.
+func Poll(d time.Duration, command ...string) {
+ for range time.NewTicker(d).C {
+ cmd := exec.Command(command[0], command[1:]...)
+ if err := cmd.Run(); err != nil {
+ if e, ok := errors.AsType[*exec.ExitError](err); ok && e.Exited() {
+ continue
+ }
+ log.Fatal(err)
+ }
+ break
+ }
+}
+
+const (
+ // XDGRuntimeDir is the hardcoded XDG runtime directory for the user
+ // described by [GetUser].
+ XDGRuntimeDir = "/var/run/user/1000"
+
+ // XDGRuntimeEnv is the environment variable string for XDG_RUNTIME_DIR.
+ XDGRuntimeEnv = "XDG_RUNTIME_DIR=" + XDGRuntimeDir
+)
+
+// MustStartSessionBus starts a session bus that is never explicitly terminated.
+// The test suite is terminated if the session bus daemon terminates.
+func MustStartSessionBus(username string) (dbusEnv string) {
+ r, w, err := os.Pipe()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // this is never explicitly terminated
+ _, done := MustStartAs(
+ context.Background(), username, []*os.File{w},
+ "dbus-daemon",
+ "--print-address=3",
+ "--address=unix:path="+XDGRuntimeDir+"/dbus",
+ "--session",
+ "--nofork",
+ "--nopidfile",
+ )
+
+ go func() {
+ if _err := <-done; _err != nil {
+ log.Fatal(_err)
+ }
+ log.Fatal("session bus terminated unexpectedly")
+ }()
+
+ dbusEnv, err = bufio.NewReader(r).ReadString('\n')
+ if err != nil {
+ log.Fatal(err)
+ }
+ dbusEnv = dbusEnv[:len(dbusEnv)-1]
+ log.Printf("dbus listening on %s", dbusEnv)
+ dbusEnv = "DBUS_SESSION_BUS_ADDRESS=" + dbusEnv
+
+ if err = r.Close(); err != nil {
+ log.Fatal(err)
+ }
+ return
+}
+
+const (
+ // SwayEnv is the environment variable string for the sway IPC socket.
+ SwayEnv = "SWAYSOCK=" + XDGRuntimeDir + "/sway"
+ // WaylandEnv is the environment variable string for the wayland display.
+ WaylandEnv = "WAYLAND_DISPLAY=wayland-1"
+)
+
+// MustStartSway starts the sway wayland display server which must be terminated
+// by calling [TerminateSway].
+func MustStartSway(
+ wg *sync.WaitGroup,
+ username, dbusEnv string,
+) {
+ wg.Go(func() {
+ // this is terminated via swaymsg
+ _, done := MustStartAs(
+ context.Background(), username, nil, "env",
+ "WLR_BACKENDS=headless",
+ XDGRuntimeEnv,
+ SwayEnv,
+ dbusEnv,
+ "sway",
+ )
+ if err := <-done; err != nil {
+ log.Fatal(err)
+ }
+ })
+
+ Poll(50*time.Millisecond, "sudo", "-u", username, SwayEnv, "swaymsg")
+ log.Printf("sway available via %s", SwayEnv)
+}
+
+// TerminateSway requests for the sway server to terminate via sway IPC.
+func TerminateSway(username string) {
+ MustFailAs(username, SwayEnv, "swaymsg", "exit")
+}
+
+// MustStartPipeWire starts a PipeWire server that is never explicitly
+// terminated. The test suite is terminated if the PipeWire server terminates.
+func MustStartPipeWire(username, dbusEnv string) {
+ // this is never explicitly terminated
+ _, done := MustStartAs(
+ context.Background(), username, nil, "env",
+ XDGRuntimeEnv,
+ dbusEnv,
+ "pipewire",
+ )
+
+ go func() {
+ if _err := <-done; _err != nil {
+ log.Fatal(_err)
+ }
+ log.Fatal("pipewire terminated unexpectedly")
+ }()
+
+ Poll(50*time.Millisecond, "sudo", "-u", username,
+ XDGRuntimeEnv,
+ dbusEnv,
+ "wpctl",
+ "status",
+ )
+
+ _, _done := MustStartAs(
+ context.Background(), username, nil, "env",
+ XDGRuntimeEnv,
+ dbusEnv,
+ "wireplumber",
+ )
+
+ go func() {
+ if _err := <-_done; _err != nil {
+ log.Fatal(_err)
+ }
+ log.Fatal("wireplumber terminated unexpectedly")
+ }()
+}
diff --git a/test/sandbox/case/default.nix b/test/sandbox/case/default.nix
deleted file mode 100644
index 337f4bf2..00000000
--- a/test/sandbox/case/default.nix
+++ /dev/null
@@ -1,97 +0,0 @@
-system: lib: testProgram:
-let
- fs = mode: dir: data: {
- mode = lib.fromHexString mode;
- inherit
- dir
- data
- ;
- };
-
- ignore = "//ignore";
-
- ent = root: target: vfs_optstr: fstype: source: fs_optstr: {
- id = -1;
- parent = -1;
- inherit
- root
- target
- vfs_optstr
- fstype
- source
- fs_optstr
- ;
- };
-
- importTestCase =
- path:
- import path {
- inherit
- fs
- ent
- ignore
- system
- ;
- };
-
- callTestCase =
- path: identity:
- let
- tc = importTestCase path;
- in
- {
- name = "check-sandbox-${tc.name}";
- inherit identity;
- verbose = true;
- inherit (tc)
- tty
- device
- mapRealUid
- useCommonPaths
- userns
- hostAbstract
- shareRuntime
- shareTmpdir
- ;
- enablements = {
- inherit (tc) x11;
- };
- share = testProgram;
- packages = [ ];
- path = "${testProgram}/bin/hakurei-test";
- args = [
- "hakurei-test"
- "-p"
- "/var/tmp/.hakurei-check-ok.${toString identity}"
- "-t"
- (toString (builtins.toFile "hakurei-${tc.name}-want.json" (builtins.toJSON tc.want)))
- "-s"
- tc.expectedFilter.${system}
- ];
-
- extraPaths =
- if tc.useCommonPaths then
- [ ]
- else
- [
- {
- type = "bind";
- src = "/var/tmp";
- write = true;
- }
- ];
- };
-
- testCaseName = name: "cat.gensokyo.hakurei.test." + name;
-in
-{
- apps = {
- ${testCaseName "preset"} = callTestCase ./preset.nix 1;
- ${testCaseName "tty"} = callTestCase ./tty.nix 2;
- ${testCaseName "mapuid"} = callTestCase ./mapuid.nix 3;
- ${testCaseName "device"} = callTestCase ./device.nix 4;
- ${testCaseName "pdlike"} = callTestCase ./pdlike.nix 5;
- };
-
- pd = importTestCase ./pd.nix;
-}
diff --git a/test/sandbox/case/device.nix b/test/sandbox/case/device.nix
deleted file mode 100644
index 889e0a06..00000000
--- a/test/sandbox/case/device.nix
+++ /dev/null
@@ -1,255 +0,0 @@
-{
- fs,
- ent,
- ignore,
- system,
-}:
-let
- extraPaths = {
- x86_64-linux = {
- fd = "fd0";
- sr = {
- sr0 = fs "80001ff" null null;
- };
- };
- aarch64-linux = {
- fd = "mtdblock0";
- sr = { };
- };
- };
-in
-{
- name = "device";
- tty = false;
- device = true;
- mapRealUid = false;
- useCommonPaths = true;
- userns = false;
- x11 = true;
- hostAbstract = false;
- shareRuntime = false;
- shareTmpdir = true;
-
- # 0, PresetStrict
- expectedFilter = {
- x86_64-linux = "e880298df2bd6751d0040fc21bc0ed4c00f95dc0d7ba506c244d8b8cf6866dba8ef4a33296f287b66cccc1d78e97026597f84cc7dec1573e148960fbd35cd735";
- aarch64-linux = "79318538a3dc851314b6bd96f10d5861acb2aa7e13cb8de0619d0f6a76709d67f01ef3fd67e195862b02f9711e5b769bc4d1eb4fc0dfc41a723c89c968a93297";
- };
-
- want = {
- env = [
- "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
- "DISPLAY=unix:/tmp/.X11-unix/X0"
- "HOME=/var/lib/hakurei/u0/a4"
- "SHELL=/run/current-system/sw/bin/bash"
- "TERM=linux"
- "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 = fs "dead" {
- ".hakurei" = fs "800001ed" {
- ".ro-store" = fs "801001fd" null null;
- store = fs "800001ff" null null;
- } null;
- bin = fs "800001ed" { sh = fs "80001ff" null null; } null;
- dev = fs "800001ed" null null;
- etc = fs "800001ed" {
- ".clean" = fs "80001ff" null null;
- ".host" = fs "800001c0" null null;
- ".updated" = fs "80001ff" null null;
- "NIXOS" = fs "80001ff" null null;
- "X11" = fs "80001ff" null null;
- "alsa" = fs "80001ff" null null;
- "bash_logout" = fs "80001ff" null null;
- "bashrc" = fs "80001ff" null null;
- "binfmt.d" = fs "80001ff" null null;
- "dbus-1" = fs "80001ff" null null;
- "default" = fs "80001ff" null null;
- "dhcpcd.exit-hook" = fs "80001ff" null null;
- "environment.d" = fs "80001ff" null null;
- "fonts" = fs "80001ff" null null;
- "fstab" = fs "80001ff" null null;
- "hsurc" = fs "80001ff" null null;
- "fuse.conf" = fs "80001ff" null null;
- "gai.conf" = fs "80001ff" null null;
- "group" = fs "180" null "hakurei:x:65534:\n";
- "host.conf" = fs "80001ff" null null;
- "hostname" = fs "80001ff" null null;
- "hosts" = fs "80001ff" null null;
- "inputrc" = fs "80001ff" null null;
- "issue" = fs "80001ff" null null;
- "kbd" = fs "80001ff" null null;
- "locale.conf" = fs "80001ff" null null;
- "login.defs" = fs "80001ff" null null;
- "lsb-release" = fs "80001ff" null null;
- "lvm" = fs "80001ff" null null;
- "machine-id" = fs "80001ff" null null;
- "man_db.conf" = fs "80001ff" null null;
- "modprobe.d" = fs "80001ff" null null;
- "modules-load.d" = fs "80001ff" null null;
- "mtab" = fs "80001ff" null null;
- "nanorc" = fs "80001ff" null null;
- "netgroup" = fs "80001ff" null null;
- "nix" = fs "80001ff" null null;
- "nixos" = fs "80001ff" null null;
- "nscd.conf" = fs "80001ff" null null;
- "nsswitch.conf" = fs "80001ff" null null;
- "os-release" = fs "80001ff" null null;
- "pam" = fs "80001ff" null null;
- "pam.d" = fs "80001ff" null null;
- "passwd" = fs "180" null "u0_a4:x:65534:65534:Hakurei:/var/lib/hakurei/u0/a4:/run/current-system/sw/bin/bash\n";
- "pipewire" = fs "80001ff" null null;
- "pki" = fs "80001ff" null null;
- "polkit-1" = fs "80001ff" null null;
- "profile" = fs "80001ff" null null;
- "protocols" = fs "80001ff" null null;
- "resolv.conf" = fs "80001ff" null null;
- "resolvconf.conf" = fs "80001ff" null null;
- "rpc" = fs "80001ff" null null;
- "services" = fs "80001ff" null null;
- "set-environment" = fs "80001ff" null null;
- "shadow" = fs "80001ff" null null;
- "shells" = fs "80001ff" null null;
- "speech-dispatcher" = fs "80001ff" null null;
- "ssh" = fs "80001ff" null null;
- "ssl" = fs "80001ff" null null;
- "static" = fs "80001ff" null null;
- "subgid" = fs "80001ff" null null;
- "subuid" = fs "80001ff" null null;
- "sudoers" = fs "80001ff" null null;
- "sway" = fs "80001ff" null null;
- "sysctl.d" = fs "80001ff" null null;
- "systemd" = fs "80001ff" null null;
- "terminfo" = fs "80001ff" null null;
- "tmpfiles.d" = fs "80001ff" null null;
- "udev" = fs "80001ff" null null;
- "vconsole.conf" = fs "80001ff" null null;
- "xdg" = fs "80001ff" null null;
- "zoneinfo" = fs "80001ff" null null;
- } null;
- nix = fs "800001c0" { store = fs "801001fd" null null; } null;
- proc = fs "8000016d" null null;
- run = fs "800001ed" {
- current-system = fs "80001ff" null null;
- opengl-driver = fs "80001ff" null null;
- user = fs "800001ed" {
- "65534" = fs "800001c0" {
- bus = fs "10001fd" null null;
- pulse = fs "800001c0" { native = fs "10001ff" null null; } null;
- wayland-0 = fs "1000038" null null;
- } null;
- } null;
- } null;
- sys = fs "800001c0" {
- block = fs "800001ed" (
- {
- ${extraPaths.${system}.fd} = fs "80001ff" null null;
- loop0 = fs "80001ff" null null;
- loop1 = fs "80001ff" null null;
- loop2 = fs "80001ff" null null;
- loop3 = fs "80001ff" null null;
- loop4 = fs "80001ff" null null;
- loop5 = fs "80001ff" null null;
- loop6 = fs "80001ff" null null;
- loop7 = fs "80001ff" null null;
- vda = fs "80001ff" null null;
- }
- // extraPaths.${system}.sr
- ) null;
- bus = fs "800001ed" null null;
- class = fs "800001ed" null null;
- dev = fs "800001ed" {
- block = fs "800001ed" null null;
- char = fs "800001ed" null null;
- } null;
- devices = fs "800001ed" null null;
- } null;
- tmp = fs "800001f8" {
- ".X11-unix" = fs "801001ff" { X0 = fs "10001fd" null null; } null;
- } null;
- usr = fs "800001c0" { bin = fs "800001ed" { env = fs "80001ff" null null; } null; } null;
- var = fs "800001c0" {
- tmp = fs "801001ff" null null;
- lib = fs "800001c0" {
- hakurei = fs "800001c0" {
- u0 = fs "800001c0" {
- a4 = fs "800001c0" {
- ".cache" = fs "800001ed" { ".keep" = fs "80001ff" null ""; } null;
- ".config" = fs "800001ed" {
- "environment.d" = fs "800001ed" { "10-home-manager.conf" = fs "80001ff" null null; } null;
- systemd = fs "800001ed" {
- user = fs "800001ed" { "tray.target" = fs "80001ff" null null; } null;
- } null;
- } null;
- ".local" = fs "800001ed" {
- state = fs "800001ed" {
- ".keep" = fs "80001ff" null "";
- home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
- nix = fs "800001ed" {
- profiles = fs "800001ed" {
- profile = fs "80001ff" null null;
- profile-1-link = fs "80001ff" null null;
- } null;
- } null;
- } null;
- } null;
- ".nix-defexpr" = fs "800001ed" {
- channels = fs "80001ff" null null;
- channels_root = fs "80001ff" null null;
- } null;
- ".nix-profile" = fs "80001ff" null null;
- } null;
- } null;
- } null;
- } null;
- cache = fs "800001ed" { private = fs "800001c0" null null; } null;
- } null;
- } null;
-
- mount = [
- (ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10004,gid=10004")
- (ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
- (ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10004,gid=10004")
- (ent "/" "/dev" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666")
- (ent "/" ignore ignore ignore ignore ignore) # not deterministic
- (ent "/" ignore ignore ignore ignore ignore)
- (ent "/" ignore ignore ignore ignore ignore)
- (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10004,gid=10004")
- (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,mode=755,uid=10004,gid=10004")
- (ent "/tmp/hakurei.0/tmpdir/4" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10004,gid=10004")
- (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10004,gid=10004")
- (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/tmp/.X11-unix" "/tmp/.X11-unix" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/sysroot/nix/.ro-store,upperdir=/sysroot/nix/.rw-store/upper,workdir=/sysroot/nix/.rw-store/work,uuid=on")
- (ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/class" "/sys/class" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/" "/.hakurei/.ro-store" "rw,relatime" "overlay" "overlay" "ro,lowerdir+=/host/nix/.ro-store,lowerdir+=/host/nix/.rw-store/upper,redirect_dir=nofollow,userxattr")
- (ent "/" "/.hakurei/store" "rw,relatime" "overlay" "overlay" "rw,lowerdir+=/host/nix/.ro-store,lowerdir+=/host/nix/.rw-store/upper,upperdir=/host/tmp/.hakurei-store-rw/upper,workdir=/host/tmp/.hakurei-store-rw/work,redirect_dir=nofollow,userxattr")
- (ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/var/lib/hakurei/u0/a4" "/var/lib/hakurei/u0/a4" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- ];
-
- seccomp = true;
-
- try_socket = "/tmp/.X11-unix/X0";
- socket_abstract = false;
- socket_pathname = true;
- };
-}
diff --git a/test/sandbox/case/mapuid.nix b/test/sandbox/case/mapuid.nix
deleted file mode 100644
index 1b6ef0e7..00000000
--- a/test/sandbox/case/mapuid.nix
+++ /dev/null
@@ -1,282 +0,0 @@
-{
- fs,
- ent,
- ignore,
- system,
-}:
-let
- extraPaths = {
- x86_64-linux = {
- fd = "fd0";
- "/dev/dri" = {
- by-path = fs "800001ed" {
- "pci-0000:00:09.0-card" = fs "80001ff" null null;
- "pci-0000:00:09.0-render" = fs "80001ff" null null;
- } null;
- card0 = fs "42001b0" null null;
- renderD128 = fs "42001b6" null null;
- };
- sr = {
- sr0 = fs "80001ff" null null;
- };
- };
- aarch64-linux = {
- fd = "mtdblock0";
- "/dev/dri" = null;
- sr = { };
- };
- };
-in
-{
- name = "mapuid";
- tty = false;
- device = false;
- mapRealUid = true;
- useCommonPaths = true;
- userns = false;
- x11 = false;
- hostAbstract = false;
- shareRuntime = true;
- shareTmpdir = true;
-
- # 0, PresetStrict
- expectedFilter = {
- x86_64-linux = "e880298df2bd6751d0040fc21bc0ed4c00f95dc0d7ba506c244d8b8cf6866dba8ef4a33296f287b66cccc1d78e97026597f84cc7dec1573e148960fbd35cd735";
- aarch64-linux = "79318538a3dc851314b6bd96f10d5861acb2aa7e13cb8de0619d0f6a76709d67f01ef3fd67e195862b02f9711e5b769bc4d1eb4fc0dfc41a723c89c968a93297";
- };
-
- want = {
- env = [
- "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus"
- "HOME=/var/lib/hakurei/u0/a3"
- "SHELL=/run/current-system/sw/bin/bash"
- "TERM=linux"
- "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 = fs "dead" {
- ".hakurei" = fs "800001ed" {
- ".ro-store" = fs "801001fd" null null;
- store = fs "800001ff" null null;
- } null;
- bin = fs "800001ed" { sh = fs "80001ff" null null; } null;
- dev = fs "800001ed" {
- core = fs "80001ff" null null;
- dri = fs "800001ed" extraPaths.${system}."/dev/dri" null;
- fd = fs "80001ff" null null;
- full = fs "42001b6" null null;
- mqueue = fs "801001ff" { } null;
- null = fs "42001b6" null "";
- ptmx = fs "80001ff" null null;
- pts = fs "800001ed" { ptmx = fs "42001b6" null null; } null;
- random = fs "42001b6" null null;
- shm = fs "801001ff" { } null;
- stderr = fs "80001ff" null null;
- stdin = fs "80001ff" null null;
- stdout = fs "80001ff" null null;
- tty = fs "42001b6" null null;
- urandom = fs "42001b6" null null;
- zero = fs "42001b6" null null;
- } null;
- etc = fs "800001ed" {
- ".clean" = fs "80001ff" null null;
- ".host" = fs "800001c0" null null;
- ".updated" = fs "80001ff" null null;
- "NIXOS" = fs "80001ff" null null;
- "X11" = fs "80001ff" null null;
- "alsa" = fs "80001ff" null null;
- "bash_logout" = fs "80001ff" null null;
- "bashrc" = fs "80001ff" null null;
- "binfmt.d" = fs "80001ff" null null;
- "dbus-1" = fs "80001ff" null null;
- "default" = fs "80001ff" null null;
- "dhcpcd.exit-hook" = fs "80001ff" null null;
- "environment.d" = fs "80001ff" null null;
- "fonts" = fs "80001ff" null null;
- "fstab" = fs "80001ff" null null;
- "hsurc" = fs "80001ff" null null;
- "fuse.conf" = fs "80001ff" null null;
- "gai.conf" = fs "80001ff" null null;
- "group" = fs "180" null "hakurei:x:100:\n";
- "host.conf" = fs "80001ff" null null;
- "hostname" = fs "80001ff" null null;
- "hosts" = fs "80001ff" null null;
- "inputrc" = fs "80001ff" null null;
- "issue" = fs "80001ff" null null;
- "kbd" = fs "80001ff" null null;
- "locale.conf" = fs "80001ff" null null;
- "login.defs" = fs "80001ff" null null;
- "lsb-release" = fs "80001ff" null null;
- "lvm" = fs "80001ff" null null;
- "machine-id" = fs "80001ff" null null;
- "man_db.conf" = fs "80001ff" null null;
- "modprobe.d" = fs "80001ff" null null;
- "modules-load.d" = fs "80001ff" null null;
- "mtab" = fs "80001ff" null null;
- "nanorc" = fs "80001ff" null null;
- "netgroup" = fs "80001ff" null null;
- "nix" = fs "80001ff" null null;
- "nixos" = fs "80001ff" null null;
- "nscd.conf" = fs "80001ff" null null;
- "nsswitch.conf" = fs "80001ff" null null;
- "os-release" = fs "80001ff" null null;
- "pam" = fs "80001ff" null null;
- "pam.d" = fs "80001ff" null null;
- "passwd" = fs "180" null "u0_a3:x:1000:100:Hakurei:/var/lib/hakurei/u0/a3:/run/current-system/sw/bin/bash\n";
- "pipewire" = fs "80001ff" null null;
- "pki" = fs "80001ff" null null;
- "polkit-1" = fs "80001ff" null null;
- "profile" = fs "80001ff" null null;
- "protocols" = fs "80001ff" null null;
- "resolv.conf" = fs "80001ff" null null;
- "resolvconf.conf" = fs "80001ff" null null;
- "rpc" = fs "80001ff" null null;
- "services" = fs "80001ff" null null;
- "set-environment" = fs "80001ff" null null;
- "shadow" = fs "80001ff" null null;
- "shells" = fs "80001ff" null null;
- "speech-dispatcher" = fs "80001ff" null null;
- "ssh" = fs "80001ff" null null;
- "ssl" = fs "80001ff" null null;
- "static" = fs "80001ff" null null;
- "subgid" = fs "80001ff" null null;
- "subuid" = fs "80001ff" null null;
- "sudoers" = fs "80001ff" null null;
- "sway" = fs "80001ff" null null;
- "sysctl.d" = fs "80001ff" null null;
- "systemd" = fs "80001ff" null null;
- "terminfo" = fs "80001ff" null null;
- "tmpfiles.d" = fs "80001ff" null null;
- "udev" = fs "80001ff" null null;
- "vconsole.conf" = fs "80001ff" null null;
- "xdg" = fs "80001ff" null null;
- "zoneinfo" = fs "80001ff" null null;
- } null;
- nix = fs "800001c0" { store = fs "801001fd" null null; } null;
- proc = fs "8000016d" null null;
- run = fs "800001ed" {
- current-system = fs "80001ff" null null;
- opengl-driver = fs "80001ff" null null;
- user = fs "800001ed" {
- "1000" = fs "800001f8" {
- bus = fs "10001fd" null null;
- pulse = fs "800001c0" { native = fs "10001ff" null null; } null;
- wayland-0 = fs "1000038" null null;
- } null;
- } null;
- } null;
- sys = fs "800001c0" {
- block = fs "800001ed" (
- {
- ${extraPaths.${system}.fd} = fs "80001ff" null null;
- loop0 = fs "80001ff" null null;
- loop1 = fs "80001ff" null null;
- loop2 = fs "80001ff" null null;
- loop3 = fs "80001ff" null null;
- loop4 = fs "80001ff" null null;
- loop5 = fs "80001ff" null null;
- loop6 = fs "80001ff" null null;
- loop7 = fs "80001ff" null null;
- vda = fs "80001ff" null null;
- }
- // extraPaths.${system}.sr
- ) null;
- bus = fs "800001ed" null null;
- class = fs "800001ed" null null;
- dev = fs "800001ed" {
- block = fs "800001ed" null null;
- char = fs "800001ed" null null;
- } null;
- devices = fs "800001ed" null null;
- } null;
- tmp = fs "800001f8" { } null;
- usr = fs "800001c0" { bin = fs "800001ed" { env = fs "80001ff" null null; } null; } null;
- var = fs "800001c0" {
- tmp = fs "801001ff" null null;
- lib = fs "800001c0" {
- hakurei = fs "800001c0" {
- u0 = fs "800001c0" {
- a3 = fs "800001c0" {
- ".cache" = fs "800001ed" { ".keep" = fs "80001ff" null ""; } null;
- ".config" = fs "800001ed" {
- "environment.d" = fs "800001ed" { "10-home-manager.conf" = fs "80001ff" null null; } null;
- systemd = fs "800001ed" {
- user = fs "800001ed" { "tray.target" = fs "80001ff" null null; } null;
- } null;
- } null;
- ".local" = fs "800001ed" {
- state = fs "800001ed" {
- ".keep" = fs "80001ff" null "";
- home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
- nix = fs "800001ed" {
- profiles = fs "800001ed" {
- profile = fs "80001ff" null null;
- profile-1-link = fs "80001ff" null null;
- } null;
- } null;
- } null;
- } null;
- ".nix-defexpr" = fs "800001ed" {
- channels = fs "80001ff" null null;
- channels_root = fs "80001ff" null null;
- } null;
- ".nix-profile" = fs "80001ff" null null;
- } null;
- } null;
- } null;
- } null;
- cache = fs "800001ed" { private = fs "800001c0" null null; } null;
- } null;
- } null;
-
- mount = [
- (ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10003,gid=10003")
- (ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
- (ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10003,gid=10003")
- (ent "/" "/dev" "ro,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=10003,gid=10003")
- (ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/full" "/dev/full" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/random" "/dev/random" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/urandom" "/dev/urandom" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/tty" "/dev/tty" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,mode=620,ptmxmode=666")
- (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
- (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10003,gid=10003")
- (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,mode=755,uid=10003,gid=10003")
- (ent "/tmp/hakurei.0/runtime/3" "/run/user/1000" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/tmp/hakurei.0/tmpdir/3" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10003,gid=10003")
- (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10003,gid=10003")
- (ent ignore "/run/user/1000/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/run/user/1000/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/sysroot/nix/.ro-store,upperdir=/sysroot/nix/.rw-store/upper,workdir=/sysroot/nix/.rw-store/work,uuid=on")
- (ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/class" "/sys/class" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/" "/.hakurei/.ro-store" "rw,relatime" "overlay" "overlay" "ro,lowerdir+=/host/nix/.ro-store,lowerdir+=/host/nix/.rw-store/upper,redirect_dir=nofollow,userxattr")
- (ent "/" "/.hakurei/store" "rw,relatime" "overlay" "overlay" "rw,lowerdir+=/host/nix/.ro-store,lowerdir+=/host/nix/.rw-store/upper,upperdir=/host/tmp/.hakurei-store-rw/upper,workdir=/host/tmp/.hakurei-store-rw/work,redirect_dir=nofollow,userxattr")
- (ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/var/lib/hakurei/u0/a3" "/var/lib/hakurei/u0/a3" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/run/user/1000/pulse/native" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- ];
-
- seccomp = true;
-
- try_socket = "/tmp/.X11-unix/X0";
- socket_abstract = false;
- socket_pathname = false;
- };
-}
diff --git a/test/sandbox/case/pd.nix b/test/sandbox/case/pd.nix
deleted file mode 100644
index 25f42979..00000000
--- a/test/sandbox/case/pd.nix
+++ /dev/null
@@ -1,206 +0,0 @@
-{
- fs,
- ent,
- ignore,
- ...
-}:
-{
- # 0, PresetExt | PresetDenyDevel
- expectedFilter = {
- x86_64-linux = "c698b081ff957afe17a6d94374537d37f2a63f6f9dd75da7546542407a9e32476ebda3312ba7785d7f618542bcfaf27ca27dcc2dddba852069d28bcfe8cad39a";
- aarch64-linux = "433ce9b911282d6dcc8029319fb79b816b60d5a795ec8fc94344dd027614d68f023166a91bb881faaeeedd26e3d89474e141e5a69a97e93b8984ca8f14999980";
- };
-
- want = {
- env = [
- "HOME=/var/lib/hakurei/u0/a0"
- "SHELL=/run/current-system/sw/bin/bash"
- "TERM=linux"
- "USER=u0_a0"
- "XDG_RUNTIME_DIR=/run/user/65534"
- "XDG_SESSION_CLASS=user"
- "XDG_SESSION_TYPE=tty"
- ];
-
- fs = fs "dead" {
- ".hakurei" = fs "800001ed" { } null;
- bin = fs "800001ed" { sh = fs "80001ff" null null; } null;
- dev = fs "800001ed" {
- console = fs "4200190" null null;
- core = fs "80001ff" null null;
- fd = fs "80001ff" null null;
- full = fs "42001b6" null null;
- kvm = fs "42001b6" null null;
- mqueue = fs "801001ff" { } null;
- null = fs "42001b6" null "";
- ptmx = fs "80001ff" null null;
- pts = fs "800001ed" { ptmx = fs "42001b6" null null; } null;
- random = fs "42001b6" null null;
- shm = fs "801001ff" { } null;
- stderr = fs "80001ff" null null;
- stdin = fs "80001ff" null null;
- stdout = fs "80001ff" null null;
- tty = fs "42001b6" null null;
- urandom = fs "42001b6" null null;
- zero = fs "42001b6" null null;
- } null;
- etc = fs "800001ed" {
- ".clean" = fs "80001ff" null null;
- ".host" = fs "800001c0" null null;
- ".updated" = fs "80001ff" null null;
- "NIXOS" = fs "80001ff" null null;
- "X11" = fs "80001ff" null null;
- "alsa" = fs "80001ff" null null;
- "bash_logout" = fs "80001ff" null null;
- "bashrc" = fs "80001ff" null null;
- "binfmt.d" = fs "80001ff" null null;
- "dbus-1" = fs "80001ff" null null;
- "default" = fs "80001ff" null null;
- "dhcpcd.exit-hook" = fs "80001ff" null null;
- "environment.d" = fs "80001ff" null null;
- "fonts" = fs "80001ff" null null;
- "fstab" = fs "80001ff" null null;
- "hsurc" = fs "80001ff" null null;
- "fuse.conf" = fs "80001ff" null null;
- "gai.conf" = fs "80001ff" null null;
- "group" = fs "180" null "hakurei:x:65534:\n";
- "host.conf" = fs "80001ff" null null;
- "hostname" = fs "80001ff" null null;
- "hosts" = fs "80001ff" null null;
- "inputrc" = fs "80001ff" null null;
- "issue" = fs "80001ff" null null;
- "kbd" = fs "80001ff" null null;
- "locale.conf" = fs "80001ff" null null;
- "login.defs" = fs "80001ff" null null;
- "lsb-release" = fs "80001ff" null null;
- "lvm" = fs "80001ff" null null;
- "machine-id" = fs "80001ff" null null;
- "man_db.conf" = fs "80001ff" null null;
- "modprobe.d" = fs "80001ff" null null;
- "modules-load.d" = fs "80001ff" null null;
- "mtab" = fs "80001ff" null null;
- "nanorc" = fs "80001ff" null null;
- "netgroup" = fs "80001ff" null null;
- "nix" = fs "80001ff" null null;
- "nixos" = fs "80001ff" null null;
- "nscd.conf" = fs "80001ff" null null;
- "nsswitch.conf" = fs "80001ff" null null;
- "os-release" = fs "80001ff" null null;
- "pam" = fs "80001ff" null null;
- "pam.d" = fs "80001ff" null null;
- "passwd" = fs "180" null "u0_a0:x:65534:65534:Hakurei:/var/lib/hakurei/u0/a0:/run/current-system/sw/bin/bash\n";
- "pipewire" = fs "80001ff" null null;
- "pki" = fs "80001ff" null null;
- "polkit-1" = fs "80001ff" null null;
- "profile" = fs "80001ff" null null;
- "protocols" = fs "80001ff" null null;
- "resolv.conf" = fs "80001ff" null null;
- "resolvconf.conf" = fs "80001ff" null null;
- "rpc" = fs "80001ff" null null;
- "services" = fs "80001ff" null null;
- "set-environment" = fs "80001ff" null null;
- "shadow" = fs "80001ff" null null;
- "shells" = fs "80001ff" null null;
- "speech-dispatcher" = fs "80001ff" null null;
- "ssh" = fs "80001ff" null null;
- "ssl" = fs "80001ff" null null;
- "static" = fs "80001ff" null null;
- "subgid" = fs "80001ff" null null;
- "subuid" = fs "80001ff" null null;
- "sudoers" = fs "80001ff" null null;
- "sway" = fs "80001ff" null null;
- "sysctl.d" = fs "80001ff" null null;
- "systemd" = fs "80001ff" null null;
- "terminfo" = fs "80001ff" null null;
- "tmpfiles.d" = fs "80001ff" null null;
- "udev" = fs "80001ff" null null;
- "vconsole.conf" = fs "80001ff" null null;
- "xdg" = fs "80001ff" null null;
- "zoneinfo" = fs "80001ff" null null;
- } null;
- home = fs "800001ed" { alice = fs "800001c0" null null; } null;
- lib64 = fs "800001ed" { "ld-linux-x86-64.so.2" = fs "80001ff" null null; } null;
- "lost+found" = fs "800001c0" null null;
- nix = fs "800001ed" {
- ".ro-store" = fs "801001fd" null null;
- ".rw-store" = fs "800001ed" null null;
- store = fs "801001fd" null null;
- var = fs "800001ed" {
- log = fs "800001ed" null null;
- nix = fs "800001ed" null null;
- } null;
- } null;
- proc = fs "8000016d" null null;
- root = fs "800001c0" null null;
- run = fs "800001ed" null null;
- srv = fs "800001ed" { } null;
- sys = fs "8000016d" null null;
- tmp = fs "800001f8" { } null;
- usr = fs "800001ed" { bin = fs "800001ed" { env = fs "80001ff" null null; } null; } null;
- var = fs "800001ed" null null;
- } null;
-
- mount = [
- (ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10000,gid=10000")
- (ent "/bin" "/bin" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/home" "/home" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/lib64" "/lib64" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/lost+found" "/lost+found" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/nix" "/nix" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- # systemd nondeterminism: ro-store rw-store
- (ent "/" ignore "rw,nosuid,nodev,relatime" ignore ignore ignore)
- (ent "/" ignore "rw,nosuid,nodev,relatime" ignore ignore ignore)
- (ent "/" "/nix/store" "rw,relatime" "overlay" "overlay" "rw,lowerdir=/sysroot/nix/.ro-store,upperdir=/sysroot/nix/.rw-store/upper,workdir=/sysroot/nix/.rw-store/work,uuid=on")
- (ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/sysroot/nix/.ro-store,upperdir=/sysroot/nix/.rw-store/upper,workdir=/sysroot/nix/.rw-store/work,uuid=on")
- (ent "/root" "/root" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/" "/run" "rw,nosuid,nodev" "tmpfs" "tmpfs" ignore)
- (ent "/" "/run/keys" "rw,nosuid,nodev,relatime" "ramfs" "ramfs" "rw,mode=750")
- (ent "/" "/run/credentials/systemd-journald.service" "rw,nosuid,nodev,noexec,relatime,nosymfollow" "tmpfs" "none" "ro,size=1024k,nr_inodes=1024,mode=700,noswap")
- (ent "/" "/run/wrappers" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
- (ent "/" "/run/credentials/getty@tty1.service" "rw,nosuid,nodev,noexec,relatime,nosymfollow" "tmpfs" "none" "ro,size=1024k,nr_inodes=1024,mode=700,noswap")
- (ent "/" "/run/user/1000" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
- (ent "/srv" "/srv" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/" "/sys" "rw,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/" "/sys/kernel/security" "rw,nosuid,nodev,noexec,relatime" "securityfs" "securityfs" "rw")
- (ent "/../../.." "/sys/fs/cgroup" "rw,nosuid,nodev,noexec,relatime" "cgroup2" "cgroup2" "rw,nsdelegate,memory_recursiveprot,memory_hugetlb_accounting")
- (ent "/" "/sys/fs/pstore" "rw,nosuid,nodev,noexec,relatime" "pstore" "none" "rw")
- (ent "/" "/sys/fs/bpf" "rw,nosuid,nodev,noexec,relatime" "bpf" "bpf" "rw,mode=700")
- # systemd nondeterminism: tracefs debugfs configfs fusectl
- (ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
- (ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
- (ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
- (ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
- (ent "/usr" "/usr" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/var" "/var" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
- (ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10000,gid=10000")
- (ent "/" "/dev" "ro,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=10000,gid=10000")
- (ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/full" "/dev/full" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/random" "/dev/random" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/urandom" "/dev/urandom" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/tty" "/dev/tty" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,mode=620,ptmxmode=666")
- (ent ignore "/dev/console" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666")
- (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
- (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10000,gid=10000")
- (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,mode=755,uid=10000,gid=10000")
- (ent "/tmp/hakurei.0/runtime/0" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/tmp/hakurei.0/tmpdir/0" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10000,gid=10000")
- (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10000,gid=10000")
- (ent "/kvm" "/dev/kvm" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/" "/run/user/1000" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=10000,gid=10000")
- (ent "/" "/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=10000,gid=10000")
- (ent "/" "/run/dbus" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=10000,gid=10000")
- ];
-
- seccomp = true;
-
- try_socket = "/tmp/.X11-unix/X0";
- socket_abstract = true;
- socket_pathname = false;
- };
-}
diff --git a/test/sandbox/case/pdlike.nix b/test/sandbox/case/pdlike.nix
deleted file mode 100644
index 7f0716fb..00000000
--- a/test/sandbox/case/pdlike.nix
+++ /dev/null
@@ -1,277 +0,0 @@
-{
- fs,
- ent,
- ignore,
- system,
-}:
-let
- extraPaths = {
- x86_64-linux = {
- fd = "fd0";
- "/dev/dri" = {
- by-path = fs "800001ed" {
- "pci-0000:00:09.0-card" = fs "80001ff" null null;
- "pci-0000:00:09.0-render" = fs "80001ff" null null;
- } null;
- card0 = fs "42001b0" null null;
- renderD128 = fs "42001b6" null null;
- };
- sr = {
- sr0 = fs "80001ff" null null;
- };
- };
- aarch64-linux = {
- fd = "mtdblock0";
- "/dev/dri" = null;
- sr = { };
- };
- };
-in
-{
- name = "pdlike";
- tty = true;
- device = false;
- mapRealUid = false;
- useCommonPaths = false;
- userns = true;
- x11 = false;
- hostAbstract = false;
- shareRuntime = true;
- shareTmpdir = true;
-
- # 0, PresetExt | PresetDenyDevel
- expectedFilter = {
- x86_64-linux = "c698b081ff957afe17a6d94374537d37f2a63f6f9dd75da7546542407a9e32476ebda3312ba7785d7f618542bcfaf27ca27dcc2dddba852069d28bcfe8cad39a";
- aarch64-linux = "433ce9b911282d6dcc8029319fb79b816b60d5a795ec8fc94344dd027614d68f023166a91bb881faaeeedd26e3d89474e141e5a69a97e93b8984ca8f14999980";
- };
-
- want = {
- env = [
- "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
- "HOME=/var/lib/hakurei/u0/a5"
- "SHELL=/run/current-system/sw/bin/bash"
- "TERM=linux"
- "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 = fs "dead" {
- ".hakurei" = fs "800001ed" { } null;
- bin = fs "800001ed" { sh = fs "80001ff" null null; } null;
- dev = fs "800001ed" {
- console = fs "4200190" null null;
- core = fs "80001ff" null null;
- dri = fs "800001ed" extraPaths.${system}."/dev/dri" null;
- fd = fs "80001ff" null null;
- full = fs "42001b6" null null;
- mqueue = fs "801001ff" { } null;
- null = fs "42001b6" null "";
- ptmx = fs "80001ff" null null;
- pts = fs "800001ed" { ptmx = fs "42001b6" null null; } null;
- random = fs "42001b6" null null;
- shm = fs "801001ff" { } null;
- stderr = fs "80001ff" null null;
- stdin = fs "80001ff" null null;
- stdout = fs "80001ff" null null;
- tty = fs "42001b6" null null;
- urandom = fs "42001b6" null null;
- zero = fs "42001b6" null null;
- } null;
- etc = fs "800001ed" {
- ".clean" = fs "80001ff" null null;
- ".host" = fs "800001c0" null null;
- ".updated" = fs "80001ff" null null;
- "NIXOS" = fs "80001ff" null null;
- "X11" = fs "80001ff" null null;
- "alsa" = fs "80001ff" null null;
- "bash_logout" = fs "80001ff" null null;
- "bashrc" = fs "80001ff" null null;
- "binfmt.d" = fs "80001ff" null null;
- "dbus-1" = fs "80001ff" null null;
- "default" = fs "80001ff" null null;
- "dhcpcd.exit-hook" = fs "80001ff" null null;
- "environment.d" = fs "80001ff" null null;
- "fonts" = fs "80001ff" null null;
- "fstab" = fs "80001ff" null null;
- "hsurc" = fs "80001ff" null null;
- "fuse.conf" = fs "80001ff" null null;
- "gai.conf" = fs "80001ff" null null;
- "group" = fs "180" null "hakurei:x:65534:\n";
- "host.conf" = fs "80001ff" null null;
- "hostname" = fs "80001ff" null null;
- "hosts" = fs "80001ff" null null;
- "inputrc" = fs "80001ff" null null;
- "issue" = fs "80001ff" null null;
- "kbd" = fs "80001ff" null null;
- "locale.conf" = fs "80001ff" null null;
- "login.defs" = fs "80001ff" null null;
- "lsb-release" = fs "80001ff" null null;
- "lvm" = fs "80001ff" null null;
- "machine-id" = fs "80001ff" null null;
- "man_db.conf" = fs "80001ff" null null;
- "modprobe.d" = fs "80001ff" null null;
- "modules-load.d" = fs "80001ff" null null;
- "mtab" = fs "80001ff" null null;
- "nanorc" = fs "80001ff" null null;
- "netgroup" = fs "80001ff" null null;
- "nix" = fs "80001ff" null null;
- "nixos" = fs "80001ff" null null;
- "nscd.conf" = fs "80001ff" null null;
- "nsswitch.conf" = fs "80001ff" null null;
- "os-release" = fs "80001ff" null null;
- "pam" = fs "80001ff" null null;
- "pam.d" = fs "80001ff" null null;
- "passwd" = fs "180" null "u0_a5:x:65534:65534:Hakurei:/var/lib/hakurei/u0/a5:/run/current-system/sw/bin/bash\n";
- "pipewire" = fs "80001ff" null null;
- "pki" = fs "80001ff" null null;
- "polkit-1" = fs "80001ff" null null;
- "profile" = fs "80001ff" null null;
- "protocols" = fs "80001ff" null null;
- "resolv.conf" = fs "80001ff" null null;
- "resolvconf.conf" = fs "80001ff" null null;
- "rpc" = fs "80001ff" null null;
- "services" = fs "80001ff" null null;
- "set-environment" = fs "80001ff" null null;
- "shadow" = fs "80001ff" null null;
- "shells" = fs "80001ff" null null;
- "speech-dispatcher" = fs "80001ff" null null;
- "ssh" = fs "80001ff" null null;
- "ssl" = fs "80001ff" null null;
- "static" = fs "80001ff" null null;
- "subgid" = fs "80001ff" null null;
- "subuid" = fs "80001ff" null null;
- "sudoers" = fs "80001ff" null null;
- "sway" = fs "80001ff" null null;
- "sysctl.d" = fs "80001ff" null null;
- "systemd" = fs "80001ff" null null;
- "terminfo" = fs "80001ff" null null;
- "tmpfiles.d" = fs "80001ff" null null;
- "udev" = fs "80001ff" null null;
- "vconsole.conf" = fs "80001ff" null null;
- "xdg" = fs "80001ff" null null;
- "zoneinfo" = fs "80001ff" null null;
- } null;
- nix = fs "800001c0" { store = fs "801001fd" null null; } null;
- proc = fs "8000016d" null null;
- run = fs "800001ed" {
- current-system = fs "80001ff" null null;
- opengl-driver = fs "80001ff" null null;
- user = fs "800001ed" {
- "65534" = fs "800001f8" {
- bus = fs "10001fd" null null;
- pulse = fs "800001c0" { native = fs "10001ff" null null; } null;
- wayland-0 = fs "1000038" null null;
- } null;
- } null;
- } null;
- sys = fs "800001c0" {
- block = fs "800001ed" (
- {
- ${extraPaths.${system}.fd} = fs "80001ff" null null;
- loop0 = fs "80001ff" null null;
- loop1 = fs "80001ff" null null;
- loop2 = fs "80001ff" null null;
- loop3 = fs "80001ff" null null;
- loop4 = fs "80001ff" null null;
- loop5 = fs "80001ff" null null;
- loop6 = fs "80001ff" null null;
- loop7 = fs "80001ff" null null;
- vda = fs "80001ff" null null;
- }
- // extraPaths.${system}.sr
- ) null;
- bus = fs "800001ed" null null;
- class = fs "800001ed" null null;
- dev = fs "800001ed" {
- block = fs "800001ed" null null;
- char = fs "800001ed" null null;
- } null;
- devices = fs "800001ed" null null;
- } null;
- tmp = fs "800001f8" { } null;
- usr = fs "800001c0" { bin = fs "800001ed" { env = fs "80001ff" null null; } null; } null;
- var = fs "800001c0" {
- tmp = fs "801001ff" null null;
- lib = fs "800001c0" {
- hakurei = fs "800001c0" {
- u0 = fs "800001c0" {
- a5 = fs "800001c0" {
- ".cache" = fs "800001ed" { ".keep" = fs "80001ff" null ""; } null;
- ".config" = fs "800001ed" {
- "environment.d" = fs "800001ed" { "10-home-manager.conf" = fs "80001ff" null null; } null;
- systemd = fs "800001ed" {
- user = fs "800001ed" { "tray.target" = fs "80001ff" null null; } null;
- } null;
- } null;
- ".local" = fs "800001ed" {
- state = fs "800001ed" {
- ".keep" = fs "80001ff" null "";
- home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
- nix = fs "800001ed" {
- profiles = fs "800001ed" {
- profile = fs "80001ff" null null;
- profile-1-link = fs "80001ff" null null;
- } null;
- } null;
- } null;
- } null;
- ".nix-defexpr" = fs "800001ed" {
- channels = fs "80001ff" null null;
- channels_root = fs "80001ff" null null;
- } null;
- ".nix-profile" = fs "80001ff" null null;
- } null;
- } null;
- } null;
- } null;
- } null;
- } null;
-
- mount = [
- (ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10005,gid=10005")
- (ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
- (ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10005,gid=10005")
- (ent "/" "/dev" "ro,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=10005,gid=10005")
- (ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/full" "/dev/full" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/random" "/dev/random" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/urandom" "/dev/urandom" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/tty" "/dev/tty" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,mode=620,ptmxmode=666")
- (ent ignore "/dev/console" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666")
- (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
- (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10005,gid=10005")
- (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,mode=755,uid=10005,gid=10005")
- (ent "/tmp/hakurei.0/runtime/5" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/tmp/hakurei.0/tmpdir/5" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10005,gid=10005")
- (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10005,gid=10005")
- (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/sysroot/nix/.ro-store,upperdir=/sysroot/nix/.rw-store/upper,workdir=/sysroot/nix/.rw-store/work,uuid=on")
- (ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/class" "/sys/class" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/var/lib/hakurei/u0/a5" "/var/lib/hakurei/u0/a5" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- ];
-
- seccomp = true;
-
- try_socket = "/tmp/.X11-unix/X0";
- socket_abstract = false;
- socket_pathname = false;
- };
-}
diff --git a/test/sandbox/case/preset.nix b/test/sandbox/case/preset.nix
deleted file mode 100644
index 33cc3b8b..00000000
--- a/test/sandbox/case/preset.nix
+++ /dev/null
@@ -1,274 +0,0 @@
-{
- fs,
- ent,
- ignore,
- system,
-}:
-let
- extraPaths = {
- x86_64-linux = {
- fd = "fd0";
- "/dev/dri" = {
- by-path = fs "800001ed" {
- "pci-0000:00:09.0-card" = fs "80001ff" null null;
- "pci-0000:00:09.0-render" = fs "80001ff" null null;
- } null;
- card0 = fs "42001b0" null null;
- renderD128 = fs "42001b6" null null;
- };
- sr = {
- sr0 = fs "80001ff" null null;
- };
- };
- aarch64-linux = {
- fd = "mtdblock0";
- "/dev/dri" = null;
- sr = { };
- };
- };
-in
-{
- name = "preset";
- tty = false;
- device = false;
- mapRealUid = false;
- useCommonPaths = false;
- userns = false;
- x11 = false;
- hostAbstract = false;
- shareRuntime = false;
- shareTmpdir = false;
-
- # 0, PresetStrict
- expectedFilter = {
- x86_64-linux = "e880298df2bd6751d0040fc21bc0ed4c00f95dc0d7ba506c244d8b8cf6866dba8ef4a33296f287b66cccc1d78e97026597f84cc7dec1573e148960fbd35cd735";
- aarch64-linux = "79318538a3dc851314b6bd96f10d5861acb2aa7e13cb8de0619d0f6a76709d67f01ef3fd67e195862b02f9711e5b769bc4d1eb4fc0dfc41a723c89c968a93297";
- };
-
- want = {
- env = [
- "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
- "HOME=/var/lib/hakurei/u0/a1"
- "SHELL=/run/current-system/sw/bin/bash"
- "TERM=linux"
- "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 = fs "dead" {
- ".hakurei" = fs "800001ed" { } null;
- bin = fs "800001ed" { sh = fs "80001ff" null null; } null;
- dev = fs "800001ed" {
- core = fs "80001ff" null null;
- dri = fs "800001ed" extraPaths.${system}."/dev/dri" null;
- fd = fs "80001ff" null null;
- full = fs "42001b6" null null;
- mqueue = fs "801001ff" { } null;
- null = fs "42001b6" null "";
- ptmx = fs "80001ff" null null;
- pts = fs "800001ed" { ptmx = fs "42001b6" null null; } null;
- random = fs "42001b6" null null;
- shm = fs "801001ff" { } null;
- stderr = fs "80001ff" null null;
- stdin = fs "80001ff" null null;
- stdout = fs "80001ff" null null;
- tty = fs "42001b6" null null;
- urandom = fs "42001b6" null null;
- zero = fs "42001b6" null null;
- } null;
- etc = fs "800001ed" {
- ".clean" = fs "80001ff" null null;
- ".host" = fs "800001c0" null null;
- ".updated" = fs "80001ff" null null;
- "NIXOS" = fs "80001ff" null null;
- "X11" = fs "80001ff" null null;
- "alsa" = fs "80001ff" null null;
- "bash_logout" = fs "80001ff" null null;
- "bashrc" = fs "80001ff" null null;
- "binfmt.d" = fs "80001ff" null null;
- "dbus-1" = fs "80001ff" null null;
- "default" = fs "80001ff" null null;
- "dhcpcd.exit-hook" = fs "80001ff" null null;
- "environment.d" = fs "80001ff" null null;
- "fonts" = fs "80001ff" null null;
- "fstab" = fs "80001ff" null null;
- "hsurc" = fs "80001ff" null null;
- "fuse.conf" = fs "80001ff" null null;
- "gai.conf" = fs "80001ff" null null;
- "group" = fs "180" null "hakurei:x:65534:\n";
- "host.conf" = fs "80001ff" null null;
- "hostname" = fs "80001ff" null null;
- "hosts" = fs "80001ff" null null;
- "inputrc" = fs "80001ff" null null;
- "issue" = fs "80001ff" null null;
- "kbd" = fs "80001ff" null null;
- "locale.conf" = fs "80001ff" null null;
- "login.defs" = fs "80001ff" null null;
- "lsb-release" = fs "80001ff" null null;
- "lvm" = fs "80001ff" null null;
- "machine-id" = fs "80001ff" null null;
- "man_db.conf" = fs "80001ff" null null;
- "modprobe.d" = fs "80001ff" null null;
- "modules-load.d" = fs "80001ff" null null;
- "mtab" = fs "80001ff" null null;
- "nanorc" = fs "80001ff" null null;
- "netgroup" = fs "80001ff" null null;
- "nix" = fs "80001ff" null null;
- "nixos" = fs "80001ff" null null;
- "nscd.conf" = fs "80001ff" null null;
- "nsswitch.conf" = fs "80001ff" null null;
- "os-release" = fs "80001ff" null null;
- "pam" = fs "80001ff" null null;
- "pam.d" = fs "80001ff" null null;
- "passwd" = fs "180" null "u0_a1:x:65534:65534:Hakurei:/var/lib/hakurei/u0/a1:/run/current-system/sw/bin/bash\n";
- "pipewire" = fs "80001ff" null null;
- "pki" = fs "80001ff" null null;
- "polkit-1" = fs "80001ff" null null;
- "profile" = fs "80001ff" null null;
- "protocols" = fs "80001ff" null null;
- "resolv.conf" = fs "80001ff" null null;
- "resolvconf.conf" = fs "80001ff" null null;
- "rpc" = fs "80001ff" null null;
- "services" = fs "80001ff" null null;
- "set-environment" = fs "80001ff" null null;
- "shadow" = fs "80001ff" null null;
- "shells" = fs "80001ff" null null;
- "speech-dispatcher" = fs "80001ff" null null;
- "ssh" = fs "80001ff" null null;
- "ssl" = fs "80001ff" null null;
- "static" = fs "80001ff" null null;
- "subgid" = fs "80001ff" null null;
- "subuid" = fs "80001ff" null null;
- "sudoers" = fs "80001ff" null null;
- "sway" = fs "80001ff" null null;
- "sysctl.d" = fs "80001ff" null null;
- "systemd" = fs "80001ff" null null;
- "terminfo" = fs "80001ff" null null;
- "tmpfiles.d" = fs "80001ff" null null;
- "udev" = fs "80001ff" null null;
- "vconsole.conf" = fs "80001ff" null null;
- "xdg" = fs "80001ff" null null;
- "zoneinfo" = fs "80001ff" null null;
- } null;
- nix = fs "800001c0" { store = fs "801001fd" null null; } null;
- proc = fs "8000016d" null null;
- run = fs "800001ed" {
- current-system = fs "80001ff" null null;
- opengl-driver = fs "80001ff" null null;
- user = fs "800001ed" {
- "65534" = fs "800001c0" {
- bus = fs "10001fd" null null;
- pulse = fs "800001c0" { native = fs "10001ff" null null; } null;
- wayland-0 = fs "1000038" null null;
- } null;
- } null;
- } null;
- sys = fs "800001c0" {
- block = fs "800001ed" (
- {
- ${extraPaths.${system}.fd} = fs "80001ff" null null;
- loop0 = fs "80001ff" null null;
- loop1 = fs "80001ff" null null;
- loop2 = fs "80001ff" null null;
- loop3 = fs "80001ff" null null;
- loop4 = fs "80001ff" null null;
- loop5 = fs "80001ff" null null;
- loop6 = fs "80001ff" null null;
- loop7 = fs "80001ff" null null;
- vda = fs "80001ff" null null;
- }
- // extraPaths.${system}.sr
- ) null;
- bus = fs "800001ed" null null;
- class = fs "800001ed" null null;
- dev = fs "800001ed" {
- block = fs "800001ed" null null;
- char = fs "800001ed" null null;
- } null;
- devices = fs "800001ed" null null;
- } null;
- tmp = fs "801001ff" { } null;
- usr = fs "800001c0" { bin = fs "800001ed" { env = fs "80001ff" null null; } null; } null;
- var = fs "800001c0" {
- tmp = fs "801001ff" null null;
- lib = fs "800001c0" {
- hakurei = fs "800001c0" {
- u0 = fs "800001c0" {
- a1 = fs "800001c0" {
- ".cache" = fs "800001ed" { ".keep" = fs "80001ff" null ""; } null;
- ".config" = fs "800001ed" {
- "environment.d" = fs "800001ed" { "10-home-manager.conf" = fs "80001ff" null null; } null;
- systemd = fs "800001ed" {
- user = fs "800001ed" { "tray.target" = fs "80001ff" null null; } null;
- } null;
- } null;
- ".local" = fs "800001ed" {
- state = fs "800001ed" {
- ".keep" = fs "80001ff" null "";
- home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
- nix = fs "800001ed" {
- profiles = fs "800001ed" {
- profile = fs "80001ff" null null;
- profile-1-link = fs "80001ff" null null;
- } null;
- } null;
- } null;
- } null;
- ".nix-defexpr" = fs "800001ed" {
- channels = fs "80001ff" null null;
- channels_root = fs "80001ff" null null;
- } null;
- ".nix-profile" = fs "80001ff" null null;
- } null;
- } null;
- } null;
- } null;
- } null;
- } null;
-
- mount = [
- (ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10001,gid=10001")
- (ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
- (ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10001,gid=10001")
- (ent "/" "/dev" "ro,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=10001,gid=10001")
- (ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/full" "/dev/full" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/random" "/dev/random" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/urandom" "/dev/urandom" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/tty" "/dev/tty" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,mode=620,ptmxmode=666")
- (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
- (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10001,gid=10001")
- (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,mode=755,uid=10001,gid=10001")
- (ent "/" "/tmp" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10001,gid=10001")
- (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10001,gid=10001")
- (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10001,gid=10001")
- (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/sysroot/nix/.ro-store,upperdir=/sysroot/nix/.rw-store/upper,workdir=/sysroot/nix/.rw-store/work,uuid=on")
- (ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/class" "/sys/class" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/var/lib/hakurei/u0/a1" "/var/lib/hakurei/u0/a1" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- ];
-
- seccomp = true;
-
- try_socket = "/tmp/.X11-unix/X0";
- socket_abstract = false;
- socket_pathname = false;
- };
-}
diff --git a/test/sandbox/case/tty.nix b/test/sandbox/case/tty.nix
deleted file mode 100644
index 4ba9360e..00000000
--- a/test/sandbox/case/tty.nix
+++ /dev/null
@@ -1,288 +0,0 @@
-{
- fs,
- ent,
- ignore,
- system,
-}:
-let
- extraPaths = {
- x86_64-linux = {
- fd = "fd0";
- "/dev/dri" = {
- by-path = fs "800001ed" {
- "pci-0000:00:09.0-card" = fs "80001ff" null null;
- "pci-0000:00:09.0-render" = fs "80001ff" null null;
- } null;
- card0 = fs "42001b0" null null;
- renderD128 = fs "42001b6" null null;
- };
- sr = {
- sr0 = fs "80001ff" null null;
- };
- };
- aarch64-linux = {
- fd = "mtdblock0";
- "/dev/dri" = null;
- sr = { };
- };
- };
-in
-{
- name = "tty";
- tty = true;
- device = false;
- mapRealUid = false;
- useCommonPaths = true;
- userns = false;
- x11 = true;
- hostAbstract = true;
- shareRuntime = true;
- shareTmpdir = false;
-
- # 0, PresetExt | PresetDenyNS | PresetDenyDevel
- expectedFilter = {
- x86_64-linux = "0b76007476c1c9e25dbf674c29fdf609a1656a70063e49327654e1b5360ad3da06e1a3e32bf80e961c5516ad83d4b9e7e9bde876a93797e27627d2555c25858b";
- aarch64-linux = "cf1f4dc87436ba8ec95d268b663a6397bb0b4a5ac64d8557e6cc529d8b0f6f65dad3a92b62ed29d85eee9c6dde1267757a4d0f86032e8a45ca1bceadfa34cf5e";
- };
-
- want = {
- env = [
- "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
- "DISPLAY=:0"
- "HOME=/var/lib/hakurei/u0/a2"
- "SHELL=/run/current-system/sw/bin/bash"
- "TERM=linux"
- "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 = fs "dead" {
- ".hakurei" = fs "800001ed" {
- ".ro-store" = fs "801001fd" null null;
- store = fs "800001ff" null null;
- } null;
- bin = fs "800001ed" { sh = fs "80001ff" null null; } null;
- dev = fs "800001ed" {
- console = fs "4200190" null null;
- core = fs "80001ff" null null;
- dri = fs "800001ed" extraPaths.${system}."/dev/dri" null;
- fd = fs "80001ff" null null;
- full = fs "42001b6" null null;
- mqueue = fs "801001ff" { } null;
- null = fs "42001b6" null "";
- ptmx = fs "80001ff" null null;
- pts = fs "800001ed" { ptmx = fs "42001b6" null null; } null;
- random = fs "42001b6" null null;
- shm = fs "801001ff" { } null;
- stderr = fs "80001ff" null null;
- stdin = fs "80001ff" null null;
- stdout = fs "80001ff" null null;
- tty = fs "42001b6" null null;
- urandom = fs "42001b6" null null;
- zero = fs "42001b6" null null;
- } null;
- etc = fs "800001ed" {
- ".clean" = fs "80001ff" null null;
- ".host" = fs "800001c0" null null;
- ".updated" = fs "80001ff" null null;
- "NIXOS" = fs "80001ff" null null;
- "X11" = fs "80001ff" null null;
- "alsa" = fs "80001ff" null null;
- "bash_logout" = fs "80001ff" null null;
- "bashrc" = fs "80001ff" null null;
- "binfmt.d" = fs "80001ff" null null;
- "dbus-1" = fs "80001ff" null null;
- "default" = fs "80001ff" null null;
- "dhcpcd.exit-hook" = fs "80001ff" null null;
- "environment.d" = fs "80001ff" null null;
- "fonts" = fs "80001ff" null null;
- "fstab" = fs "80001ff" null null;
- "hsurc" = fs "80001ff" null null;
- "fuse.conf" = fs "80001ff" null null;
- "gai.conf" = fs "80001ff" null null;
- "group" = fs "180" null "hakurei:x:65534:\n";
- "host.conf" = fs "80001ff" null null;
- "hostname" = fs "80001ff" null null;
- "hosts" = fs "80001ff" null null;
- "inputrc" = fs "80001ff" null null;
- "issue" = fs "80001ff" null null;
- "kbd" = fs "80001ff" null null;
- "locale.conf" = fs "80001ff" null null;
- "login.defs" = fs "80001ff" null null;
- "lsb-release" = fs "80001ff" null null;
- "lvm" = fs "80001ff" null null;
- "machine-id" = fs "80001ff" null null;
- "man_db.conf" = fs "80001ff" null null;
- "modprobe.d" = fs "80001ff" null null;
- "modules-load.d" = fs "80001ff" null null;
- "mtab" = fs "80001ff" null null;
- "nanorc" = fs "80001ff" null null;
- "netgroup" = fs "80001ff" null null;
- "nix" = fs "80001ff" null null;
- "nixos" = fs "80001ff" null null;
- "nscd.conf" = fs "80001ff" null null;
- "nsswitch.conf" = fs "80001ff" null null;
- "os-release" = fs "80001ff" null null;
- "pam" = fs "80001ff" null null;
- "pam.d" = fs "80001ff" null null;
- "passwd" = fs "180" null "u0_a2:x:65534:65534:Hakurei:/var/lib/hakurei/u0/a2:/run/current-system/sw/bin/bash\n";
- "pipewire" = fs "80001ff" null null;
- "pki" = fs "80001ff" null null;
- "polkit-1" = fs "80001ff" null null;
- "profile" = fs "80001ff" null null;
- "protocols" = fs "80001ff" null null;
- "resolv.conf" = fs "80001ff" null null;
- "resolvconf.conf" = fs "80001ff" null null;
- "rpc" = fs "80001ff" null null;
- "services" = fs "80001ff" null null;
- "set-environment" = fs "80001ff" null null;
- "shadow" = fs "80001ff" null null;
- "shells" = fs "80001ff" null null;
- "speech-dispatcher" = fs "80001ff" null null;
- "ssh" = fs "80001ff" null null;
- "ssl" = fs "80001ff" null null;
- "static" = fs "80001ff" null null;
- "subgid" = fs "80001ff" null null;
- "subuid" = fs "80001ff" null null;
- "sudoers" = fs "80001ff" null null;
- "sway" = fs "80001ff" null null;
- "sysctl.d" = fs "80001ff" null null;
- "systemd" = fs "80001ff" null null;
- "terminfo" = fs "80001ff" null null;
- "tmpfiles.d" = fs "80001ff" null null;
- "udev" = fs "80001ff" null null;
- "vconsole.conf" = fs "80001ff" null null;
- "xdg" = fs "80001ff" null null;
- "zoneinfo" = fs "80001ff" null null;
- } null;
- nix = fs "800001c0" { store = fs "801001fd" null null; } null;
- proc = fs "8000016d" null null;
- run = fs "800001ed" {
- current-system = fs "80001ff" null null;
- opengl-driver = fs "80001ff" null null;
- user = fs "800001ed" {
- "65534" = fs "800001f8" {
- bus = fs "10001fd" null null;
- pulse = fs "800001c0" { native = fs "10001ff" null null; } null;
- wayland-0 = fs "1000038" null null;
- } null;
- } null;
- } null;
- sys = fs "800001c0" {
- block = fs "800001ed" (
- {
- ${extraPaths.${system}.fd} = fs "80001ff" null null;
- loop0 = fs "80001ff" null null;
- loop1 = fs "80001ff" null null;
- loop2 = fs "80001ff" null null;
- loop3 = fs "80001ff" null null;
- loop4 = fs "80001ff" null null;
- loop5 = fs "80001ff" null null;
- loop6 = fs "80001ff" null null;
- loop7 = fs "80001ff" null null;
- vda = fs "80001ff" null null;
- }
- // extraPaths.${system}.sr
- ) null;
- bus = fs "800001ed" null null;
- class = fs "800001ed" null null;
- dev = fs "800001ed" {
- block = fs "800001ed" null null;
- char = fs "800001ed" null null;
- } null;
- devices = fs "800001ed" null null;
- } null;
- tmp = fs "801001ff" {
- ".X11-unix" = fs "801001ff" { X0 = fs "10001fd" null null; } null;
- } null;
- usr = fs "800001c0" { bin = fs "800001ed" { env = fs "80001ff" null null; } null; } null;
- var = fs "800001c0" {
- tmp = fs "801001ff" null null;
- lib = fs "800001c0" {
- hakurei = fs "800001c0" {
- u0 = fs "800001c0" {
- a2 = fs "800001c0" {
- ".cache" = fs "800001ed" { ".keep" = fs "80001ff" null ""; } null;
- ".config" = fs "800001ed" {
- "environment.d" = fs "800001ed" { "10-home-manager.conf" = fs "80001ff" null null; } null;
- systemd = fs "800001ed" {
- user = fs "800001ed" { "tray.target" = fs "80001ff" null null; } null;
- } null;
- } null;
- ".local" = fs "800001ed" {
- state = fs "800001ed" {
- ".keep" = fs "80001ff" null "";
- home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
- nix = fs "800001ed" {
- profiles = fs "800001ed" {
- profile = fs "80001ff" null null;
- profile-1-link = fs "80001ff" null null;
- } null;
- } null;
- } null;
- } null;
- ".nix-defexpr" = fs "800001ed" {
- channels = fs "80001ff" null null;
- channels_root = fs "80001ff" null null;
- } null;
- ".nix-profile" = fs "80001ff" null null;
- } null;
- } null;
- } null;
- } null;
- cache = fs "800001ed" { private = fs "800001c0" null null; } null;
- } null;
- } null;
-
- mount = [
- (ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10002,gid=10002")
- (ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
- (ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10002,gid=10002")
- (ent "/" "/dev" "ro,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=10002,gid=10002")
- (ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/full" "/dev/full" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/random" "/dev/random" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/urandom" "/dev/urandom" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/tty" "/dev/tty" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,mode=620,ptmxmode=666")
- (ent ignore "/dev/console" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666")
- (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
- (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10002,gid=10002")
- (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,mode=755,uid=10002,gid=10002")
- (ent "/tmp/hakurei.0/runtime/2" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/" "/tmp" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10002,gid=10002")
- (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10002,gid=10002")
- (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10002,gid=10002")
- (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/tmp/.X11-unix" "/tmp/.X11-unix" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/sysroot/nix/.ro-store,upperdir=/sysroot/nix/.rw-store/upper,workdir=/sysroot/nix/.rw-store/work,uuid=on")
- (ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/class" "/sys/class" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
- (ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
- (ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/" "/.hakurei/.ro-store" "rw,relatime" "overlay" "overlay" "ro,lowerdir+=/host/nix/.ro-store,lowerdir+=/host/nix/.rw-store/upper,redirect_dir=nofollow,userxattr")
- (ent "/" "/.hakurei/store" "rw,relatime" "overlay" "overlay" "rw,lowerdir+=/host/nix/.ro-store,lowerdir+=/host/nix/.rw-store/upper,upperdir=/host/tmp/.hakurei-store-rw/upper,workdir=/host/tmp/.hakurei-store-rw/work,redirect_dir=nofollow,uuid=on,userxattr")
- (ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent "/var/lib/hakurei/u0/a2" "/var/lib/hakurei/u0/a2" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- (ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
- ];
-
- seccomp = true;
-
- try_socket = "/tmp/.X11-unix/X0";
- socket_abstract = true;
- socket_pathname = true;
- };
-}
diff --git a/test/sandbox/configuration.nix b/test/sandbox/configuration.nix
deleted file mode 100644
index bc189f7d..00000000
--- a/test/sandbox/configuration.nix
+++ /dev/null
@@ -1,113 +0,0 @@
-{
- lib,
- pkgs,
- config,
- ...
-}:
-let
- testProgram = pkgs.callPackage ./tool/package.nix { inherit (config.environment.hakurei.package) version; };
- testCases = import ./case pkgs.stdenv.hostPlatform.system lib testProgram;
-in
-{
- users.users = {
- alice = {
- isNormalUser = true;
- description = "Alice Foobar";
- password = "foobar";
- uid = 1000;
- };
- };
-
- home-manager.users.alice.home.stateVersion = "24.11";
-
- # Automatically login on tty1 as a normal user:
- services.getty.autologinUser = "alice";
-
- environment = {
- systemPackages = [
- # For checking seccomp outcome:
- testProgram
-
- # For checking pd outcome:
- (pkgs.writeShellScriptBin "check-sandbox-pd" ''
- hakurei -v exec hakurei-test \
- -p "/var/tmp/.hakurei-check-ok.0" \
- -t ${toString (builtins.toFile "hakurei-pd-want.json" (builtins.toJSON testCases.pd.want))} \
- -s ${testCases.pd.expectedFilter.${pkgs.stdenv.hostPlatform.system}} "$@"
- '')
- ];
-
- variables = {
- SWAYSOCK = "/tmp/sway-ipc.sock";
- WLR_RENDERER = "pixman";
- };
- };
-
- # Automatically configure and start Sway when logging in on tty1:
- programs.bash.loginShellInit = ''
- if [ "$(tty)" = "/dev/tty1" ]; then
- set -e
-
- mkdir -p ~/.config/sway
- (sed s/Mod4/Mod1/ /etc/sway/config &&
- echo 'output * bg ${pkgs.nixos-artwork.wallpapers.simple-light-gray.gnomeFilePath} fill' &&
- echo 'output Virtual-1 res 1680x1050') > ~/.config/sway/config
-
- sway --validate
- systemd-cat --identifier=session sway && touch /tmp/sway-exit-ok
- fi
- '';
-
- programs.sway.enable = true;
-
- virtualisation.qemu.options = [
- # Need to switch to a different GPU driver than the default one (-vga std) so that Sway can launch:
- "-vga none -device virtio-gpu-pci"
-
- # Increase performance:
- "-smp 8"
- ];
-
- environment.hakurei = {
- enable = true;
- stateDir = "/var/lib/hakurei";
- users.alice = 0;
-
- extraHomeConfig = {
- home.stateVersion = "23.05";
- };
-
- commonPaths = [
- {
- type = "bind";
- src = "/var/tmp";
- write = true;
- }
- {
- type = "bind";
- src = "/var/cache";
- write = true;
- }
- {
- type = "overlay";
- dst = "/.hakurei/.ro-store";
- lower = [
- "/nix/.ro-store"
- "/nix/.rw-store/upper"
- ];
- }
- {
- type = "overlay";
- dst = "/.hakurei/store";
- lower = [
- "/nix/.ro-store"
- "/nix/.rw-store/upper"
- ];
- upper = "/tmp/.hakurei-store-rw/upper";
- work = "/tmp/.hakurei-store-rw/work";
- }
- ];
-
- inherit (testCases) apps;
- };
-}
diff --git a/test/sandbox/default.nix b/test/sandbox/default.nix
deleted file mode 100644
index 47a59de9..00000000
--- a/test/sandbox/default.nix
+++ /dev/null
@@ -1,41 +0,0 @@
-{
- lib,
- testers,
-
- self,
- withRace ? false,
-}:
-
-testers.nixosTest {
- name = "hakurei-sandbox" + (if withRace then "-race" else "");
- nodes.machine =
- { options, pkgs, ... }:
- {
- # Run with Go race detector:
- environment.hakurei = lib.mkIf withRace rec {
- # race detector does not support static linking
- package = (pkgs.callPackage ../package.nix { }).overrideAttrs (previousAttrs: {
- env = previousAttrs.env // {
- GOFLAGS = previousAttrs.env.GOFLAGS + " -race";
- };
- });
- hsuPackage = options.environment.hakurei.hsuPackage.default.override { hakurei = package; };
- };
-
- imports = [
- ./configuration.nix
-
- self.nixosModules.hakurei
- self.inputs.home-manager.nixosModules.home-manager
- ];
- };
-
- # adapted from nixos sway integration tests
-
- # testScriptWithTypes:49: error: Cannot call function of unknown type
- # (machine.succeed if succeed else machine.execute)(
- # ^
- # Found 1 error in 1 file (checked 1 source file)
- skipTypeCheck = true;
- testScript = builtins.readFile ./test.py;
-}
diff --git a/test/sandbox/main.go b/test/sandbox/main.go
new file mode 100644
index 00000000..311b9f58
--- /dev/null
+++ b/test/sandbox/main.go
@@ -0,0 +1,410 @@
+//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,
+ username string,
+ files ...*os.File,
+) (pid int, done <-chan error) {
+ _serial := strconv.FormatUint(serial, 10)
+ _, done = testsuite.MustStartAs(
+ ctx, username, 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()
+ username := testsuite.GetUser().Username
+
+ // the signal handler does not wait for termination
+ ctx := context.Background()
+
+ 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.MustRunAs(
+ username, "-i",
+ "hakurei", "exec", "capsh", "--print",
+ )
+ wg.Go(func() {
+ defer log.Println("validated capabilities/securebits in user namespace")
+
+ testsuite.MustRunAs(
+ username, "-i",
+ "hakurei", "exec", "capsh", "--has-no-new-privs",
+ )
+
+ for _, p := range []byte{'a', 'b', 'i', 'p'} {
+ testsuite.MustFailAs(
+ username, "-i",
+ "hakurei", "exec", "capsh", "--has-"+string(p)+"=CAP_SYS_ADMIN",
+ )
+ }
+ testsuite.MustFailAs(
+ username, "-i",
+ "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(), username)
+ 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(), username, 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(username)
+ testsuite.MustStartSway(&swg, username, dbusEnv)
+ defer testsuite.TerminateSway(username)
+ testsuite.MustStartPipeWire(username, 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(
+ "sudo",
+ "-u", username,
+ "-C", "6",
+ "TERM=xterm",
+ testsuite.XDGRuntimeEnv,
+ testsuite.WaylandEnv,
+ "DISPLAY=:0",
+ dbusEnv,
+ "--",
+
+ "script", "/dev/null",
+ "-E", "always",
+ "-qec",
+ "hakurei run "+
+ "--identifier-fd=5"+
+ " 4 1>&3",
+ )
+ cmd.SysProcAttr = &syscall.SysProcAttr{
+ Pdeathsig: syscall.SIGTERM,
+ }
+ var output bytes.Buffer
+ cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, &output, &output
+
+ 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
new file mode 100644
index 00000000..ddabc71e
--- /dev/null
+++ b/test/sandbox/seccomp.patch
@@ -0,0 +1,18 @@
+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/test.py b/test/sandbox/test.py
deleted file mode 100644
index a431daab..00000000
--- a/test/sandbox/test.py
+++ /dev/null
@@ -1,88 +0,0 @@
-import json
-import shlex
-
-q = shlex.quote
-
-
-def swaymsg(command: str = "", succeed=True, type="command"):
- assert command != "" or type != "command", "Must specify command or type"
- shell = q(f"swaymsg -t {q(type)} -- {q(command)}")
- with machine.nested(
- f"sending swaymsg {shell!r}" + " (allowed to fail)" * (not succeed)
- ):
- ret = (machine.succeed if succeed else machine.execute)(
- f"su - alice -c {shell}"
- )
-
- # execute also returns a status code, but disregard.
- if not succeed:
- _, ret = ret
-
- if not succeed and not ret:
- return None
-
- parsed = json.loads(ret)
- return parsed
-
-
-def check_filter(check_offset, name, pname):
- pid = int(machine.wait_until_succeeds(f"pgrep -U {10000+check_offset} -x {pname}"))
- hash = machine.succeed(f"sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 WAYLAND_DISPLAY=wayland-1 check-sandbox-{name} hash")
- print(machine.succeed(f"hakurei-test -s {hash} filter {pid}"))
-
-
-start_all()
-machine.wait_for_unit("multi-user.target")
-
-# To check hakurei's version:
-print(machine.succeed("sudo -u alice -i hakurei version"))
-
-# Wait for Sway to complete startup:
-machine.wait_for_file("/run/user/1000/wayland-1")
-machine.wait_for_file("/tmp/sway-ipc.sock")
-
-# Check pd seccomp outcome:
-swaymsg("exec hakurei exec cat")
-check_filter(0, "pdlike", "cat")
-
-# Check fd leak:
-swaymsg("exec exec 127</proc/cmdline && hakurei -v exec sleep infinity")
-pd_identity0_sleep_pid = int(machine.wait_until_succeeds("pgrep -U 10000 -x sleep"))
-print(machine.succeed(f"hakurei-test fd {pd_identity0_sleep_pid}"))
-machine.succeed(f"kill -INT {pd_identity0_sleep_pid}")
-
-# Verify capabilities/securebits in user namespace:
-print(machine.succeed("sudo -u alice -i hakurei exec capsh --print"))
-print(machine.succeed("sudo -u alice -i hakurei exec capsh --has-no-new-privs"))
-print(machine.fail("sudo -u alice -i hakurei exec capsh --has-a=CAP_SYS_ADMIN"))
-print(machine.fail("sudo -u alice -i hakurei exec capsh --has-b=CAP_SYS_ADMIN"))
-print(machine.fail("sudo -u alice -i hakurei exec capsh --has-i=CAP_SYS_ADMIN"))
-print(machine.fail("sudo -u alice -i hakurei exec capsh --has-p=CAP_SYS_ADMIN"))
-print(machine.fail("sudo -u alice -i hakurei exec umount -R /dev"))
-
-# Check sandbox outcome:
-machine.succeed("install -dm0777 /tmp/.hakurei-store-rw/{upper,work}")
-check_offset = 0
-def check_sandbox(name):
- global check_offset
- swaymsg(f"exec script /dev/null -E always -qec check-sandbox-{name}")
- machine.wait_for_file(f"/var/tmp/.hakurei-check-ok.{check_offset}")
- check_filter(check_offset, name, "hakurei-test")
- check_offset += 1
-
-
-check_sandbox("pd")
-check_sandbox("preset")
-check_sandbox("tty")
-check_sandbox("mapuid")
-check_sandbox("device")
-check_sandbox("pdlike")
-
-# Exit Sway and verify process exit status 0:
-machine.wait_until_fails("pgrep -x hakurei")
-swaymsg("exit", succeed=False)
-machine.wait_for_file("/tmp/sway-exit-ok")
-
-# Print hakurei runDir contents:
-print(machine.fail("ls /run/user/1000/hakurei"))
-machine.succeed("find /tmp -maxdepth 1 -type d -name '.hakurei-shim-*' -print -exec false '{}' +")
diff --git a/test/sandbox/testdata/device.go b/test/sandbox/testdata/device.go
new file mode 100644
index 00000000..89feb819
--- /dev/null
+++ b/test/sandbox/testdata/device.go
@@ -0,0 +1,134 @@
+//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
new file mode 100644
index 00000000..fad4ec36
--- /dev/null
+++ b/test/sandbox/testdata/mapuid.go
@@ -0,0 +1,124 @@
+//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:1000:Hakurei:/.hakurei:/usr/bin/bash\n")},
+ "group": {Mode: 0600,
+ Data: new("hakurei:x:1000:\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
new file mode 100644
index 00000000..53d8062a
--- /dev/null
+++ b/test/sandbox/testdata/pdlike.go
@@ -0,0 +1,141 @@
+//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
new file mode 100644
index 00000000..c410e626
--- /dev/null
+++ b/test/sandbox/testdata/simple.go
@@ -0,0 +1,140 @@
+//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
new file mode 100644
index 00000000..e4e8643a
--- /dev/null
+++ b/test/sandbox/testdata/sum.go
@@ -0,0 +1,22 @@
+//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
new file mode 100644
index 00000000..bd751105
--- /dev/null
+++ b/test/sandbox/testdata/sum_amd64.go
@@ -0,0 +1,9 @@
+//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
new file mode 100644
index 00000000..1691828f
--- /dev/null
+++ b/test/sandbox/testdata/sum_arm64.go
@@ -0,0 +1,9 @@
+//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
new file mode 100644
index 00000000..3418d8ae
--- /dev/null
+++ b/test/sandbox/testdata/testdata.go
@@ -0,0 +1,125 @@
+//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
new file mode 100644
index 00000000..4788f484
--- /dev/null
+++ b/test/sandbox/testdata/tty.go
@@ -0,0 +1,145 @@
+//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
new file mode 100644
index 00000000..0782a5e0
--- /dev/null
+++ b/test/sandbox/tester/main.go
@@ -0,0 +1,224 @@
+//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
+}
diff --git a/test/sandbox/tool/main.go b/test/sandbox/tool/main.go
deleted file mode 100644
index 889142d4..00000000
--- a/test/sandbox/tool/main.go
+++ /dev/null
@@ -1,106 +0,0 @@
-//go:build testtool
-
-package main
-
-import (
- "flag"
- "fmt"
- "log"
- "os"
- "os/signal"
- "strconv"
- "strings"
- "syscall"
-
- "hakurei.app/test/internal/sandbox"
-)
-
-var (
- flagMarkerPath string
- flagTestCase string
- flagBpfHash string
-)
-
-func init() {
- flag.StringVar(&flagMarkerPath, "p", "/tmp/sandbox-ok", "Pathname of completion marker")
- flag.StringVar(&flagTestCase, "t", "", "Nix store path to test case file")
- flag.StringVar(&flagBpfHash, "s", "", "String representation of expected bpf sha512 hash")
-}
-
-func main() {
- log.SetFlags(0)
- log.SetPrefix("test: ")
- flag.Parse()
-
- args := flag.Args()
- if len(args) < 1 {
- s := make(chan os.Signal, 1)
- signal.Notify(s, syscall.SIGINT)
- go func() { <-s; log.Println("exiting on signal (likely from verifier)"); os.Exit(0) }()
-
- (&sandbox.T{FS: os.DirFS("/")}).MustCheckFile(flagTestCase)
- if _, err := os.Create(flagMarkerPath); err != nil {
- log.Fatalf("cannot create success marker: %v", err)
- }
- log.Printf("blocking for seccomp check (%s)", flagMarkerPath)
- select {}
- return
- }
-
- switch args[0] {
- case "filter":
- if len(args) != 2 {
- log.Fatal("invalid argument")
- }
-
- if pid, err := strconv.Atoi(strings.TrimSpace(args[1])); err != nil {
- log.Fatalf("%s", err)
- } else if pid < 1 {
- log.Fatalf("%d out of range", pid)
- } else {
- sandbox.MustCheckFilter(pid, flagBpfHash)
- if err = syscall.Kill(pid, syscall.SIGINT); err != nil {
- log.Fatalf("cannot signal check process: %v", err)
- }
- }
-
- case "hash": // this eases the pain of passing the hash to python
- fmt.Print(flagBpfHash)
-
- case "fd":
- if len(args) != 2 {
- log.Fatal("invalid argument")
- }
- prefix := fmt.Sprintf("/proc/%s/fd/", args[1])
-
- 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(prefix + ent.Name()); err != nil {
- log.Fatal(err.Error())
- }
- log.Printf("[FAIL] extra fd %d -> %s", fd, d)
- }
- }
- if fail {
- log.Fatal("[FAIL] file descriptors leaked")
- }
-
- default:
- log.Fatal("invalid argument")
- }
-}
diff --git a/test/sandbox/tool/package.nix b/test/sandbox/tool/package.nix
deleted file mode 100644
index bd57b432..00000000
--- a/test/sandbox/tool/package.nix
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- lib,
- buildGoModule,
- pkg-config,
- util-linux,
-
- version,
-}:
-buildGoModule rec {
- pname = "check-sandbox";
- inherit version;
-
- src = builtins.path {
- name = "${pname}-src";
- path = lib.cleanSource ../../.;
- filter = path: type: (type == "directory") || (type == "regular" && lib.hasSuffix ".go" path);
- };
- vendorHash = null;
-
- tags = [ "testtool" "tester" ];
-
- buildInputs = [ util-linux ];
- nativeBuildInputs = [ pkg-config ];
-
- preBuild = ''
- go mod init hakurei.app/test >& /dev/null
- '';
-
- postInstall = ''
- mv $out/bin/tool $out/bin/hakurei-test
- '';
-}