2 Commits

Author SHA1 Message Date
699c19e972 hst/container: optional runtime and tmpdir sharing
All checks were successful
Test / Create distribution (push) Successful in 25s
Test / Sandbox (push) Successful in 39s
Test / Sandbox (race detector) (push) Successful in 39s
Test / Hakurei (push) Successful in 42s
Test / Hpkg (push) Successful in 40s
Test / Hakurei (race detector) (push) Successful in 44s
Test / Flake checks (push) Successful in 1m23s
Sharing and persisting these directories do not always make sense. Make it optional here.

Closes #16.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-19 04:11:38 +09:00
b5b30aea2e test: place marker in common path
All checks were successful
Test / Create distribution (push) Successful in 26s
Test / Sandbox (race detector) (push) Successful in 39s
Test / Sandbox (push) Successful in 41s
Test / Hakurei (race detector) (push) Successful in 45s
Test / Hpkg (push) Successful in 42s
Test / Hakurei (push) Successful in 46s
Test / Flake checks (push) Successful in 1m33s
This discontinues the dependency on shared tmpdir and xdg_runtime_dir implementation detail, for #16.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-19 03:50:48 +09:00
20 changed files with 130 additions and 62 deletions

View File

@@ -81,6 +81,8 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr
flagHomeDir string flagHomeDir string
flagUserName string flagUserName string
flagPrivateRuntime, flagPrivateTmpdir bool
flagWayland, flagX11, flagDBus, flagPulse bool flagWayland, flagX11, flagDBus, flagPulse bool
) )
@@ -211,6 +213,13 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr
} }
} }
if !flagPrivateRuntime {
config.Container.Flags |= hst.FShareRuntime
}
if !flagPrivateTmpdir {
config.Container.Flags |= hst.FShareTmpdir
}
// parse D-Bus config file from flags if applicable // parse D-Bus config file from flags if applicable
if flagDBus { if flagDBus {
if flagDBusConfigSession == "builtin" { if flagDBusConfigSession == "builtin" {
@@ -264,6 +273,10 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr
"Container home directory"). "Container home directory").
Flag(&flagUserName, "u", command.StringFlag("chronos"), Flag(&flagUserName, "u", command.StringFlag("chronos"),
"Passwd user name within sandbox"). "Passwd user name within sandbox").
Flag(&flagPrivateRuntime, "private-runtime", command.BoolFlag(false),
"Do not share XDG_RUNTIME_DIR between containers under the same identity").
Flag(&flagPrivateTmpdir, "private-tmpdir", command.BoolFlag(false),
"Do not share TMPDIR between containers under the same identity").
Flag(&flagWayland, "wayland", command.BoolFlag(false), Flag(&flagWayland, "wayland", command.BoolFlag(false),
"Enable connection to Wayland via security-context-v1"). "Enable connection to Wayland via security-context-v1").
Flag(&flagX11, "X", command.BoolFlag(false), Flag(&flagX11, "X", command.BoolFlag(false),

View File

@@ -36,7 +36,7 @@ Commands:
}, },
{ {
"run", []string{"run", "-h"}, ` "run", []string{"run", "-h"}, `
Usage: hakurei run [-h | --help] [--dbus-config <value>] [--dbus-system <value>] [--mpris] [--dbus-log] [--id <value>] [-a <int>] [-g <value>] [-d <value>] [-u <value>] [--wayland] [-X] [--dbus] [--pulse] COMMAND [OPTIONS] Usage: hakurei run [-h | --help] [--dbus-config <value>] [--dbus-system <value>] [--mpris] [--dbus-log] [--id <value>] [-a <int>] [-g <value>] [-d <value>] [-u <value>] [--private-runtime] [--private-tmpdir] [--wayland] [-X] [--dbus] [--pulse] COMMAND [OPTIONS]
Flags: Flags:
-X Enable direct connection to X11 -X Enable direct connection to X11
@@ -58,6 +58,10 @@ Flags:
Reverse-DNS style Application identifier, leave empty to inherit instance identifier Reverse-DNS style Application identifier, leave empty to inherit instance identifier
-mpris -mpris
Allow owning MPRIS D-Bus path, has no effect if custom config is available Allow owning MPRIS D-Bus path, has no effect if custom config is available
-private-runtime
Do not share XDG_RUNTIME_DIR between containers under the same identity
-private-tmpdir
Do not share TMPDIR between containers under the same identity
-pulse -pulse
Enable direct connection to PulseAudio Enable direct connection to PulseAudio
-u string -u string

View File

@@ -332,7 +332,9 @@ App
"tty": true, "tty": true,
"multiarch": true, "multiarch": true,
"map_real_uid": true, "map_real_uid": true,
"device": true "device": true,
"share_runtime": true,
"share_tmpdir": true
} }
}, },
"time": "1970-01-01T00:00:00.000000009Z" "time": "1970-01-01T00:00:00.000000009Z"
@@ -482,7 +484,9 @@ App
"tty": true, "tty": true,
"multiarch": true, "multiarch": true,
"map_real_uid": true, "map_real_uid": true,
"device": true "device": true,
"share_runtime": true,
"share_tmpdir": true
} }
} }
`, true}, `, true},
@@ -692,7 +696,9 @@ func TestPrintPs(t *testing.T) {
"tty": true, "tty": true,
"multiarch": true, "multiarch": true,
"map_real_uid": true, "map_real_uid": true,
"device": true "device": true,
"share_runtime": true,
"share_tmpdir": true
} }
}, },
"time": "1970-01-01T00:00:00.000000009Z" "time": "1970-01-01T00:00:00.000000009Z"

