test/sandbox: check mount outcome
All checks were successful
Test / Create distribution (push) Successful in 21s
Test / Fpkg (push) Successful in 32s
Test / Fortify (push) Successful in 35s
Test / Data race detector (push) Successful in 35s
Test / Flake checks (push) Successful in 49s

Do this at the beginning of the test for early failure.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-02-28 15:56:15 +09:00
parent 558974b996
commit d8e9d71f87
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
4 changed files with 124 additions and 14 deletions

View File

@ -102,6 +102,21 @@
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/host-mounts";
}
];
}
{
name = "ne-foot";
verbose = true;

12
test/sandbox/default.nix Normal file
View File

@ -0,0 +1,12 @@
{
writeShellScript,
callPackage,
version,
}:
writeShellScript "check-sandbox" ''
set -e
${callPackage ./mount.nix { inherit version; }}/bin/test
touch /tmp/sandbox-ok
''

79
test/sandbox/mount.nix Normal file
View File

@ -0,0 +1,79 @@
{
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/host-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" "ro,nosuid,nodev,relatime,size=98784k,nr_inodes=24696,mode=700,uid=1000,gid=100" 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/host-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
'';
}

View File

@ -104,6 +104,10 @@ 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")
machine.wait_for_file("/tmp/fortify.1000/tmpdir/1/sandbox-ok")
# Start fortify permissive defaults outside Wayland session:
print(machine.succeed("sudo -u alice -i fortify -v run -a 0 touch /tmp/success-bare"))
machine.wait_for_file("/tmp/fortify.1000/tmpdir/0/success-bare")
@ -144,24 +148,24 @@ machine.succeed("pkill -9 mako")
# Start app (foot) with Wayland enablement:
swaymsg("exec ne-foot")
wait_for_window("u0_a1@machine")
wait_for_window("u0_a2@machine")
machine.send_chars("clear; wayland-info && touch /tmp/success-client\n")
machine.wait_for_file("/tmp/fortify.1000/tmpdir/1/success-client")
machine.wait_for_file("/tmp/fortify.1000/tmpdir/2/success-client")
collect_state_ui("foot_wayland")
check_state("ne-foot", 1)
# Verify acl on XDG_RUNTIME_DIR:
print(machine.succeed("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 1000001"))
print(machine.succeed("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 1000002"))
machine.send_chars("exit\n")
machine.wait_until_fails("pgrep foot")
# Verify acl cleanup on XDG_RUNTIME_DIR:
machine.wait_until_fails("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 1000001")
machine.wait_until_fails("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 1000002")
# Start app (foot) with Wayland enablement from a terminal:
swaymsg(
"exec foot $SHELL -c '(ne-foot) & sleep 1 && fortify show $(fortify ps --short) && touch /tmp/ps-show-ok && cat'")
wait_for_window("u0_a1@machine")
wait_for_window("u0_a2@machine")
machine.send_chars("clear; wayland-info && touch /tmp/success-client-term\n")
machine.wait_for_file("/tmp/fortify.1000/tmpdir/1/success-client-term")
machine.wait_for_file("/tmp/fortify.1000/tmpdir/2/success-client-term")
machine.wait_for_file("/tmp/ps-show-ok")
collect_state_ui("foot_wayland_term")
check_state("ne-foot", 1)
@ -172,9 +176,9 @@ machine.wait_until_fails("pgrep foot")
# Test PulseAudio (fortify does not support PipeWire yet):
swaymsg("exec pa-foot")
wait_for_window("u0_a2@machine")
wait_for_window("u0_a3@machine")
machine.send_chars("clear; pactl info && touch /tmp/success-pulse\n")
machine.wait_for_file("/tmp/fortify.1000/tmpdir/2/success-pulse")
machine.wait_for_file("/tmp/fortify.1000/tmpdir/3/success-pulse")
collect_state_ui("pulse_wayland")
check_state("pa-foot", 9)
machine.send_chars("exit\n")
@ -182,9 +186,9 @@ machine.wait_until_fails("pgrep foot")
# Test XWayland (foot does not support X):
swaymsg("exec x11-alacritty")
wait_for_window("u0_a3@machine")
wait_for_window("u0_a4@machine")
machine.send_chars("clear; glinfo && touch /tmp/success-client-x11\n")
machine.wait_for_file("/tmp/fortify.1000/tmpdir/3/success-client-x11")
machine.wait_for_file("/tmp/fortify.1000/tmpdir/4/success-client-x11")
collect_state_ui("alacritty_x11")
check_state("x11-alacritty", 2)
machine.send_chars("exit\n")
@ -192,17 +196,17 @@ machine.wait_until_fails("pgrep alacritty")
# Start app (foot) with direct Wayland access:
swaymsg("exec da-foot")
wait_for_window("u0_a4@machine")
wait_for_window("u0_a5@machine")
machine.send_chars("clear; wayland-info && touch /tmp/success-direct\n")
machine.wait_for_file("/tmp/fortify.1000/tmpdir/4/success-direct")
machine.wait_for_file("/tmp/fortify.1000/tmpdir/5/success-direct")
collect_state_ui("foot_direct")
check_state("da-foot", 1)
# Verify acl on XDG_RUNTIME_DIR:
print(machine.succeed("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 1000004"))
print(machine.succeed("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 1000005"))
machine.send_chars("exit\n")
machine.wait_until_fails("pgrep foot")
# Verify acl cleanup on XDG_RUNTIME_DIR:
machine.wait_until_fails("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 1000004")
machine.wait_until_fails("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 1000005")
# Test syscall filter:
print(machine.fail("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 strace-failure"))