From a7383510fb05abc98b992240cb76ad4b6c598956 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sun, 4 Oct 2026 01:05:10 +0900 Subject: 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 --- .gitea/workflows/test.yml | 2 +- internal/workflows/doc.go | 18 +- internal/workflows/step.go | 3 +- internal/workflows/test.go | 45 +++- test/flake.nix | 6 - test/internal/sandbox/assert.go | 247 --------------------- test/internal/sandbox/assert_test.go | 34 --- test/internal/sandbox/seccomp.go | 46 ---- test/internal/testsuite/proc.go | 23 +- test/internal/testsuite/ptrace.go | 44 ++-- test/internal/testsuite/testsuite.go | 231 ++++++++++++++++++++ test/sandbox/case/default.nix | 97 --------- test/sandbox/case/device.nix | 255 ---------------------- test/sandbox/case/mapuid.nix | 282 ------------------------ test/sandbox/case/pd.nix | 206 ------------------ test/sandbox/case/pdlike.nix | 277 ----------------------- test/sandbox/case/preset.nix | 274 ----------------------- test/sandbox/case/tty.nix | 288 ------------------------ test/sandbox/configuration.nix | 113 ---------- test/sandbox/default.nix | 41 ---- test/sandbox/main.go | 410 +++++++++++++++++++++++++++++++++++ test/sandbox/seccomp.patch | 18 ++ test/sandbox/test.py | 88 -------- test/sandbox/testdata/device.go | 134 ++++++++++++ test/sandbox/testdata/mapuid.go | 124 +++++++++++ test/sandbox/testdata/pdlike.go | 141 ++++++++++++ test/sandbox/testdata/simple.go | 140 ++++++++++++ test/sandbox/testdata/sum.go | 22 ++ test/sandbox/testdata/sum_amd64.go | 9 + test/sandbox/testdata/sum_arm64.go | 9 + test/sandbox/testdata/testdata.go | 125 +++++++++++ test/sandbox/testdata/tty.go | 145 +++++++++++++ test/sandbox/tester/main.go | 224 +++++++++++++++++++ test/sandbox/tool/main.go | 106 --------- test/sandbox/tool/package.nix | 32 --- 35 files changed, 1822 insertions(+), 2437 deletions(-) delete mode 100644 test/internal/sandbox/assert.go delete mode 100644 test/internal/sandbox/assert_test.go delete mode 100644 test/internal/sandbox/seccomp.go delete mode 100644 test/sandbox/case/default.nix delete mode 100644 test/sandbox/case/device.nix delete mode 100644 test/sandbox/case/mapuid.nix delete mode 100644 test/sandbox/case/pd.nix delete mode 100644 test/sandbox/case/pdlike.nix delete mode 100644 test/sandbox/case/preset.nix delete mode 100644 test/sandbox/case/tty.nix delete mode 100644 test/sandbox/configuration.nix delete mode 100644 test/sandbox/default.nix create mode 100644 test/sandbox/main.go create mode 100644 test/sandbox/seccomp.patch delete mode 100644 test/sandbox/test.py create mode 100644 test/sandbox/testdata/device.go create mode 100644 test/sandbox/testdata/mapuid.go create mode 100644 test/sandbox/testdata/pdlike.go create mode 100644 test/sandbox/testdata/simple.go create mode 100644 test/sandbox/testdata/sum.go create mode 100644 test/sandbox/testdata/sum_amd64.go create mode 100644 test/sandbox/testdata/sum_arm64.go create mode 100644 test/sandbox/testdata/testdata.go create mode 100644 test/sandbox/testdata/tty.go create mode 100644 test/sandbox/tester/main.go delete mode 100644 test/sandbox/tool/main.go delete mode 100644 test/sandbox/tool/package.nix diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index f1547782..4e584550 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -1 +1 @@ -{"name":"Test","on":["push"],"jobs":{"hakurei":{"name":"Hakurei","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.hakurei"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-vm-output","path":"result/*","retention-days":1}}]},"race":{"name":"Hakurei (race detector)","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.race"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-race-vm-output","path":"result/*","retention-days":1}}]},"sandbox":{"name":"Sandbox","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sandbox"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"sandbox-vm-output","path":"result/*","retention-days":1}}]},"sandbox-race":{"name":"Sandbox (race detector)","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sandbox-race"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"sandbox-race-vm-output","path":"result/*","retention-days":1}}]},"sharefs":{"name":"ShareFS","runs-on":"rosa","steps":[{"name":"Fix container filesystem","run":"rm /var/run \u0026\u0026 ln -sf ../run /var"},{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Set up Go toolchain","uses":"actions/setup-go@v6","with":{"go-version-file":"go.mod"}},{"name":"Install packages","uses":"awalsh128/cache-apt-pkgs-action@v1","with":{"packages":"fuse3 fsmark","version":0,"execute_install_scripts":true}},{"name":"Request distribution","id":"dist","run":"HAKUREI_REV=\"$(git rev-parse --short HEAD)\" \u0026\u0026 /rosa/bin/mbf ci dist -o result . \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" \u0026\u0026 echo \"rev=$HAKUREI_REV\" \u003e\u003e \"$GITHUB_OUTPUT\""},{"name":"Install hakurei","run":"HAKUREI_VERSION=\"$(cat cmd/dist/VERSION)-${{ steps.dist.outputs.rev }}\" \u0026\u0026 tar xf result/hakurei-$HAKUREI_VERSION*-amd64.tar.gz \u0026\u0026 ./hakurei-$HAKUREI_VERSION*-amd64/install.sh \u0026\u0026 sudo -u ubuntu hakurei version \u0026\u0026 echo 'Defaults closefrom_override' \u003e /etc/sudoers.d/closefrom_override \u0026\u0026 mkdir /var/empty"},{"name":"Mount sharefs","run":"useradd -ru 1023 -md /var/lib/sdcard -k /var/empty -s /sbin/nologin media_rw \u0026\u0026 install -dm0 /sdcard \u0026\u0026 sharefs -o rw,noexec,nosuid,nodev,noatime,allow_other,mkdir,source=/var/lib/sdcard,setuid=1023,setgid=1023 /sdcard"},{"name":"Compile and run test suite","run":"sharefs -V \u0026\u0026 rm -rf result \u0026\u0026 go run -tags=testsuite ./test/sharefs"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"fs_mark","path":"result/*","retention-days":1}}]},"check":{"name":"Flake checks","needs":["hakurei","race","sandbox","sandbox-race"],"runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run checks","run":"nix --print-build-logs --experimental-features 'nix-command flakes' flake check ./test"}]},"dist":{"name":"Create distribution","runs-on":"rosa","steps":[{"name":"Fix container filesystem","run":"rm /var/run \u0026\u0026 ln -sf ../run /var"},{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Request distribution","id":"dist-test","run":"HAKUREI_REV=\"$(git rev-parse --short HEAD)\" \u0026\u0026 /rosa/bin/mbf ci dist -o result . \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" \u0026\u0026 echo \"rev=$HAKUREI_REV\" \u003e\u003e \"$GITHUB_OUTPUT\""},{"name":"Upload distribution","uses":"actions/upload-artifact@v3","with":{"name":"dist-${{ steps.dist-test.outputs.rev }}","path":"result/*","retention-days":1}}]}}} +{"name":"Test","on":["push"],"jobs":{"hakurei":{"name":"Hakurei (legacy)","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.hakurei"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-vm-output","path":"result/*","retention-days":1}}]},"race":{"name":"Hakurei (legacy with race instrument)","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.race"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-race-vm-output","path":"result/*","retention-days":1}}]},"sandbox":{"name":"Sandbox","runs-on":"rosa","steps":[{"name":"Fix container filesystem","run":"rm /var/run \u0026\u0026 ln -sf ../run /var"},{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Set up Go toolchain","uses":"actions/setup-go@v6","with":{"go-version-file":"go.mod"}},{"name":"Install packages","uses":"awalsh128/cache-apt-pkgs-action@v1","with":{"add-repository":"ppa:savoury1/pipewire","packages":"libmount-dev sway xwayland xdg-dbus-proxy pipewire","version":0,"execute_install_scripts":true}},{"name":"Request distribution","id":"dist","run":"HAKUREI_REV=\"$(git rev-parse --short HEAD)\" \u0026\u0026 /rosa/bin/mbf ci dist -o result . \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" \u0026\u0026 echo \"rev=$HAKUREI_REV\" \u003e\u003e \"$GITHUB_OUTPUT\""},{"name":"Install hakurei","run":"HAKUREI_VERSION=\"$(cat cmd/dist/VERSION)-${{ steps.dist.outputs.rev }}\" \u0026\u0026 tar xf result/hakurei-$HAKUREI_VERSION*-amd64.tar.gz \u0026\u0026 ./hakurei-$HAKUREI_VERSION*-amd64/install.sh \u0026\u0026 sudo -u ubuntu hakurei version \u0026\u0026 echo 'Defaults closefrom_override' \u003e /etc/sudoers.d/closefrom_override \u0026\u0026 mkdir /var/empty"},{"name":"Compile and run test suite","run":"rm -rf result \u0026\u0026 go run -tags=testsuite ./test/sandbox"}]},"sandbox-race":{"name":"Sandbox (with race instrument)","runs-on":"rosa","steps":[{"name":"Fix container filesystem","run":"rm /var/run \u0026\u0026 ln -sf ../run /var"},{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Set up Go toolchain","uses":"actions/setup-go@v6","with":{"go-version-file":"go.mod"}},{"name":"Install packages","uses":"awalsh128/cache-apt-pkgs-action@v1","with":{"add-repository":"ppa:savoury1/pipewire","packages":"libmount-dev sway xwayland xdg-dbus-proxy pipewire","version":0,"execute_install_scripts":true}},{"name":"Request distribution","id":"dist","run":"HAKUREI_REV=\"$(git rev-parse --short HEAD)\" \u0026\u0026 /rosa/bin/mbf ci race -o result . \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" \u0026\u0026 echo \"rev=$HAKUREI_REV\" \u003e\u003e \"$GITHUB_OUTPUT\""},{"name":"Install hakurei","run":"HAKUREI_VERSION=\"$(cat cmd/dist/VERSION)-${{ steps.dist.outputs.rev }}\" \u0026\u0026 tar xf result/hakurei-$HAKUREI_VERSION*-amd64.tar.gz \u0026\u0026 ./hakurei-$HAKUREI_VERSION*-amd64/install.sh \u0026\u0026 sudo -u ubuntu hakurei version \u0026\u0026 echo 'Defaults closefrom_override' \u003e /etc/sudoers.d/closefrom_override \u0026\u0026 mkdir /var/empty"},{"name":"Compile and run test suite","run":"rm -rf result \u0026\u0026 go run -tags=testsuite ./test/sandbox"}]},"sharefs":{"name":"ShareFS","runs-on":"rosa","steps":[{"name":"Fix container filesystem","run":"rm /var/run \u0026\u0026 ln -sf ../run /var"},{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Set up Go toolchain","uses":"actions/setup-go@v6","with":{"go-version-file":"go.mod"}},{"name":"Install packages","uses":"awalsh128/cache-apt-pkgs-action@v1","with":{"add-repository":"","packages":"fuse3 fsmark","version":0,"execute_install_scripts":true}},{"name":"Request distribution","id":"dist","run":"HAKUREI_REV=\"$(git rev-parse --short HEAD)\" \u0026\u0026 /rosa/bin/mbf ci dist -o result . \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" \u0026\u0026 echo \"rev=$HAKUREI_REV\" \u003e\u003e \"$GITHUB_OUTPUT\""},{"name":"Install hakurei","run":"HAKUREI_VERSION=\"$(cat cmd/dist/VERSION)-${{ steps.dist.outputs.rev }}\" \u0026\u0026 tar xf result/hakurei-$HAKUREI_VERSION*-amd64.tar.gz \u0026\u0026 ./hakurei-$HAKUREI_VERSION*-amd64/install.sh \u0026\u0026 sudo -u ubuntu hakurei version \u0026\u0026 echo 'Defaults closefrom_override' \u003e /etc/sudoers.d/closefrom_override \u0026\u0026 mkdir /var/empty"},{"name":"Mount sharefs","run":"useradd -ru 1023 -md /var/lib/sdcard -k /var/empty -s /sbin/nologin media_rw \u0026\u0026 install -dm0 /sdcard \u0026\u0026 sharefs -o rw,noexec,nosuid,nodev,noatime,allow_other,mkdir,source=/var/lib/sdcard,setuid=1023,setgid=1023 /sdcard"},{"name":"Compile and run test suite","run":"sharefs -V \u0026\u0026 rm -rf result \u0026\u0026 go run -tags=testsuite ./test/sharefs"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"fs_mark","path":"result/*","retention-days":1}}]},"check":{"name":"Flake checks","needs":["hakurei","race"],"runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run checks","run":"nix --print-build-logs --experimental-features 'nix-command flakes' flake check ./test"}]},"dist":{"name":"Create distribution","runs-on":"rosa","steps":[{"name":"Fix container filesystem","run":"rm /var/run \u0026\u0026 ln -sf ../run /var"},{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Request distribution","id":"dist-test","run":"HAKUREI_REV=\"$(git rev-parse --short HEAD)\" \u0026\u0026 /rosa/bin/mbf ci dist -o result . \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" \u0026\u0026 echo \"rev=$HAKUREI_REV\" \u003e\u003e \"$GITHUB_OUTPUT\""},{"name":"Upload distribution","uses":"actions/upload-artifact@v3","with":{"name":"dist-${{ steps.dist-test.outputs.rev }}","path":"result/*","retention-days":1}}]}}} diff --git a/internal/workflows/doc.go b/internal/workflows/doc.go index e34c59c8..a8f18ab4 100644 --- a/internal/workflows/doc.go +++ b/internal/workflows/doc.go @@ -20,7 +20,8 @@ The Gitea act_runner simply bind mounts whatever socket it sees into the container. With a regular docker daemon, this allows not only a simple container escape, but also privilege escalation as unconstrained root in the init namespace. To mitigate this, set up an unprivileged podman daemon and expose its -socket to the container instead. +socket to the container instead. Since mountinfo always use credentials from the +init user namespace, subordinate user and group ID must always be 100000. On Alpine Linux, this is achieved by: @@ -67,6 +68,7 @@ Before starting the container, configure act_runner via config.yaml: -v /var/lib/rosa:/rosa --security-opt='unmask=/proc/*' --cap-add=SYS_ADMIN + --cap-add=SYS_PTRACE --device=/dev/kvm --device=/dev/fuse valid_volumes: @@ -76,8 +78,9 @@ where /var/lib/rosa is the absolute pathname of the cache directory in the init namespace. Setting MBF_POISON_OPEN enables cmd/mbf to run as root. It is also a good idea here to set runner.capacity to reflect the capacity of the guest, so jobs can be consumed quicker. Removing mount points covering /proc enables -testing of cmd/hakurei. Exposing the fuse device and adding capability SYS_ADMIN -enables testing of cmd/sharefs. +testing of cmd/hakurei. Exposing the fuse device and adding capability +CAP_SYS_ADMIN enables testing of cmd/sharefs. Adding capability CAP_SYS_PTRACE +enables dumping seccomp filters via ptrace on the patched kernel. Build a statically-linked cmd/mbf: @@ -120,6 +123,15 @@ this can be achieved by the init script: It is often a good idea to populate the cache from a mirror service before the first workflow job is started and re-populate it after every cmd/mbf update. +# Configuring the kernel + +In order to attach to the container process, the sysctl kernel.yama.ptrace_scope +must be set to 0. After which, apply the patch test/sandbox/seccomp.patch to +your kernel sources, compile and install the new kernel. Refer to +https://wiki.alpinelinux.org/wiki/Custom_Kernel if the guest runs Alpine Linux. +If running podman or docker as root, the patch is not required. Do not apply +this patch on a system meant to be secure. + # Security The design of Microsoft Github workflows is inherently insecure: it requires diff --git a/internal/workflows/step.go b/internal/workflows/step.go index 83d9c5f3..199f82d9 100644 --- a/internal/workflows/step.go +++ b/internal/workflows/step.go @@ -76,11 +76,12 @@ func newTestsuite(name, prefix string) Step { // newPackages returns a job for installing the specified packages with // best-effort caching. Package names must not contain spaces. -func newPackages(rev int, packages ...string) Step { +func newPackages(rev int, repos []string, packages ...string) Step { return Step{ Name: "Install packages", Uses: "awalsh128/cache-apt-pkgs-action@v1", With: []KV[any]{ + {"add-repository", strings.Join(repos, " ")}, {"packages", strings.Join(packages, " ")}, {"version", rev}, {"execute_install_scripts", true}, diff --git a/internal/workflows/test.go b/internal/workflows/test.go index 723cf463..c81574ea 100644 --- a/internal/workflows/test.go +++ b/internal/workflows/test.go @@ -8,7 +8,7 @@ var _ = (&Workflow{ Jobs: Map[Job]{ {"hakurei", Job{ - Name: "Hakurei", + Name: "Hakurei (legacy)", On: "nix", Steps: []Step{ @@ -19,7 +19,7 @@ var _ = (&Workflow{ }}, {"race", Job{ - Name: "Hakurei (race detector)", + Name: "Hakurei (legacy with race instrument)", On: "nix", Steps: []Step{ @@ -31,23 +31,46 @@ var _ = (&Workflow{ {"sandbox", Job{ Name: "Sandbox", - On: "nix", + On: "rosa", Steps: []Step{ + fixup, checkout, - newNixOSTest("sandbox"), - newUploadArtifact("test output", "sandbox-vm-output"), + toolchain, + newPackages(0, []string{"ppa:savoury1/pipewire"}, + "libmount-dev", + "sway", + "xwayland", + "xdg-dbus-proxy", + "pipewire", + ), + + newCIRequest("distribution", "dist -o result", "dist"), + install, + newTestsuite("sandbox", ""), }, }}, {"sandbox-race", Job{ - Name: "Sandbox (race detector)", - On: "nix", + Name: "Sandbox (with race instrument)", + On: "rosa", Steps: []Step{ + fixup, checkout, - newNixOSTest("sandbox-race"), - newUploadArtifact("test output", "sandbox-race-vm-output"), + toolchain, + + newPackages(0, []string{"ppa:savoury1/pipewire"}, + "libmount-dev", + "sway", + "xwayland", + "xdg-dbus-proxy", + "pipewire", + ), + + newCIRequest("distribution", "race -o result", "dist"), + install, + newTestsuite("sandbox", ""), }, }}, @@ -59,7 +82,7 @@ var _ = (&Workflow{ fixup, checkout, toolchain, - newPackages(0, "fuse3", "fsmark"), + newPackages(0, nil, "fuse3", "fsmark"), newCIRequest("distribution", "dist -o result", "dist"), install, @@ -90,8 +113,6 @@ var _ = (&Workflow{ Needs: []string{ "hakurei", "race", - "sandbox", - "sandbox-race", }, Steps: []Step{ 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 -*/ -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 +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 - ''; -} -- cgit v1.3.1