View File

@@ -130,6 +130,7 @@ func (app *appInfo) toHst(pathSet *appPathSet, pathname *check.Absolute, argv []
if app.Multiarch { if app.Multiarch {
config.Container.Flags |= hst.FMultiarch config.Container.Flags |= hst.FMultiarch
} }
config.Container.Flags |= hst.FShareRuntime | hst.FShareTmpdir
return config return config
} }

2
dist/comp/_hakurei vendored
View File

@@ -12,6 +12,8 @@ _hakurei_run() {
'-g[Groups inherited by all container processes]: :_groups' \ '-g[Groups inherited by all container processes]: :_groups' \
'-d[Container home directory]: :_files -/' \ '-d[Container home directory]: :_files -/' \
'-u[Passwd user name within sandbox]: :_users' \ '-u[Passwd user name within sandbox]: :_users' \
'--private-runtime[Do not share XDG_RUNTIME_DIR between containers under the same identity]' \
'--private-tmpdir[Do not share TMPDIR between containers under the same identity]' \
'--wayland[Enable connection to Wayland via security-context-v1]' \ '--wayland[Enable connection to Wayland via security-context-v1]' \
'-X[Enable direct connection to X11]' \ '-X[Enable direct connection to X11]' \
'--dbus[Enable proxied connection to D-Bus]' \ '--dbus[Enable proxied connection to D-Bus]' \

View File

@@ -63,6 +63,11 @@ const (
// FDevice mount /dev/ from the init mount namespace as-is in the container mount namespace. // FDevice mount /dev/ from the init mount namespace as-is in the container mount namespace.
FDevice FDevice
// FShareRuntime shares XDG_RUNTIME_DIR between containers under the same identity.
FShareRuntime
// FShareTmpdir shares TMPDIR between containers under the same identity.
FShareTmpdir
fMax fMax
// FAll is [ContainerConfig.Flags] with all currently defined bits set. // FAll is [ContainerConfig.Flags] with all currently defined bits set.
@@ -133,6 +138,11 @@ type containerConfigJSON = struct {
// Corresponds to [FDevice]. // Corresponds to [FDevice].
Device bool `json:"device,omitempty"` Device bool `json:"device,omitempty"`
// Corresponds to [FShareRuntime].
ShareRuntime bool `json:"share_runtime,omitempty"`
// Corresponds to [FShareTmpdir]
ShareTmpdir bool `json:"share_tmpdir,omitempty"`
} }
func (c *ContainerConfig) MarshalJSON() ([]byte, error) { func (c *ContainerConfig) MarshalJSON() ([]byte, error) {
@@ -151,6 +161,8 @@ func (c *ContainerConfig) MarshalJSON() ([]byte, error) {
Multiarch: c.Flags&FMultiarch != 0, Multiarch: c.Flags&FMultiarch != 0,
MapRealUID: c.Flags&FMapRealUID != 0, MapRealUID: c.Flags&FMapRealUID != 0,
Device: c.Flags&FDevice != 0, Device: c.Flags&FDevice != 0,
ShareRuntime: c.Flags&FShareRuntime != 0,
ShareTmpdir: c.Flags&FShareTmpdir != 0,
}) })
} }
@@ -192,5 +204,11 @@ func (c *ContainerConfig) UnmarshalJSON(data []byte) error {
if v.Device { if v.Device {
c.Flags |= FDevice c.Flags |= FDevice
} }
if v.ShareRuntime {
c.Flags |= FShareRuntime
}
if v.ShareTmpdir {
c.Flags |= FShareTmpdir
}
return nil return nil
} }

