From 75e0c5d4061d68dc4802285ec137e464f4e01b17 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sun, 23 Mar 2025 14:14:45 +0900 Subject: test/sandbox: parse full test case This makes declaring multiple tests much cleaner. Signed-off-by: Ophestra --- test/configuration.nix | 23 ++-- test/sandbox/assert.go | 132 +++++++++++++-------- test/sandbox/assert.nix | 31 +++++ test/sandbox/case/default.nix | 57 +++++++++ test/sandbox/case/module-default.nix | 222 +++++++++++++++++++++++++++++++++++ test/sandbox/default.nix | 9 +- test/sandbox/fs.nix | 214 --------------------------------- test/sandbox/fs_test.go | 6 - test/sandbox/mount.go | 44 +++++-- test/sandbox/mount.nix | 79 ------------- test/sandbox/mount_test.go | 22 ++-- test/sandbox/seccomp.nix | 27 ----- test/test.py | 4 +- 13 files changed, 447 insertions(+), 423 deletions(-) create mode 100644 test/sandbox/assert.nix create mode 100644 test/sandbox/case/default.nix create mode 100644 test/sandbox/case/module-default.nix delete mode 100644 test/sandbox/fs.nix delete mode 100644 test/sandbox/mount.nix delete mode 100644 test/sandbox/seccomp.nix (limited to 'test') diff --git a/test/configuration.nix b/test/configuration.nix index f6c3d147..6bdc13ea 100644 --- a/test/configuration.nix +++ b/test/configuration.nix @@ -4,6 +4,12 @@ config, ... }: +let + testCases = import ./sandbox/case { + inherit (pkgs) lib callPackage foot; + inherit (config.environment.fortify.package) version; + }; +in { users.users = { alice = { @@ -102,21 +108,8 @@ home-manager = _: _: { home.stateVersion = "23.05"; }; apps = [ - { - name = "check-sandbox"; - verbose = true; - share = pkgs.foot; - packages = [ ]; - command = "${pkgs.callPackage ./sandbox { - inherit (config.environment.fortify.package) version; - }}"; - extraPaths = [ - { - src = "/proc/mounts"; - dst = "/.fortify/mounts"; - } - ]; - } + testCases.moduleDefault + { name = "ne-foot"; verbose = true; diff --git a/test/sandbox/assert.go b/test/sandbox/assert.go index 3a7b31ed..99ac9e8c 100644 --- a/test/sandbox/assert.go +++ b/test/sandbox/assert.go @@ -5,6 +5,7 @@ import ( "io/fs" "log" "os" + "slices" ) var ( @@ -16,76 +17,103 @@ var ( func printf(format string, v ...any) { printfFunc(format, v...) } func fatalf(format string, v ...any) { fatalfFunc(format, v...) } -func mustDecode(wantFile string, v any) { - if f, err := os.Open(wantFile); err != nil { - fatalf("cannot open %q: %v", wantFile, err) - } else if err = json.NewDecoder(f).Decode(v); err != nil { - fatalf("cannot decode %q: %v", wantFile, err) - } else if err = f.Close(); err != nil { - fatalf("cannot close %q: %v", wantFile, err) - } +type TestCase struct { + FS *FS `json:"fs"` + Mount []*Mntent `json:"mount"` + Seccomp bool `json:"seccomp"` +} + +type T struct { + FS fs.FS + + MountsPath, PMountsPath string } -func MustAssertMounts(name, hostMountsFile, wantFile string) { - hostMounts := make([]*Mntent, 0, 128) - if err := IterMounts(hostMountsFile, func(e *Mntent) { - hostMounts = append(hostMounts, e) - }); err != nil { - fatalf("cannot parse host mounts: %v", err) +func (t *T) MustCheckFile(wantFilePath string) { + var want *TestCase + mustDecode(wantFilePath, &want) + t.MustCheck(want) +} + +func (t *T) MustCheck(want *TestCase) { + if want.FS != nil && t.FS != nil { + if err := want.FS.Compare(".", t.FS); err != nil { + fatalf("%v", err) + } + } else { + printf("[SKIP] skipping fs check") } - var want []Mntent - mustDecode(wantFile, &want) + if want.Mount != nil && t.PMountsPath != "" { + pm := mustOpenMounts(t.PMountsPath) + passthruMounts := slices.AppendSeq(make([]*Mntent, 0, 128), pm.Entries()) + if err := pm.Err(); err != nil { + fatalf("cannot parse host mounts: %v", err) + } - for i := range want { - if want[i].Opts == "host_passthrough" { - for _, ent := range hostMounts { - if want[i].FSName == ent.FSName && want[i].Type == ent.Type { - // special case for tmpfs bind mounts - if want[i].FSName == "tmpfs" && want[i].Dir != ent.Dir { - continue - } + for _, e := range want.Mount { + if e.Opts == "host_passthrough" { + for _, ent := range passthruMounts { + if e.FSName == ent.FSName && e.Type == ent.Type { + // special case for tmpfs bind mounts + if e.FSName == "tmpfs" && e.Dir != ent.Dir { + continue + } - want[i].Opts = ent.Opts - goto out + e.Opts = ent.Opts + goto out + } } + fatalf("host passthrough missing %q", e.FSName) + out: } - fatalf("host passthrough missing %q", want[i].FSName) - out: } - } - i := 0 - if err := IterMounts(name, func(e *Mntent) { - if i == len(want) { - fatalf("got more than %d entries", i) + f := mustOpenMounts(t.MountsPath) + i := 0 + for e := range f.Entries() { + if i == len(want.Mount) { + fatalf("got more than %d entries", i) + } + if !e.Is(want.Mount[i]) { + fatalf("entry %d\n got: %s\nwant: %s", i, + e, want.Mount[i]) + } + printf("[ OK ] %s", e) + + i++ } - if !e.Is(&want[i]) { - fatalf("entry %d\n got: %s\nwant: %s", i, - e, &want[i]) + if err := f.Err(); err != nil { + fatalf("cannot parse mounts: %v", err) } - - printf("%s", e) - i++ - }); err != nil { - fatalf("cannot iterate mounts: %v", err) + } else { + printf("[SKIP] skipping mounts check") } -} -func MustAssertFS(e fs.FS, wantFile string) { - var want *FS - mustDecode(wantFile, &want) - if want == nil { - fatalf("invalid payload") + if want.Seccomp { + if TrySyscalls() != nil { + os.Exit(1) + } + } else { + printf("[SKIP] skipping seccomp check") } +} - if err := want.Compare(".", e); err != nil { - fatalf("%v", err) +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 MustAssertSeccomp() { - if TrySyscalls() != nil { - os.Exit(1) +func mustOpenMounts(name string) *MountsFile { + if f, err := OpenMounts(name); err != nil { + fatalf("cannot open mounts %q: %v", name, err) + panic("unreachable") + } else { + return f } } diff --git a/test/sandbox/assert.nix b/test/sandbox/assert.nix new file mode 100644 index 00000000..d45e4cb9 --- /dev/null +++ b/test/sandbox/assert.nix @@ -0,0 +1,31 @@ +{ + writeText, + buildGoModule, + + version, + name, + want, +}: +let + wantFile = writeText "fortify-${name}-want.json" (builtins.toJSON want); + mainFile = writeText "main.go" '' + package main + + import "os" + import "git.gensokyo.uk/security/fortify/test/sandbox" + + func main() { (&sandbox.T{FS: os.DirFS("/"), PMountsPath: "/.fortify/mounts"}).MustCheckFile("${wantFile}") } + ''; +in +buildGoModule { + pname = "fortify-${name}-check-sandbox"; + inherit version; + + src = ../.; + vendorHash = null; + + preBuild = '' + go mod init git.gensokyo.uk/security/fortify/test >& /dev/null + cp ${mainFile} main.go + ''; +} diff --git a/test/sandbox/case/default.nix b/test/sandbox/case/default.nix new file mode 100644 index 00000000..b65b6ed2 --- /dev/null +++ b/test/sandbox/case/default.nix @@ -0,0 +1,57 @@ +{ + lib, + callPackage, + foot, + + version, +}: +let + fs = mode: dir: data: { + mode = lib.fromHexString mode; + inherit + dir + data + ; + }; + + ent = fsname: dir: type: opts: freq: passno: { + inherit + fsname + dir + type + opts + freq + passno + ; + }; + + callTestCase = + path: + let + tc = import path { + inherit + fs + ent + ; + }; + in + { + name = "check-sandbox-${tc.name}"; + verbose = true; + share = foot; + packages = [ ]; + command = "${callPackage ../. { + inherit (tc) name want; + inherit version; + }}"; + extraPaths = [ + { + src = "/proc/mounts"; + dst = "/.fortify/mounts"; + } + ]; + }; +in +{ + moduleDefault = callTestCase ./module-default.nix; +} diff --git a/test/sandbox/case/module-default.nix b/test/sandbox/case/module-default.nix new file mode 100644 index 00000000..caca6745 --- /dev/null +++ b/test/sandbox/case/module-default.nix @@ -0,0 +1,222 @@ +{ fs, ent }: +{ + name = "module-default"; + + want = { + fs = fs "dead" { + ".fortify" = fs "800001ed" { + etc = fs "800001ed" null null; + sbin = fs "800001c0" { + fortify = fs "16d" null null; + init0 = fs "80001ff" null null; + } null; + mounts = fs "124" null null; + } null; + bin = fs "800001ed" { sh = fs "80001ff" null null; } null; + dev = fs "800001ed" { + 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 "800001c0" { + ".clean" = fs "80001ff" null null; + ".updated" = fs "80001ff" null null; + "NIXOS" = fs "80001ff" null null; + "X11" = fs "80001ff" null null; + "alsa" = 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; + "fsurc" = fs "80001ff" null null; + "fuse.conf" = fs "80001ff" null null; + "group" = fs "180" null "fortify: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:Fortify:/var/lib/fortify/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; + "profiles" = 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 "800001c0" { + current-system = fs "8000016d" null null; + opengl-driver = fs "8000016d" null null; + user = fs "800001ed" { + "65534" = fs "800001ed" { + 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" { + fortify = 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; } null; + ".local" = fs "800001ed" { + state = fs "800001ed" { + 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 "tmpfs" "/" "tmpfs" "rw,nosuid,nodev,relatime,uid=1000001,gid=1000001" 0 0) + (ent "proc" "/proc" "proc" "rw,nosuid,nodev,noexec,relatime" 0 0) + (ent "tmpfs" "/.fortify" "tmpfs" "rw,nosuid,nodev,relatime,size=4k,mode=755,uid=1000001,gid=1000001" 0 0) + (ent "tmpfs" "/dev" "tmpfs" "rw,nosuid,nodev,relatime,mode=755,uid=1000001,gid=1000001" 0 0) + (ent "devtmpfs" "/dev/null" "devtmpfs" "host_passthrough" 0 0) + (ent "devtmpfs" "/dev/zero" "devtmpfs" "host_passthrough" 0 0) + (ent "devtmpfs" "/dev/full" "devtmpfs" "host_passthrough" 0 0) + (ent "devtmpfs" "/dev/random" "devtmpfs" "host_passthrough" 0 0) + (ent "devtmpfs" "/dev/urandom" "devtmpfs" "host_passthrough" 0 0) + (ent "devtmpfs" "/dev/tty" "devtmpfs" "host_passthrough" 0 0) + (ent "devpts" "/dev/pts" "devpts" "rw,nosuid,noexec,relatime,mode=620,ptmxmode=666" 0 0) + (ent "mqueue" "/dev/mqueue" "mqueue" "rw,relatime" 0 0) + (ent "/dev/disk/by-label/nixos" "/bin" "ext4" "ro,nosuid,nodev,relatime" 0 0) + (ent "/dev/disk/by-label/nixos" "/usr/bin" "ext4" "ro,nosuid,nodev,relatime" 0 0) + (ent "overlay" "/nix/store" "overlay" "ro,nosuid,nodev,relatime,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on" 0 0) + (ent "overlay" "/run/current-system" "overlay" "ro,nosuid,nodev,relatime,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on" 0 0) + (ent "sysfs" "/sys/block" "sysfs" "ro,nosuid,nodev,noexec,relatime" 0 0) + (ent "sysfs" "/sys/bus" "sysfs" "ro,nosuid,nodev,noexec,relatime" 0 0) + (ent "sysfs" "/sys/class" "sysfs" "ro,nosuid,nodev,noexec,relatime" 0 0) + (ent "sysfs" "/sys/dev" "sysfs" "ro,nosuid,nodev,noexec,relatime" 0 0) + (ent "sysfs" "/sys/devices" "sysfs" "ro,nosuid,nodev,noexec,relatime" 0 0) + (ent "overlay" "/run/opengl-driver" "overlay" "ro,nosuid,nodev,relatime,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on" 0 0) + (ent "devtmpfs" "/dev/dri" "devtmpfs" "host_passthrough" 0 0) + (ent "proc" "/.fortify/mounts" "proc" "ro,nosuid,nodev,noexec,relatime" 0 0) + (ent "/dev/disk/by-label/nixos" "/.fortify/etc" "ext4" "ro,nosuid,nodev,relatime" 0 0) + (ent "tmpfs" "/run/user" "tmpfs" "rw,nosuid,nodev,relatime,size=1024k,mode=755,uid=1000001,gid=1000001" 0 0) + (ent "tmpfs" "/run/user/65534" "tmpfs" "rw,nosuid,nodev,relatime,size=8192k,mode=755,uid=1000001,gid=1000001" 0 0) + (ent "/dev/disk/by-label/nixos" "/tmp" "ext4" "rw,nosuid,nodev,relatime" 0 0) + (ent "/dev/disk/by-label/nixos" "/var/lib/fortify/u0/a1" "ext4" "rw,nosuid,nodev,relatime" 0 0) + (ent "tmpfs" "/etc/passwd" "tmpfs" "ro,nosuid,nodev,relatime,uid=1000001,gid=1000001" 0 0) + (ent "tmpfs" "/etc/group" "tmpfs" "ro,nosuid,nodev,relatime,uid=1000001,gid=1000001" 0 0) + (ent "/dev/disk/by-label/nixos" "/run/user/65534/wayland-0" "ext4" "ro,nosuid,nodev,relatime" 0 0) + (ent "tmpfs" "/run/user/65534/pulse/native" "tmpfs" "host_passthrough" 0 0) + (ent "/dev/disk/by-label/nixos" "/run/user/65534/bus" "ext4" "ro,nosuid,nodev,relatime" 0 0) + (ent "tmpfs" "/var/run/nscd" "tmpfs" "rw,nosuid,nodev,relatime,size=8k,mode=755,uid=1000001,gid=1000001" 0 0) + (ent "overlay" "/.fortify/sbin/fortify" "overlay" "ro,nosuid,nodev,relatime,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on" 0 0) + ]; + + seccomp = true; + }; +} diff --git a/test/sandbox/default.nix b/test/sandbox/default.nix index 8543493d..9e7f12b9 100644 --- a/test/sandbox/default.nix +++ b/test/sandbox/default.nix @@ -2,13 +2,12 @@ writeShellScript, callPackage, + name, version, + want, }: -writeShellScript "check-sandbox" '' +writeShellScript "fortify-${name}-check-sandbox-script" '' set -e - ${callPackage ./mount.nix { inherit version; }}/bin/test - ${callPackage ./fs.nix { inherit version; }}/bin/test - ${callPackage ./seccomp.nix { inherit version; }}/bin/test - + ${callPackage ./assert.nix { inherit name version want; }}/bin/test touch /tmp/sandbox-ok '' diff --git a/test/sandbox/fs.nix b/test/sandbox/fs.nix deleted file mode 100644 index 9714a82b..00000000 --- a/test/sandbox/fs.nix +++ /dev/null @@ -1,214 +0,0 @@ -{ - lib, - writeText, - buildGoModule, - - version, -}: -let - wantFS = - let - fs = mode: dir: data: { - mode = lib.fromHexString mode; - inherit - dir - data - ; - }; - in - fs "dead" { - ".fortify" = fs "800001ed" { - etc = fs "800001ed" null null; - sbin = fs "800001c0" { - fortify = fs "16d" null null; - init0 = fs "80001ff" null null; - } null; - mounts = fs "124" null null; - } null; - bin = fs "800001ed" { sh = fs "80001ff" null null; } null; - dev = fs "800001ed" { - 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 "800001c0" { - ".clean" = fs "80001ff" null null; - ".updated" = fs "80001ff" null null; - "NIXOS" = fs "80001ff" null null; - "X11" = fs "80001ff" null null; - "alsa" = 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; - "fsurc" = fs "80001ff" null null; - "fuse.conf" = fs "80001ff" null null; - "group" = fs "180" null "fortify: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:Fortify:/var/lib/fortify/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; - "profiles" = 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 "800001c0" { - current-system = fs "8000016d" null null; - opengl-driver = fs "8000016d" null null; - user = fs "800001ed" { - "65534" = fs "800001ed" { - 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" { - fortify = 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; } null; - ".local" = fs "800001ed" { - state = fs "800001ed" { - 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; - - mainFile = writeText "main.go" '' - package main - - import "os" - import "git.gensokyo.uk/security/fortify/test/sandbox" - - func main() { sandbox.MustAssertFS(os.DirFS("/"), "${writeText "want-fs.json" (builtins.toJSON wantFS)}") } - ''; -in -buildGoModule { - pname = "check-fs"; - inherit version; - - src = ../.; - vendorHash = null; - - preBuild = '' - go mod init git.gensokyo.uk/security/fortify/test >& /dev/null - cp ${mainFile} main.go - ''; -} diff --git a/test/sandbox/fs_test.go b/test/sandbox/fs_test.go index 5ac31dff..1262ddac 100644 --- a/test/sandbox/fs_test.go +++ b/test/sandbox/fs_test.go @@ -75,10 +75,4 @@ func TestCompare(t *testing.T) { } }) } - - t.Run("assert", func(t *testing.T) { - oldFatal := sandbox.SwapFatal(t.Fatalf) - t.Cleanup(func() { sandbox.SwapFatal(oldFatal) }) - sandbox.MustAssertFS(make(fstest.MapFS), sandbox.MustWantFile(t, &sandbox.FS{Mode: 0xDEADBEEF})) - }) } diff --git a/test/sandbox/mount.go b/test/sandbox/mount.go index 9a3b58c9..d65824f2 100644 --- a/test/sandbox/mount.go +++ b/test/sandbox/mount.go @@ -12,6 +12,7 @@ import "C" import ( "fmt" + "iter" "runtime" "sync" "unsafe" @@ -49,21 +50,38 @@ func (e *Mntent) Is(want *Mntent) bool { (e.Passno == want.Passno || want.Passno == -1) } -func IterMounts(name string, f func(e *Mntent)) error { - m := new(mounts) - m.p = name - if err := m.open(); err != nil { - return err - } +type MountsFile struct { + m *mounts + mu sync.Mutex + done bool +} - for m.scan() { - e := new(Mntent) - m.copy(e) - f(e) - } +func OpenMounts(name string) (*MountsFile, error) { + f := new(MountsFile) + f.m = new(mounts) + f.m.p = name + return f, f.m.open() +} - m.close() - return m.Err() +func (f *MountsFile) Err() error { return f.m.Err() } +func (f *MountsFile) Entries() iter.Seq[*Mntent] { + return func(yield func(*Mntent) bool) { + f.mu.Lock() + defer f.mu.Unlock() + if f.done { + return + } + + for f.m.scan() { + e := new(Mntent) + f.m.copy(e) + if !yield(e) { + return + } + } + f.done = true + f.m.close() + } } type mounts struct { diff --git a/test/sandbox/mount.nix b/test/sandbox/mount.nix deleted file mode 100644 index 0c1bda19..00000000 --- a/test/sandbox/mount.nix +++ /dev/null @@ -1,79 +0,0 @@ -{ - writeText, - buildGoModule, - - version, -}: -let - wantMounts = - let - ent = fsname: dir: type: opts: freq: passno: { - inherit - fsname - dir - type - opts - freq - passno - ; - }; - in - [ - (ent "tmpfs" "/" "tmpfs" "rw,nosuid,nodev,relatime,uid=1000001,gid=1000001" 0 0) - (ent "proc" "/proc" "proc" "rw,nosuid,nodev,noexec,relatime" 0 0) - (ent "tmpfs" "/.fortify" "tmpfs" "rw,nosuid,nodev,relatime,size=4k,mode=755,uid=1000001,gid=1000001" 0 0) - (ent "tmpfs" "/dev" "tmpfs" "rw,nosuid,nodev,relatime,mode=755,uid=1000001,gid=1000001" 0 0) - (ent "devtmpfs" "/dev/null" "devtmpfs" "host_passthrough" 0 0) - (ent "devtmpfs" "/dev/zero" "devtmpfs" "host_passthrough" 0 0) - (ent "devtmpfs" "/dev/full" "devtmpfs" "host_passthrough" 0 0) - (ent "devtmpfs" "/dev/random" "devtmpfs" "host_passthrough" 0 0) - (ent "devtmpfs" "/dev/urandom" "devtmpfs" "host_passthrough" 0 0) - (ent "devtmpfs" "/dev/tty" "devtmpfs" "host_passthrough" 0 0) - (ent "devpts" "/dev/pts" "devpts" "rw,nosuid,noexec,relatime,mode=620,ptmxmode=666" 0 0) - (ent "mqueue" "/dev/mqueue" "mqueue" "rw,relatime" 0 0) - (ent "/dev/disk/by-label/nixos" "/bin" "ext4" "ro,nosuid,nodev,relatime" 0 0) - (ent "/dev/disk/by-label/nixos" "/usr/bin" "ext4" "ro,nosuid,nodev,relatime" 0 0) - (ent "overlay" "/nix/store" "overlay" "ro,nosuid,nodev,relatime,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on" 0 0) - (ent "overlay" "/run/current-system" "overlay" "ro,nosuid,nodev,relatime,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on" 0 0) - (ent "sysfs" "/sys/block" "sysfs" "ro,nosuid,nodev,noexec,relatime" 0 0) - (ent "sysfs" "/sys/bus" "sysfs" "ro,nosuid,nodev,noexec,relatime" 0 0) - (ent "sysfs" "/sys/class" "sysfs" "ro,nosuid,nodev,noexec,relatime" 0 0) - (ent "sysfs" "/sys/dev" "sysfs" "ro,nosuid,nodev,noexec,relatime" 0 0) - (ent "sysfs" "/sys/devices" "sysfs" "ro,nosuid,nodev,noexec,relatime" 0 0) - (ent "overlay" "/run/opengl-driver" "overlay" "ro,nosuid,nodev,relatime,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on" 0 0) - (ent "devtmpfs" "/dev/dri" "devtmpfs" "host_passthrough" 0 0) - (ent "proc" "/.fortify/mounts" "proc" "ro,nosuid,nodev,noexec,relatime" 0 0) - (ent "/dev/disk/by-label/nixos" "/.fortify/etc" "ext4" "ro,nosuid,nodev,relatime" 0 0) - (ent "tmpfs" "/run/user" "tmpfs" "rw,nosuid,nodev,relatime,size=1024k,mode=755,uid=1000001,gid=1000001" 0 0) - (ent "tmpfs" "/run/user/65534" "tmpfs" "rw,nosuid,nodev,relatime,size=8192k,mode=755,uid=1000001,gid=1000001" 0 0) - (ent "/dev/disk/by-label/nixos" "/tmp" "ext4" "rw,nosuid,nodev,relatime" 0 0) - (ent "/dev/disk/by-label/nixos" "/var/lib/fortify/u0/a1" "ext4" "rw,nosuid,nodev,relatime" 0 0) - (ent "tmpfs" "/etc/passwd" "tmpfs" "ro,nosuid,nodev,relatime,uid=1000001,gid=1000001" 0 0) - (ent "tmpfs" "/etc/group" "tmpfs" "ro,nosuid,nodev,relatime,uid=1000001,gid=1000001" 0 0) - (ent "/dev/disk/by-label/nixos" "/run/user/65534/wayland-0" "ext4" "ro,nosuid,nodev,relatime" 0 0) - (ent "tmpfs" "/run/user/65534/pulse/native" "tmpfs" "host_passthrough" 0 0) - (ent "/dev/disk/by-label/nixos" "/run/user/65534/bus" "ext4" "ro,nosuid,nodev,relatime" 0 0) - (ent "tmpfs" "/var/run/nscd" "tmpfs" "rw,nosuid,nodev,relatime,size=8k,mode=755,uid=1000001,gid=1000001" 0 0) - (ent "overlay" "/.fortify/sbin/fortify" "overlay" "ro,nosuid,nodev,relatime,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on" 0 0) - ]; - - mainFile = writeText "main.go" '' - package main - - import "git.gensokyo.uk/security/fortify/test/sandbox" - - func main() { sandbox.MustAssertMounts("", "/.fortify/mounts", "${writeText "want-mounts.json" (builtins.toJSON wantMounts)}") } - ''; -in -buildGoModule { - pname = "check-mounts"; - inherit version; - - src = ../.; - vendorHash = null; - - preBuild = '' - go mod init git.gensokyo.uk/security/fortify/test >& /dev/null - cp ${mainFile} main.go - ''; -} diff --git a/test/sandbox/mount_test.go b/test/sandbox/mount_test.go index 47df2103..23a58fb1 100644 --- a/test/sandbox/mount_test.go +++ b/test/sandbox/mount_test.go @@ -92,27 +92,29 @@ overlay /.fortify/sbin/fortify overlay ro,nosuid,nodev,relatime,lowerdir=/mnt-ro } t.Run(tc.name, func(t *testing.T) { + f, err := sandbox.OpenMounts(name) + if err != nil { + t.Fatalf("OpenMounts: error = %v", err) + } + i := 0 - if err := sandbox.IterMounts(name, func(e *sandbox.Mntent) { + for e := range f.Entries() { if i == len(tc.want) { - t.Errorf("IterMounts: got more than %d entries", i) + t.Errorf("Entries: got more than %d entries", i) t.FailNow() } if *e != tc.want[i] { - t.Errorf("IterMounts: entry %d\n got: %s\nwant: %s", i, + t.Errorf("Entries: entry %d\n got: %s\nwant: %s", i, e, &tc.want[i]) t.FailNow() } + i++ - }); err != nil { - t.Fatalf("IterMounts: error = %v", err) } - }) - t.Run(tc.name+" assert", func(t *testing.T) { - oldFatal := sandbox.SwapFatal(t.Fatalf) - t.Cleanup(func() { sandbox.SwapFatal(oldFatal) }) - sandbox.MustAssertMounts(name, name, sandbox.MustWantFile(t, tc.want)) + if err = f.Err(); err != nil { + t.Fatalf("MountsFile: error = %v", err) + } }) if err := os.Remove(name); err != nil { diff --git a/test/sandbox/seccomp.nix b/test/sandbox/seccomp.nix deleted file mode 100644 index 3cf9b582..00000000 --- a/test/sandbox/seccomp.nix +++ /dev/null @@ -1,27 +0,0 @@ -{ - writeText, - buildGoModule, - - version, -}: -let - mainFile = writeText "main.go" '' - package main - - import "git.gensokyo.uk/security/fortify/test/sandbox" - - func main() { sandbox.MustAssertSeccomp() } - ''; -in -buildGoModule { - pname = "check-seccomp"; - inherit version; - - src = ../.; - vendorHash = null; - - preBuild = '' - go mod init git.gensokyo.uk/security/fortify/test >& /dev/null - cp ${mainFile} main.go - ''; -} diff --git a/test/test.py b/test/test.py index 99b2f16f..7f106a9d 100644 --- a/test/test.py +++ b/test/test.py @@ -102,8 +102,8 @@ if denyOutput != "fsu: uid 1001 is not in the fsurc file\n": if denyOutputVerbose != "fsu: uid 1001 is not in the fsurc file\nfortify: *cannot obtain uid from fsu: permission denied\n": raise Exception(f"unexpected deny verbose output:\n{denyOutputVerbose}") -# Check sandbox state: -swaymsg("exec check-sandbox") +# Check sandbox outcome: +swaymsg("exec check-sandbox-module-default") machine.wait_for_file("/tmp/fortify.1000/tmpdir/1/sandbox-ok", timeout=15) # Start fortify permissive defaults outside Wayland session: -- cgit v1.3.1