Compare commits

...

2 Commits

Author SHA1 Message Date
22d577ab49
test/sandbox: do not discard stderr getting hash
All checks were successful
Test / Create distribution (push) Successful in 31s
Test / Create distribution (pull_request) Successful in 29s
Test / Sandbox (push) Successful in 45s
Test / Hakurei (push) Successful in 47s
Test / Hakurei (race detector) (push) Successful in 48s
Test / Hpkg (push) Successful in 46s
Test / Sandbox (pull_request) Successful in 45s
Test / Hakurei (pull_request) Successful in 49s
Test / Hakurei (race detector) (pull_request) Successful in 49s
Test / Hpkg (pull_request) Successful in 46s
Test / Sandbox (race detector) (pull_request) Successful in 1m16s
Test / Sandbox (race detector) (push) Successful in 1m25s
Test / Flake checks (pull_request) Successful in 1m35s
Test / Flake checks (push) Successful in 1m34s
This is the first hakurei run in the test, if the container outright fails to start this is often where it happens, so throwing away the output is very unhelpful.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-08-18 11:36:13 +09:00
83a1c75f1a
app: set up acl on X11 socket
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m9s
Test / Hakurei (push) Successful in 3m22s
Test / Sandbox (race detector) (push) Successful in 4m26s
Test / Hpkg (push) Successful in 4m25s
Test / Hakurei (race detector) (push) Successful in 43s
Test / Flake checks (push) Successful in 1m38s
The socket is typically owned by the priv-user, and inaccessible by the target user, so just allowing access to the directory is not enough. This change fixes this oversight and add checks that will also be useful for merging #1.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-08-18 11:30:58 +09:00
9 changed files with 112 additions and 4 deletions

View File

@ -12,6 +12,7 @@ import (
"path" "path"
"regexp" "regexp"
"slices" "slices"
"strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
"syscall" "syscall"
@ -386,9 +387,34 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
return hlog.WrapErr(ErrXDisplay, return hlog.WrapErr(ErrXDisplay,
"DISPLAY is not set") "DISPLAY is not set")
} else { } else {
socketDir := container.AbsFHSTmp.Append(".X11-unix")
// the socket file at `/tmp/.X11-unix/X%d` is typically owned by the priv user
// and not accessible by the target user
var socketPath *container.Absolute
if len(d) > 1 && d[0] == ':' { // `:%d`
if n, err := strconv.Atoi(d[1:]); err == nil && n >= 0 {
socketPath = socketDir.Append("X" + strconv.Itoa(n))
}
} else if len(d) > 5 && strings.HasPrefix(d, "unix:") { // `unix:%s`
if a, err := container.NewAbs(d[5:]); err == nil {
socketPath = a
}
}
if socketPath != nil {
if _, err := sys.Stat(socketPath.String()); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return hlog.WrapErrSuffix(err,
fmt.Sprintf("cannot access X11 socket %q:", socketPath))
}
} else {
seal.sys.UpdatePermType(system.EX11, socketPath.String(), acl.Read, acl.Write, acl.Execute)
d = "unix:" + socketPath.String()
}
}
seal.sys.ChangeHosts("#" + seal.user.uid.String()) seal.sys.ChangeHosts("#" + seal.user.uid.String())
seal.env[display] = d seal.env[display] = d
socketDir := container.AbsFHSTmp.Append(".X11-unix")
seal.container.Bind(socketDir, socketDir, 0) seal.container.Bind(socketDir, socketDir, 0)
} }
} }

View File