View File

@@ -28,7 +28,7 @@ func TestContainerConfig(t *testing.T) {
{"hostnet hostabstract mapuid", &hst.ContainerConfig{Flags: hst.FHostNet | hst.FHostAbstract | hst.FMapRealUID}, {"hostnet hostabstract mapuid", &hst.ContainerConfig{Flags: hst.FHostNet | hst.FHostAbstract | hst.FMapRealUID},
`{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"host_net":true,"host_abstract":true,"map_real_uid":true}`}, `{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"host_net":true,"host_abstract":true,"map_real_uid":true}`},
{"all", &hst.ContainerConfig{Flags: hst.FAll}, {"all", &hst.ContainerConfig{Flags: hst.FAll},
`{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"seccomp_compat":true,"devel":true,"userns":true,"host_net":true,"host_abstract":true,"tty":true,"multiarch":true,"map_real_uid":true,"device":true}`}, `{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"seccomp_compat":true,"devel":true,"userns":true,"host_net":true,"host_abstract":true,"tty":true,"multiarch":true,"map_real_uid":true,"device":true,"share_runtime":true,"share_tmpdir":true}`},
} }
for _, tc := range testCases { for _, tc := range testCases {

View File

@@ -244,7 +244,9 @@ func TestTemplate(t *testing.T) {
"tty": true, "tty": true,
"multiarch": true, "multiarch": true,
"map_real_uid": true, "map_real_uid": true,
"device": true "device": true,
"share_runtime": true,
"share_tmpdir": true
} }
}` }`

View File

