From 6f5833bf41dd08fb374f752b1c53959c92859b7a Mon Sep 17 00:00:00 2001 From: Ophestra Date: Wed, 9 Jul 2025 03:47:16 +0900 Subject: [PATCH] test/sandbox: verify seccomp on all test cases This change also makes seccomp hashes cross-platform. Signed-off-by: Ophestra --- container/seccomp/hash_amd64_test.go | 2 + container/seccomp/libseccomp_test.go | 1 + test/sandbox/assert.go | 33 +--- test/sandbox/case/default.nix | 7 +- test/sandbox/case/device.nix | 5 + test/sandbox/case/mapuid.nix | 5 + test/sandbox/case/pdlike.nix | 253 +++++++++++++++++++++++++++ test/sandbox/case/preset.nix | 5 + test/sandbox/case/tty.nix | 5 + test/sandbox/configuration.nix | 2 +- test/sandbox/ptrace.go | 48 +---- test/sandbox/test.py | 14 +- test/sandbox/tool/main.go | 48 ++++- 13 files changed, 343 insertions(+), 85 deletions(-) create mode 100644 test/sandbox/case/pdlike.nix diff --git a/container/seccomp/hash_amd64_test.go b/container/seccomp/hash_amd64_test.go index bc5f420..7ef2b1d 100644 --- a/container/seccomp/hash_amd64_test.go +++ b/container/seccomp/hash_amd64_test.go @@ -19,4 +19,6 @@ var bpfExpected = bpfLookup{ "39871b93ffafc8b979fcedc0b0c37b9e03922f5b02748dc5c3c17c92527f6e022ede1f48bff59246ea452c0d1de54827808b1a6f84f32bbde1aa02ae30eedcfa"), {0, PresetExt | PresetDenyDevel}: toHash( "c698b081ff957afe17a6d94374537d37f2a63f6f9dd75da7546542407a9e32476ebda3312ba7785d7f618542bcfaf27ca27dcc2dddba852069d28bcfe8cad39a"), + {0, PresetExt | PresetDenyNS | PresetDenyDevel}: toHash( + "0b76007476c1c9e25dbf674c29fdf609a1656a70063e49327654e1b5360ad3da06e1a3e32bf80e961c5516ad83d4b9e7e9bde876a93797e27627d2555c25858b"), } diff --git a/container/seccomp/libseccomp_test.go b/container/seccomp/libseccomp_test.go index 8135b59..8443ade 100644 --- a/container/seccomp/libseccomp_test.go +++ b/container/seccomp/libseccomp_test.go @@ -28,6 +28,7 @@ func TestExport(t *testing.T) { {"strict", 0, PresetStrict, false}, {"strict compat", 0, PresetDenyNS | PresetDenyTTY | PresetDenyDevel, false}, {"hakurei default", 0, PresetExt | PresetDenyDevel, false}, + {"hakurei tty", 0, PresetExt | PresetDenyNS | PresetDenyDevel, false}, } buf := make([]byte, 8) diff --git a/test/sandbox/assert.go b/test/sandbox/assert.go index 388d232..57d1703 100644 --- a/test/sandbox/assert.go +++ b/test/sandbox/assert.go @@ -17,7 +17,6 @@ import ( "log" "os" "syscall" - "time" ) var ( @@ -42,13 +41,10 @@ type T struct { MountsPath string } -func (t *T) MustCheckFile(wantFilePath, markerPath string) { +func (t *T) MustCheckFile(wantFilePath string) { var want *TestCase mustDecode(wantFilePath, &want) t.MustCheck(want) - if _, err := os.Create(markerPath); err != nil { - fatalf("cannot create success marker: %v", err) - } } func (t *T) MustCheck(want *TestCase) { @@ -167,31 +163,10 @@ func CheckFilter(pid int, want string) error { }() h := sha512.New() - { - getFilter: - buf, err := getFilter[[8]byte](pid, 0) - /* this is not how ESRCH should be handled: the manpage advises the - use of waitpid, however that is not applicable for attaching to an - arbitrary process, and spawning target process here is not easily - possible under the current testing framework; - - despite checking for /proc/pid/status indicating state t (tracing stop), - it does not appear to be directly related to the internal state used to - determine whether a process is ready to accept ptrace operations, it also - introduces a TOCTOU that is irrelevant in the testing vm; this behaviour - is kept anyway as it reduces the average iterations required here; - - since this code is only ever compiled into the test program, whatever - implications this ugliness might have should not hurt anyone */ - if errors.Is(err, syscall.ESRCH) { - time.Sleep(100 * time.Millisecond) - goto getFilter - } - - if err != nil { - return err - } + if buf, err := getFilter[[8]byte](pid, 0); err != nil { + return err + } else { for _, b := range buf { h.Write(b[:]) } diff --git a/test/sandbox/case/default.nix b/test/sandbox/case/default.nix index 05a13de..6c7cf12 100644 --- a/test/sandbox/case/default.nix +++ b/test/sandbox/case/default.nix @@ -1,4 +1,4 @@ -lib: testProgram: +system: lib: testProgram: let fs = mode: dir: data: { mode = lib.fromHexString mode; @@ -43,13 +43,17 @@ let device mapRealUid useCommonPaths + userns ; share = testProgram; packages = [ ]; path = "${testProgram}/bin/hakurei-test"; args = [ "test" + "-t" (toString (builtins.toFile "hakurei-${tc.name}-want.json" (builtins.toJSON tc.want))) + "-s" + tc.expectedFilter.${system} ]; }; @@ -60,4 +64,5 @@ in ${testCaseName "tty"} = callTestCase ./tty.nix 2; ${testCaseName "mapuid"} = callTestCase ./mapuid.nix 3; ${testCaseName "device"} = callTestCase ./device.nix 4; + ${testCaseName "pdlike"} = callTestCase ./pdlike.nix 5; } diff --git a/test/sandbox/case/device.nix b/test/sandbox/case/device.nix index 9765fd5..0d1ac32 100644 --- a/test/sandbox/case/device.nix +++ b/test/sandbox/case/device.nix @@ -9,6 +9,11 @@ device = true; mapRealUid = false; useCommonPaths = true; + userns = false; + + expectedFilter = { # 0, PresetStrict + x86_64-linux = "e880298df2bd6751d0040fc21bc0ed4c00f95dc0d7ba506c244d8b8cf6866dba8ef4a33296f287b66cccc1d78e97026597f84cc7dec1573e148960fbd35cd735"; + }; want = { env = [ diff --git a/test/sandbox/case/mapuid.nix b/test/sandbox/case/mapuid.nix index af70cb3..de64e56 100644 --- a/test/sandbox/case/mapuid.nix +++ b/test/sandbox/case/mapuid.nix @@ -9,6 +9,11 @@ device = false; mapRealUid = true; useCommonPaths = true; + userns = false; + + expectedFilter = { # 0, PresetStrict + x86_64-linux = "e880298df2bd6751d0040fc21bc0ed4c00f95dc0d7ba506c244d8b8cf6866dba8ef4a33296f287b66cccc1d78e97026597f84cc7dec1573e148960fbd35cd735"; + }; want = { env = [ diff --git a/test/sandbox/case/pdlike.nix b/test/sandbox/case/pdlike.nix new file mode 100644 index 0000000..34eac55 --- /dev/null +++ b/test/sandbox/case/pdlike.nix @@ -0,0 +1,253 @@ +{ + fs, + ent, + ignore, +}: +{ + name = "pdlike"; + tty = true; + device = false; + mapRealUid = false; + useCommonPaths = false; + userns = true; + + expectedFilter = { # 0, PresetExt | PresetDenyDevel + x86_64-linux = "c698b081ff957afe17a6d94374537d37f2a63f6f9dd75da7546542407a9e32476ebda3312ba7785d7f618542bcfaf27ca27dcc2dddba852069d28bcfe8cad39a"; + }; + + want = { + env = [ + "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus" + "HOME=/var/lib/hakurei/u0/a5" + "PULSE_SERVER=unix:/run/user/65534/pulse/native" + "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=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; + dri = fs "800001ed" { + 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; + } 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 "800001ed" { } 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; + "fonts" = fs "80001ff" null null; + "fstab" = fs "80001ff" null null; + "hsurc" = fs "80001ff" null null; + "fuse.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; + "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 "10001b6" null null; } null; + wayland-0 = fs "1000038" null null; + } null; + } null; + } null; + sys = fs "800001c0" { + block = fs "800001ed" { + fd0 = 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; + sr0 = fs "80001ff" null null; + vda = fs "80001ff" null null; + } 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" { + 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" { + share = fs "800001ed" { + dbus-1 = fs "800001ed" { + services = fs "800001ed" { + "ca.desrt.dconf.service" = fs "80001ff" null null; + } null; + } null; + } null; + 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" { + home-manager = fs "80001ff" null null; + home-manager-1-link = fs "80001ff" null null; + 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; + run = fs "800001ed" { nscd = fs "800001ed" { } null; } null; + } null; + } null; + + mount = [ + (ent "/sysroot" "/" "rw,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000005,gid=1000005") + (ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw") + (ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000005,gid=1000005") + (ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000005,gid=1000005") + (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 "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") + (ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") + (ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/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 "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") + (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000005,gid=1000005") + (ent "/tmp/hakurei.1000/runtime/5" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") + (ent "/tmp/hakurei.1000/tmpdir/5" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") + (ent "/var/lib/hakurei/u0/a5" "/var/lib/hakurei/u0/a5" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") + (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000005,gid=1000005") + (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000005,gid=1000005") + (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") + (ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore) + (ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") + (ent "/" "/var/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8k,mode=755,uid=1000005,gid=1000005") + ]; + + seccomp = true; + }; +} diff --git a/test/sandbox/case/preset.nix b/test/sandbox/case/preset.nix index a2c2eb4..18c8fa3 100644 --- a/test/sandbox/case/preset.nix +++ b/test/sandbox/case/preset.nix @@ -9,6 +9,11 @@ device = false; mapRealUid = false; useCommonPaths = false; + userns = false; + + expectedFilter = { # 0, PresetStrict + x86_64-linux = "e880298df2bd6751d0040fc21bc0ed4c00f95dc0d7ba506c244d8b8cf6866dba8ef4a33296f287b66cccc1d78e97026597f84cc7dec1573e148960fbd35cd735"; + }; want = { env = [ diff --git a/test/sandbox/case/tty.nix b/test/sandbox/case/tty.nix index 3132d25..b7643ed 100644 --- a/test/sandbox/case/tty.nix +++ b/test/sandbox/case/tty.nix @@ -9,6 +9,11 @@ device = false; mapRealUid = false; useCommonPaths = true; + userns = false; + + expectedFilter = { # 0, PresetExt | PresetDenyNS | PresetDenyDevel + x86_64-linux = "0b76007476c1c9e25dbf674c29fdf609a1656a70063e49327654e1b5360ad3da06e1a3e32bf80e961c5516ad83d4b9e7e9bde876a93797e27627d2555c25858b"; + }; want = { env = [ diff --git a/test/sandbox/configuration.nix b/test/sandbox/configuration.nix index c50363c..cfc1364 100644 --- a/test/sandbox/configuration.nix +++ b/test/sandbox/configuration.nix @@ -75,6 +75,6 @@ in } ]; - apps = import ./case lib testProgram; + apps = import ./case pkgs.system lib testProgram; }; } diff --git a/test/sandbox/ptrace.go b/test/sandbox/ptrace.go index 3f1acad..8272ff5 100644 --- a/test/sandbox/ptrace.go +++ b/test/sandbox/ptrace.go @@ -3,13 +3,9 @@ package sandbox import ( - "bufio" + "errors" "fmt" - "io" - "os" - "strings" "syscall" - "time" "unsafe" ) @@ -41,49 +37,19 @@ func ptrace(op uintptr, pid, addr int, data unsafe.Pointer) (r uintptr, errno sy } func ptraceAttach(pid int) error { - const ( - statePrefix = "State:" - stateSuffix = "t (tracing stop)" - ) - - var r io.ReadSeekCloser - if f, err := os.Open(fmt.Sprintf("/proc/%d/status", pid)); err != nil { - return err - } else { - r = f - } - if _, errno := ptrace(PTRACE_ATTACH, pid, 0, nil); errno != 0 { return &ptraceError{"PTRACE_ATTACH", errno} } - // ugly! but there does not appear to be another way + var status syscall.WaitStatus for { - time.Sleep(10 * time.Millisecond) - - if _, err := r.Seek(0, io.SeekStart); err != nil { - return err - } - s := bufio.NewScanner(r) - - var found bool - for s.Scan() { - found = strings.HasPrefix(s.Text(), statePrefix) - if found { - break + if _, err := syscall.Wait4(pid, &status, syscall.WALL, nil); err != nil { + if errors.Is(err, syscall.EINTR) { + continue } + fatalf("cannot waitpid: %v", err) } - if err := s.Err(); err != nil { - return err - } - - if !found { - return syscall.EBADE - } - - if strings.HasSuffix(s.Text(), stateSuffix) { - break - } + break } return nil diff --git a/test/sandbox/test.py b/test/sandbox/test.py index 93fb824..f06e2b5 100644 --- a/test/sandbox/test.py +++ b/test/sandbox/test.py @@ -25,6 +25,12 @@ def swaymsg(command: str = "", succeed=True, type="command"): return parsed +def check_filter(check_offset, name, pname): + pid = int(machine.wait_until_succeeds(f"pgrep -U {1000000+check_offset} -x {pname}", timeout=15)) + hash = machine.succeed(f"sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 WAYLAND_DISPLAY=wayland-1 check-sandbox-{name} hash 2>/dev/null") + print(machine.succeed(f"hakurei-test -s {hash} filter {pid}")) + + start_all() machine.wait_for_unit("multi-user.target") @@ -35,11 +41,9 @@ print(machine.succeed("sudo -u alice -i hakurei version")) machine.wait_for_file("/run/user/1000/wayland-1") machine.wait_for_file("/tmp/sway-ipc.sock") -# Check seccomp outcome: +# Check pd seccomp outcome: swaymsg("exec hakurei run cat") -pid = int(machine.wait_until_succeeds("pgrep -U 1000000 -x cat", timeout=5)) -print(machine.succeed(f"hakurei-test filter {pid} c698b081ff957afe17a6d94374537d37f2a63f6f9dd75da7546542407a9e32476ebda3312ba7785d7f618542bcfaf27ca27dcc2dddba852069d28bcfe8cad39a &>/dev/stdout", timeout=5)) -machine.succeed(f"kill -TERM {pid}") +check_filter(0, "pdlike", "cat") # Verify capabilities/securebits in user namespace: print(machine.succeed("sudo -u alice -i hakurei run capsh --print")) @@ -57,12 +61,14 @@ def check_sandbox(name): check_offset += 1 swaymsg(f"exec script /dev/null -E always -qec check-sandbox-{name}") machine.wait_for_file(f"/tmp/hakurei.1000/tmpdir/{check_offset}/sandbox-ok", timeout=15) + check_filter(check_offset, name, "hakurei-test") check_sandbox("preset") check_sandbox("tty") check_sandbox("mapuid") check_sandbox("device") +check_sandbox("pdlike") # Exit Sway and verify process exit status 0: swaymsg("exit", succeed=False) diff --git a/test/sandbox/tool/main.go b/test/sandbox/tool/main.go index f53950c..37167ab 100644 --- a/test/sandbox/tool/main.go +++ b/test/sandbox/tool/main.go @@ -3,39 +3,69 @@ package main import ( + "flag" + "fmt" "log" "os" + "os/signal" "strconv" "strings" + "syscall" "hakurei.app/test/sandbox" ) +var ( + flagTestCase string + flagBpfHash string +) + +func init() { + 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() - if len(os.Args) < 2 { - log.Fatal("invalid argument") + 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("/tmp/sandbox-ok"); err != nil { + log.Fatalf("cannot create success marker: %v", err) + } + log.Println("blocking for seccomp check") + select {} + return } - switch os.Args[1] { + switch args[0] { case "filter": - if len(os.Args) != 4 { + if len(args) != 2 { log.Fatal("invalid argument") } - if pid, err := strconv.Atoi(strings.TrimSpace(os.Args[2])); err != nil { + 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, os.Args[3]) - return + 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) + default: - (&sandbox.T{FS: os.DirFS("/")}).MustCheckFile(os.Args[1], "/tmp/sandbox-ok") - return + log.Fatal("invalid argument") } }