@ -15,6 +15,7 @@ import (
"errors" "errors"
"io/fs" "io/fs"
"log" "log"
"net"
"os" "os"
"syscall" "syscall"
) )
@ -33,6 +34,10 @@ type TestCase struct {
FS *FS `json:"fs"` FS *FS `json:"fs"`
Mount []*MountinfoEntry `json:"mount"` Mount []*MountinfoEntry `json:"mount"`
Seccomp bool `json:"seccomp"` 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 { type T struct {
@ -125,6 +130,47 @@ func (t *T) MustCheck(want *TestCase) {
} else { } else {
printf("[SKIP] skipping seccomp check") 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) { func MustCheckFilter(pid int, want string) {

View File

@ -50,6 +50,9 @@ let
useCommonPaths useCommonPaths
userns userns
; ;
enablements = {
inherit (tc) x11;
};
share = testProgram; share = testProgram;
packages = [ ]; packages = [ ];
path = "${testProgram}/bin/hakurei-test"; path = "${testProgram}/bin/hakurei-test";

View File

@ -25,6 +25,7 @@ in
mapRealUid = false; mapRealUid = false;
useCommonPaths = true; useCommonPaths = true;
userns = false; userns = false;
x11 = true;
# 0, PresetStrict # 0, PresetStrict
expectedFilter = { expectedFilter = {
@ -35,6 +36,7 @@ in
want = { want = {
env = [ env = [
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus" "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
"DISPLAY=unix:/tmp/.X11-unix/X0"
"HOME=/var/lib/hakurei/u0/a4" "HOME=/var/lib/hakurei/u0/a4"
"PULSE_SERVER=unix:/run/user/65534/pulse/native" "PULSE_SERVER=unix:/run/user/65534/pulse/native"
"SHELL=/run/current-system/sw/bin/bash" "SHELL=/run/current-system/sw/bin/bash"
@ -161,7 +163,9 @@ in
} null; } null;
devices = fs "800001ed" null null; devices = fs "800001ed" null null;
} null; } null;
tmp = fs "800001f8" { } 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; usr = fs "800001c0" { bin = fs "800001ed" { env = fs "80001ff" null null; } null; } null;
var = fs "800001c0" { var = fs "800001c0" {
lib = fs "800001c0" { lib = fs "800001c0" {
@ -231,10 +235,15 @@ in
(ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000004,gid=1000004") (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000004,gid=1000004")
(ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000004,gid=1000004") (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000004,gid=1000004")
(ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/tmp/.X11-unix" "/tmp/.X11-unix" "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/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 ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
]; ];
seccomp = true; seccomp = true;
try_socket = "/tmp/.X11-unix/X0";
socket_abstract = true;
socket_pathname = true;
}; };
} }

View File

@ -34,6 +34,7 @@ in
mapRealUid = true; mapRealUid = true;
useCommonPaths = true; useCommonPaths = true;
userns = false; userns = false;
x11 = false;
# 0, PresetStrict # 0, PresetStrict
expectedFilter = { expectedFilter = {
@ -266,5 +267,9 @@ in
]; ];
seccomp = true; seccomp = true;
try_socket = "/tmp/.X11-unix/X0";
socket_abstract = true;
socket_pathname = false;
}; };
} }

View File

@ -34,6 +34,7 @@ in
mapRealUid = false; mapRealUid = false;
useCommonPaths = false; useCommonPaths = false;
userns = true; userns = true;
x11 = false;
# 0, PresetExt | PresetDenyDevel # 0, PresetExt | PresetDenyDevel
expectedFilter = { expectedFilter = {
@ -261,5 +262,9 @@ in
]; ];
seccomp = true; seccomp = true;
try_socket = "/tmp/.X11-unix/X0";
socket_abstract = true;
socket_pathname = false;
}; };
} }

View File

@ -34,6 +34,7 @@ in
mapRealUid = false; mapRealUid = false;
useCommonPaths = false; useCommonPaths = false;
userns = false; userns = false;
x11 = false;
# 0, PresetStrict # 0, PresetStrict
expectedFilter = { expectedFilter = {
@ -259,5 +260,9 @@ in
]; ];
seccomp = true; seccomp = true;
try_socket = "/tmp/.X11-unix/X0";
socket_abstract = true;
socket_pathname = false;
}; };
} }

View File

@ -34,6 +34,7 @@ in
mapRealUid = false; mapRealUid = false;
useCommonPaths = true; useCommonPaths = true;
userns = false; userns = false;
x11 = true;
# 0, PresetExt | PresetDenyNS | PresetDenyDevel # 0, PresetExt | PresetDenyNS | PresetDenyDevel
expectedFilter = { expectedFilter = {
@ -44,6 +45,7 @@ in
want = { want = {
env = [ env = [
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus" "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
"DISPLAY=unix:/tmp/.X11-unix/X0"
"HOME=/var/lib/hakurei/u0/a2" "HOME=/var/lib/hakurei/u0/a2"
"PULSE_SERVER=unix:/run/user/65534/pulse/native" "PULSE_SERVER=unix:/run/user/65534/pulse/native"
"SHELL=/run/current-system/sw/bin/bash" "SHELL=/run/current-system/sw/bin/bash"
@ -188,7 +190,9 @@ in
} null; } null;
devices = fs "800001ed" null null; devices = fs "800001ed" null null;
} null; } null;
tmp = fs "800001f8" { } 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; usr = fs "800001c0" { bin = fs "800001ed" { env = fs "80001ff" null null; } null; } null;
var = fs "800001c0" { var = fs "800001c0" {
lib = fs "800001c0" { lib = fs "800001c0" {
@ -263,10 +267,15 @@ in
(ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000002,gid=1000002") (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000002,gid=1000002")
(ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000002,gid=1000002") (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000002,gid=1000002")
(ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/tmp/.X11-unix" "/tmp/.X11-unix" "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/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 ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
]; ];
seccomp = true; seccomp = true;
try_socket = "/tmp/.X11-unix/X0";
socket_abstract = true;
socket_pathname = true;
}; };
} }

View File

@ -27,7 +27,7 @@ def swaymsg(command: str = "", succeed=True, type="command"):
def check_filter(check_offset, name, pname): def check_filter(check_offset, name, pname):
pid = int(machine.wait_until_succeeds(f"pgrep -U {1000000+check_offset} -x {pname}", timeout=15)) 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") 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}")) print(machine.succeed(f"hakurei-test -s {hash} filter {pid}"))