@@ -70,7 +70,7 @@ func TestApp(t *testing.T) {
Path: m("/run/current-system/sw/bin/zsh"), Path: m("/run/current-system/sw/bin/zsh"),
Args: []string{"/run/current-system/sw/bin/zsh"}, Args: []string{"/run/current-system/sw/bin/zsh"},
Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty, Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty | hst.FShareRuntime | hst.FShareTmpdir,
}}, }},
state.ID{ state.ID{
0x4a, 0x45, 0x0b, 0x65, 0x4a, 0x45, 0x0b, 0x65,
@@ -193,7 +193,7 @@ func TestApp(t *testing.T) {
Path: m("/run/current-system/sw/bin/zsh"), Path: m("/run/current-system/sw/bin/zsh"),
Args: []string{"zsh", "-c", "exec chromium "}, Args: []string{"zsh", "-c", "exec chromium "},
Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty, Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty | hst.FShareRuntime | hst.FShareTmpdir,
}, },
}, },
state.ID{ state.ID{
@@ -331,7 +331,7 @@ func TestApp(t *testing.T) {
Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"), Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"),
Flags: hst.FUserns | hst.FHostNet | hst.FMapRealUID, Flags: hst.FUserns | hst.FHostNet | hst.FMapRealUID | hst.FShareRuntime | hst.FShareTmpdir,
}, },
SystemBus: &hst.BusConfig{ SystemBus: &hst.BusConfig{
Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"}, Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"},

View File

@@ -72,11 +72,13 @@ type spRuntimeOp struct {
} }
func (s *spRuntimeOp) toSystem(state *outcomeStateSys) error { func (s *spRuntimeOp) toSystem(state *outcomeStateSys) error {
if state.Container.Flags&hst.FShareRuntime != 0 {
runtimeDir, runtimeDirInst := s.commonPaths(state.outcomeState) runtimeDir, runtimeDirInst := s.commonPaths(state.outcomeState)
state.sys.Ensure(runtimeDir, 0700) state.sys.Ensure(runtimeDir, 0700)
state.sys.UpdatePermType(system.User, runtimeDir, acl.Execute) state.sys.UpdatePermType(system.User, runtimeDir, acl.Execute)
state.sys.Ensure(runtimeDirInst, 0700) state.sys.Ensure(runtimeDirInst, 0700)
state.sys.UpdatePermType(system.User, runtimeDirInst, acl.Read, acl.Write, acl.Execute) state.sys.UpdatePermType(system.User, runtimeDirInst, acl.Read, acl.Write, acl.Execute)
}
if state.et&hst.EWayland != 0 { if state.et&hst.EWayland != 0 {
s.SessionType = sessionTypeWayland s.SessionType = sessionTypeWayland
@@ -106,10 +108,13 @@ func (s *spRuntimeOp) toContainer(state *outcomeStateParams) error {
} }
state.params.Tmpfs(fhs.AbsRunUser, 1<<12, 0755)
if state.Container.Flags&hst.FShareRuntime != 0 {
_, runtimeDirInst := s.commonPaths(state.outcomeState) _, runtimeDirInst := s.commonPaths(state.outcomeState)
state.params. state.params.Bind(runtimeDirInst, state.runtimeDir, bits.BindWritable)
Tmpfs(fhs.AbsRunUser, 1<<12, 0755). } else {
Bind(runtimeDirInst, state.runtimeDir, bits.BindWritable) state.params.Mkdir(state.runtimeDir, 0700)
}
return nil return nil
} }

View File

@@ -6,6 +6,7 @@ import (
"hakurei.app/container/bits" "hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/hst"
"hakurei.app/system" "hakurei.app/system"
"hakurei.app/system/acl" "hakurei.app/system/acl"
) )
@@ -16,18 +17,23 @@ func init() { gob.Register(spTmpdirOp{}) }
type spTmpdirOp struct{} type spTmpdirOp struct{}
func (s spTmpdirOp) toSystem(state *outcomeStateSys) error { func (s spTmpdirOp) toSystem(state *outcomeStateSys) error {
if state.Container.Flags&hst.FShareTmpdir != 0 {
tmpdir, tmpdirInst := s.commonPaths(state.outcomeState) tmpdir, tmpdirInst := s.commonPaths(state.outcomeState)
state.sys.Ensure(tmpdir, 0700) state.sys.Ensure(tmpdir, 0700)
state.sys.UpdatePermType(system.User, tmpdir, acl.Execute) state.sys.UpdatePermType(system.User, tmpdir, acl.Execute)
state.sys.Ensure(tmpdirInst, 01700) state.sys.Ensure(tmpdirInst, 01700)
state.sys.UpdatePermType(system.User, tmpdirInst, acl.Read, acl.Write, acl.Execute) state.sys.UpdatePermType(system.User, tmpdirInst, acl.Read, acl.Write, acl.Execute)
}
return nil return nil
} }
func (s spTmpdirOp) toContainer(state *outcomeStateParams) error { func (s spTmpdirOp) toContainer(state *outcomeStateParams) error {
// mount inner /tmp from share so it shares persistence and storage behaviour of host /tmp if state.Container.Flags&hst.FShareTmpdir != 0 {
_, tmpdirInst := s.commonPaths(state.outcomeState) _, tmpdirInst := s.commonPaths(state.outcomeState)
state.params.Bind(tmpdirInst, fhs.AbsTmp, bits.BindWritable) state.params.Bind(tmpdirInst, fhs.AbsTmp, bits.BindWritable)
} else {
state.params.Tmpfs(fhs.AbsTmp, 0, 01777)
}
return nil return nil
} }

View File

@@ -121,6 +121,8 @@ in
map_real_uid = app.mapRealUid; map_real_uid = app.mapRealUid;
host_net = app.hostNet; host_net = app.hostNet;
host_abstract = app.hostAbstract; host_abstract = app.hostAbstract;
share_runtime = app.shareRuntime;
share_tmpdir = app.shareTmpdir;
filesystem = filesystem =
let let

View File

@@ -136,6 +136,9 @@ in
''; '';
}; };
shareRuntime = mkEnableOption "sharing of XDG_RUNTIME_DIR between containers under the same identity";
shareTmpdir = mkEnableOption "sharing of TMPDIR between containers under the same identity";
dbus = { dbus = {
session = mkOption { session = mkOption {
type = nullOr (functionTo anything); type = nullOr (functionTo anything);

View File

@@ -50,6 +50,8 @@ let
useCommonPaths useCommonPaths
userns userns
hostAbstract hostAbstract
shareRuntime
shareTmpdir
; ;
enablements = { enablements = {
inherit (tc) x11; inherit (tc) x11;

View File

@@ -27,6 +27,8 @@ in
userns = false; userns = false;
x11 = true; x11 = true;
hostAbstract = false; hostAbstract = false;
shareRuntime = false;
shareTmpdir = true;
# 0, PresetStrict # 0, PresetStrict
expectedFilter = { expectedFilter = {
@@ -133,7 +135,7 @@ in
current-system = fs "80001ff" null null; current-system = fs "80001ff" null null;
opengl-driver = fs "80001ff" null null; opengl-driver = fs "80001ff" null null;
user = fs "800001ed" { user = fs "800001ed" {
"65534" = fs "800001f8" { "65534" = fs "800001c0" {
bus = fs "10001fd" null null; bus = fs "10001fd" null null;
pulse = fs "800001c0" { native = fs "10001b6" null null; } null; pulse = fs "800001c0" { native = fs "10001b6" null null; } null;
wayland-0 = fs "1000038" null null; wayland-0 = fs "1000038" null null;
@@ -219,7 +221,6 @@ in
(ent "/" ignore ignore ignore ignore ignore) (ent "/" ignore ignore ignore ignore ignore)
(ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=1000004,gid=1000004") (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=1000004,gid=1000004")
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000004,gid=1000004") (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000004,gid=1000004")
(ent "/tmp/hakurei.0/runtime/4" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/tmp/hakurei.0/tmpdir/4" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.0/tmpdir/4" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(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")

View File

@@ -36,6 +36,8 @@ in
userns = false; userns = false;
x11 = false; x11 = false;
hostAbstract = false; hostAbstract = false;
shareRuntime = true;
shareTmpdir = true;
# 0, PresetStrict # 0, PresetStrict
expectedFilter = { expectedFilter = {

View File

@@ -36,6 +36,8 @@ in
userns = true; userns = true;
x11 = false; x11 = false;
hostAbstract = false; hostAbstract = false;
shareRuntime = true;
shareTmpdir = true;
# 0, PresetExt | PresetDenyDevel # 0, PresetExt | PresetDenyDevel
expectedFilter = { expectedFilter = {

View File

@@ -36,6 +36,8 @@ in
userns = false; userns = false;
x11 = false; x11 = false;
hostAbstract = false; hostAbstract = false;
shareRuntime = false;
shareTmpdir = false;
# 0, PresetStrict # 0, PresetStrict
expectedFilter = { expectedFilter = {
@@ -155,7 +157,7 @@ in
current-system = fs "80001ff" null null; current-system = fs "80001ff" null null;
opengl-driver = fs "80001ff" null null; opengl-driver = fs "80001ff" null null;
user = fs "800001ed" { user = fs "800001ed" {
"65534" = fs "800001f8" { "65534" = fs "800001c0" {
bus = fs "10001fd" null null; bus = fs "10001fd" null null;
pulse = fs "800001c0" { native = fs "10001b6" null null; } null; pulse = fs "800001c0" { native = fs "10001b6" null null; } null;
wayland-0 = fs "1000038" null null; wayland-0 = fs "1000038" null null;
@@ -186,7 +188,7 @@ in
} null; } null;
devices = fs "800001ed" null null; devices = fs "800001ed" null null;
} null; } null;
tmp = fs "800001f8" { } null; tmp = fs "801001ff" { } 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" {
tmp = fs "801001ff" null null; tmp = fs "801001ff" null null;
@@ -242,8 +244,7 @@ in
(ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw") (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
(ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=1000001,gid=1000001") (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=1000001,gid=1000001")
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000001,gid=1000001") (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000001,gid=1000001")
(ent "/tmp/hakurei.0/runtime/1" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/" "/tmp" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=1000001,gid=1000001")
(ent "/tmp/hakurei.0/tmpdir/1" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000001,gid=1000001") (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000001,gid=1000001")
(ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000001,gid=1000001") (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000001,gid=1000001")
(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")

View File

@@ -36,6 +36,8 @@ in
userns = false; userns = false;
x11 = true; x11 = true;
hostAbstract = true; hostAbstract = true;
shareRuntime = true;
shareTmpdir = false;
# 0, PresetExt | PresetDenyNS | PresetDenyDevel # 0, PresetExt | PresetDenyNS | PresetDenyDevel
expectedFilter = { expectedFilter = {
@@ -191,7 +193,7 @@ in
} null; } null;
devices = fs "800001ed" null null; devices = fs "800001ed" null null;
} null; } null;
tmp = fs "800001f8" { tmp = fs "801001ff" {
".X11-unix" = fs "801001ff" { X0 = fs "10001fd" null null; } null; ".X11-unix" = fs "801001ff" { X0 = fs "10001fd" null 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;
@@ -252,7 +254,7 @@ in
(ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=1000002,gid=1000002") (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=1000002,gid=1000002")
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000002,gid=1000002") (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000002,gid=1000002")
(ent "/tmp/hakurei.0/runtime/2" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.0/runtime/2" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/tmp/hakurei.0/tmpdir/2" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/" "/tmp" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=1000002,gid=1000002")
(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")

View File

@@ -115,14 +115,10 @@ if linger_stderr != "init: timeout exceeded waiting for lingering processes\n":
check_offset = 0 check_offset = 0
def aid(offset): def hakurei_identity(offset):
return 1+check_offset+offset return 1+check_offset+offset
def tmpdir_path(offset, name):
return f"/tmp/hakurei.0/tmpdir/{aid(offset)}/{name}"
# Start hakurei permissive defaults outside Wayland session: # Start hakurei permissive defaults outside Wayland session:
print(machine.succeed("sudo -u alice -i hakurei -v run -a 0 touch /tmp/pd-bare-ok")) print(machine.succeed("sudo -u alice -i hakurei -v run -a 0 touch /tmp/pd-bare-ok"))
machine.wait_for_file("/tmp/hakurei.0/tmpdir/0/pd-bare-ok", timeout=5) machine.wait_for_file("/tmp/hakurei.0/tmpdir/0/pd-bare-ok", timeout=5)
@@ -136,7 +132,7 @@ if output != "":
def silent_output_interrupt(flags): def silent_output_interrupt(flags):
swaymsg("exec foot") swaymsg("exec foot")
wait_for_window("alice@machine") wait_for_window("alice@machine")
# aid 0 does not have home-manager # identity 0 does not have home-manager
machine.send_chars(f"exec hakurei run {flags}-a 0 sh -c 'export PATH=/run/current-system/sw/bin:$PATH && touch /tmp/pd-silent-ready && sleep infinity' &>/tmp/pd-silent\n") machine.send_chars(f"exec hakurei run {flags}-a 0 sh -c 'export PATH=/run/current-system/sw/bin:$PATH && touch /tmp/pd-silent-ready && sleep infinity' &>/tmp/pd-silent\n")
machine.wait_for_file("/tmp/hakurei.0/tmpdir/0/pd-silent-ready", timeout=15) machine.wait_for_file("/tmp/hakurei.0/tmpdir/0/pd-silent-ready", timeout=15)
machine.succeed("rm /tmp/hakurei.0/tmpdir/0/pd-silent-ready") machine.succeed("rm /tmp/hakurei.0/tmpdir/0/pd-silent-ready")
@@ -180,7 +176,7 @@ machine.fail("getfacl --absolute-names --omit-header --numeric /run/user/1000 |
# Check interrupt shim behaviour: # Check interrupt shim behaviour:
swaymsg("exec sh -c 'ne-foot; echo -n $? > /tmp/monitor-exit-code'") swaymsg("exec sh -c 'ne-foot; echo -n $? > /tmp/monitor-exit-code'")
wait_for_window(f"u0_a{aid(0)}@machine") wait_for_window(f"u0_a{hakurei_identity(0)}@machine")
machine.succeed("pkill -INT -f 'hakurei -v app '") machine.succeed("pkill -INT -f 'hakurei -v app '")
machine.wait_until_fails("pgrep foot", timeout=5) machine.wait_until_fails("pgrep foot", timeout=5)
machine.wait_for_file("/tmp/monitor-exit-code") machine.wait_for_file("/tmp/monitor-exit-code")
@@ -190,7 +186,7 @@ if interrupt_exit_code != 230:
# Check interrupt shim behaviour immediate termination: # Check interrupt shim behaviour immediate termination:
swaymsg("exec sh -c 'ne-foot-immediate; echo -n $? > /tmp/monitor-exit-code'") swaymsg("exec sh -c 'ne-foot-immediate; echo -n $? > /tmp/monitor-exit-code'")
wait_for_window(f"u0_a{aid(0)}@machine") wait_for_window(f"u0_a{hakurei_identity(0)}@machine")
machine.succeed("pkill -INT -f 'hakurei -v app '") machine.succeed("pkill -INT -f 'hakurei -v app '")
machine.wait_until_fails("pgrep foot", timeout=5) machine.wait_until_fails("pgrep foot", timeout=5)
machine.wait_for_file("/tmp/monitor-exit-code") machine.wait_for_file("/tmp/monitor-exit-code")
@@ -200,7 +196,7 @@ if interrupt_exit_code != 254:
# Check shim SIGCONT from unexpected process behaviour: # Check shim SIGCONT from unexpected process behaviour:
swaymsg("exec sh -c 'ne-foot &> /tmp/shim-cont-unexpected-pid'") swaymsg("exec sh -c 'ne-foot &> /tmp/shim-cont-unexpected-pid'")
wait_for_window(f"u0_a{aid(0)}@machine") wait_for_window(f"u0_a{hakurei_identity(0)}@machine")
machine.succeed("pkill -CONT -f 'hakurei shim'") machine.succeed("pkill -CONT -f 'hakurei shim'")
machine.succeed("pkill -INT -f 'hakurei -v app '") machine.succeed("pkill -INT -f 'hakurei -v app '")
machine.wait_until_fails("pgrep foot", timeout=5) machine.wait_until_fails("pgrep foot", timeout=5)
@@ -209,22 +205,22 @@ print(machine.succeed('grep "shim: got SIGCONT from unexpected process$" /tmp/sh
# Start app (foot) with Wayland enablement: # Start app (foot) with Wayland enablement:
swaymsg("exec ne-foot") swaymsg("exec ne-foot")
wait_for_window(f"u0_a{aid(0)}@machine") wait_for_window(f"u0_a{hakurei_identity(0)}@machine")
machine.send_chars("clear; wayland-info && touch /tmp/client-ok\n") machine.send_chars("clear; wayland-info && touch /var/tmp/client-ok\n")
machine.wait_for_file(tmpdir_path(0, "client-ok"), timeout=15) machine.wait_for_file("/var/tmp/client-ok", timeout=15)
collect_state_ui("foot_wayland") collect_state_ui("foot_wayland")
check_state("ne-foot", {"wayland": True}) check_state("ne-foot", {"wayland": True})
# Verify lack of acl on XDG_RUNTIME_DIR: # Verify lack of acl on XDG_RUNTIME_DIR:
machine.fail(f"getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep {aid(0) + 1000000}") machine.fail(f"getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep {hakurei_identity(0) + 1000000}")
machine.send_chars("exit\n") machine.send_chars("exit\n")
machine.wait_until_fails("pgrep foot", timeout=5) machine.wait_until_fails("pgrep foot", timeout=5)
machine.fail(f"getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep {aid(0) + 1000000}", timeout=5) machine.fail(f"getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep {hakurei_identity(0) + 1000000}", timeout=5)
# Test PulseAudio (hakurei does not support PipeWire yet): # Test PulseAudio (hakurei does not support PipeWire yet):
swaymsg("exec pa-foot") swaymsg("exec pa-foot")
wait_for_window(f"u0_a{aid(1)}@machine") wait_for_window(f"u0_a{hakurei_identity(1)}@machine")
machine.send_chars("clear; pactl info && touch /tmp/pulse-ok\n") machine.send_chars("clear; pactl info && touch /var/tmp/pulse-ok\n")
machine.wait_for_file(tmpdir_path(1, "pulse-ok"), timeout=15) machine.wait_for_file("/var/tmp/pulse-ok", timeout=15)
collect_state_ui("pulse_wayland") collect_state_ui("pulse_wayland")
check_state("pa-foot", {"wayland": True, "pulse": True}) check_state("pa-foot", {"wayland": True, "pulse": True})
machine.send_chars("exit\n") machine.send_chars("exit\n")
@@ -232,9 +228,9 @@ machine.wait_until_fails("pgrep foot", timeout=5)
# Test XWayland (foot does not support X): # Test XWayland (foot does not support X):
swaymsg("exec x11-alacritty") swaymsg("exec x11-alacritty")
wait_for_window(f"u0_a{aid(0)}@machine") wait_for_window(f"u0_a{hakurei_identity(0)}@machine")
machine.send_chars("clear; glinfo && touch /tmp/x11-ok\n") machine.send_chars("clear; glinfo && touch /var/tmp/x11-ok\n")
machine.wait_for_file(tmpdir_path(0, "x11-ok"), timeout=15) machine.wait_for_file("/var/tmp/x11-ok", timeout=15)
collect_state_ui("alacritty_x11") collect_state_ui("alacritty_x11")
check_state("x11-alacritty", {"x11": True}) check_state("x11-alacritty", {"x11": True})
machine.send_chars("exit\n") machine.send_chars("exit\n")
@@ -242,26 +238,26 @@ machine.wait_until_fails("pgrep alacritty", timeout=5)
# Start app (foot) with direct Wayland access: # Start app (foot) with direct Wayland access:
swaymsg("exec da-foot") swaymsg("exec da-foot")
wait_for_window(f"u0_a{aid(3)}@machine") wait_for_window(f"u0_a{hakurei_identity(3)}@machine")
machine.send_chars("clear; wayland-info && touch /tmp/direct-ok\n") machine.send_chars("clear; wayland-info && touch /var/tmp/direct-ok\n")
collect_state_ui("foot_direct") collect_state_ui("foot_direct")
machine.wait_for_file(tmpdir_path(3, "direct-ok"), timeout=15) machine.wait_for_file("/var/tmp/direct-ok", timeout=15)
check_state("da-foot", {"wayland": True}) check_state("da-foot", {"wayland": True})
# Verify acl on XDG_RUNTIME_DIR: # Verify acl on XDG_RUNTIME_DIR:
print(machine.succeed(f"getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep {aid(3) + 1000000}")) print(machine.succeed(f"getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep {hakurei_identity(3) + 1000000}"))
machine.send_chars("exit\n") machine.send_chars("exit\n")
machine.wait_until_fails("pgrep foot", timeout=5) machine.wait_until_fails("pgrep foot", timeout=5)
# Verify acl cleanup on XDG_RUNTIME_DIR: # Verify acl cleanup on XDG_RUNTIME_DIR:
machine.wait_until_fails(f"getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep {aid(3) + 1000000}", timeout=5) machine.wait_until_fails(f"getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep {hakurei_identity(3) + 1000000}", timeout=5)
# Test syscall filter: # Test syscall filter:
print(machine.fail("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 strace-failure")) print(machine.fail("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 strace-failure"))
# Start app (foot) with Wayland enablement from a terminal: # Start app (foot) with Wayland enablement from a terminal:
swaymsg("exec foot $SHELL -c '(ne-foot) & disown && exec $SHELL'") swaymsg("exec foot $SHELL -c '(ne-foot) & disown && exec $SHELL'")
wait_for_window(f"u0_a{aid(0)}@machine") wait_for_window(f"u0_a{hakurei_identity(0)}@machine")
machine.send_chars("clear; wayland-info && touch /tmp/term-ok\n") machine.send_chars("clear; wayland-info && touch /var/tmp/term-ok\n")
machine.wait_for_file(tmpdir_path(0, "term-ok"), timeout=15) machine.wait_for_file("/var/tmp/term-ok", timeout=15)
machine.send_key("alt-h") machine.send_key("alt-h")
machine.send_chars("clear; hakurei show $(hakurei ps --short) && touch /tmp/ps-show-ok && exec cat\n") machine.send_chars("clear; hakurei show $(hakurei ps --short) && touch /tmp/ps-show-ok && exec cat\n")
machine.wait_for_file("/tmp/ps-show-ok", timeout=5) machine.wait_for_file("/tmp/ps-show-ok", timeout=5)