diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-08-11 02:52:32 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-08-11 04:56:42 +0900 |
| commit | e99d7affb0c128fa82722f9b3d79c99b5e5918cd (patch) | |
| tree | 387e08d6c4363d8b9e0462f069c140fbdb15c917 /internal | |
| parent | 41ac2be9658b49c68cb793f3a6f0c5932d82e1c2 (diff) | |
container: use absolute for pathname
This is simultaneously more efficient and less error-prone. This change caused minor API changes in multiple other packages.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/app/app_linux.go | 5 | ||||
| -rw-r--r-- | internal/app/app_linux_test.go | 2 | ||||
| -rw-r--r-- | internal/app/app_nixos_linux_test.go | 77 | ||||
| -rw-r--r-- | internal/app/app_pd_linux_test.go | 92 | ||||
| -rw-r--r-- | internal/app/app_stub_linux_test.go | 6 | ||||
| -rw-r--r-- | internal/app/container_linux.go | 94 | ||||
| -rw-r--r-- | internal/app/process_linux.go | 4 | ||||
| -rw-r--r-- | internal/app/seal_linux.go | 228 | ||||
| -rw-r--r-- | internal/sys/interface.go | 22 |
9 files changed, 274 insertions, 256 deletions
diff --git a/internal/app/app_linux.go b/internal/app/app_linux.go index 89ff6eda..1563f99d 100644 --- a/internal/app/app_linux.go +++ b/internal/app/app_linux.go @@ -7,7 +7,6 @@ import ( "hakurei.app/hst" "hakurei.app/internal/app/state" - "hakurei.app/internal/hlog" "hakurei.app/internal/sys" ) @@ -59,10 +58,6 @@ func (a *app) Seal(config *hst.Config) (SealedApp, error) { if a.outcome != nil { panic("app sealed twice") } - if config == nil { - return nil, hlog.WrapErr(ErrConfig, - "attempted to seal app with nil config") - } seal := new(outcome) seal.id = a.id diff --git a/internal/app/app_linux_test.go b/internal/app/app_linux_test.go index 7d572b59..8b96cfba 100644 --- a/internal/app/app_linux_test.go +++ b/internal/app/app_linux_test.go @@ -11,6 +11,7 @@ import ( "hakurei.app/hst" "hakurei.app/internal/app" "hakurei.app/internal/app/state" + "hakurei.app/internal/hlog" "hakurei.app/internal/sys" "hakurei.app/system" ) @@ -36,6 +37,7 @@ func TestApp(t *testing.T) { ) if !t.Run("seal", func(t *testing.T) { if sa, err := a.Seal(tc.config); err != nil { + hlog.PrintBaseError(err, "got generic error:") t.Errorf("Seal: error = %v", err) return } else { diff --git a/internal/app/app_nixos_linux_test.go b/internal/app/app_nixos_linux_test.go index 4b7b3d38..901e825a 100644 --- a/internal/app/app_nixos_linux_test.go +++ b/internal/app/app_nixos_linux_test.go @@ -12,21 +12,24 @@ import ( "hakurei.app/system/dbus" ) +func m(pathname string) *container.Absolute { return container.MustAbs(pathname) } + var testCasesNixos = []sealTestCase{ { "nixos chromium direct wayland", new(stubNixOS), &hst.Config{ ID: "org.chromium.Chromium", - Path: "/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start", + Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"), Enablements: system.EWayland | system.EDBus | system.EPulse, + Shell: m("/run/current-system/sw/bin/zsh"), Container: &hst.ContainerConfig{ Userns: true, Net: true, MapRealUID: true, Env: nil, AutoEtc: true, - Filesystem: []*hst.FilesystemConfig{ - {Src: "/bin", Must: true}, {Src: "/usr/bin/", Must: true}, - {Src: "/nix/store", Must: true}, {Src: "/run/current-system", Must: true}, - {Src: "/sys/block"}, {Src: "/sys/bus"}, {Src: "/sys/class"}, {Src: "/sys/dev"}, {Src: "/sys/devices"}, - {Src: "/run/opengl-driver", Must: true}, {Src: "/dev/dri", Device: true}, + Filesystem: []hst.FilesystemConfig{ + {Src: m("/bin"), Must: true}, {Src: m("/usr/bin/"), Must: true}, + {Src: m("/nix/store"), Must: true}, {Src: m("/run/current-system"), Must: true}, + {Src: m("/sys/block")}, {Src: m("/sys/bus")}, {Src: m("/sys/class")}, {Src: m("/sys/dev")}, {Src: m("/sys/devices")}, + {Src: m("/run/opengl-driver"), Must: true}, {Src: m("/dev/dri"), Device: true}, }, }, SystemBus: &dbus.Config{ @@ -50,7 +53,7 @@ var testCasesNixos = []sealTestCase{ DirectWayland: true, Username: "u0_a1", - Data: "/var/lib/persist/module/hakurei/0/1", + Data: m("/var/lib/persist/module/hakurei/0/1"), Identity: 1, Groups: []string{}, }, state.ID{ @@ -98,8 +101,8 @@ var testCasesNixos = []sealTestCase{ &container.Params{ Uid: 1971, Gid: 100, - Dir: "/var/lib/persist/module/hakurei/0/1", - Path: "/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start", + Dir: m("/var/lib/persist/module/hakurei/0/1"), + Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"), Args: []string{"/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"}, Env: []string{ "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1971/bus", @@ -116,34 +119,34 @@ var testCasesNixos = []sealTestCase{ "XDG_SESSION_TYPE=tty", }, Ops: new(container.Ops). - Proc("/proc/"). - Tmpfs(hst.Tmp, 4096, 0755). - DevWritable("/dev/", true). - Bind("/bin", "/bin", 0). - Bind("/usr/bin/", "/usr/bin/", 0). - Bind("/nix/store", "/nix/store", 0). - Bind("/run/current-system", "/run/current-system", 0). - Bind("/sys/block", "/sys/block", container.BindOptional). - Bind("/sys/bus", "/sys/bus", container.BindOptional). - Bind("/sys/class", "/sys/class", container.BindOptional). - Bind("/sys/dev", "/sys/dev", container.BindOptional). - Bind("/sys/devices", "/sys/devices", container.BindOptional). - Bind("/run/opengl-driver", "/run/opengl-driver", 0). - Bind("/dev/dri", "/dev/dri", container.BindDevice|container.BindWritable|container.BindOptional). - Etc("/etc/", "8e2c76b066dabe574cf073bdb46eb5c1"). - Remount("/dev/", syscall.MS_RDONLY). - Tmpfs("/run/user/", 4096, 0755). - Bind("/tmp/hakurei.1971/runtime/1", "/run/user/1971", container.BindWritable). - Bind("/tmp/hakurei.1971/tmpdir/1", "/tmp/", container.BindWritable). - Bind("/var/lib/persist/module/hakurei/0/1", "/var/lib/persist/module/hakurei/0/1", container.BindWritable). - Place("/etc/passwd", []byte("u0_a1:x:1971:100:Hakurei:/var/lib/persist/module/hakurei/0/1:/run/current-system/sw/bin/zsh\n")). - Place("/etc/group", []byte("hakurei:x:100:\n")). - Bind("/run/user/1971/wayland-0", "/run/user/1971/wayland-0", 0). - Bind("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1/pulse", "/run/user/1971/pulse/native", 0). - Place(hst.Tmp+"/pulse-cookie", nil). - Bind("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", "/run/user/1971/bus", 0). - Bind("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", "/run/dbus/system_bus_socket", 0). - Remount("/", syscall.MS_RDONLY), + Proc(m("/proc/")). + Tmpfs(hst.AbsTmp, 4096, 0755). + DevWritable(m("/dev/"), true). + Bind(m("/bin"), m("/bin"), 0). + Bind(m("/usr/bin/"), m("/usr/bin/"), 0). + Bind(m("/nix/store"), m("/nix/store"), 0). + Bind(m("/run/current-system"), m("/run/current-system"), 0). + Bind(m("/sys/block"), m("/sys/block"), container.BindOptional). + Bind(m("/sys/bus"), m("/sys/bus"), container.BindOptional). + Bind(m("/sys/class"), m("/sys/class"), container.BindOptional). + Bind(m("/sys/dev"), m("/sys/dev"), container.BindOptional). + Bind(m("/sys/devices"), m("/sys/devices"), container.BindOptional). + Bind(m("/run/opengl-driver"), m("/run/opengl-driver"), 0). + Bind(m("/dev/dri"), m("/dev/dri"), container.BindDevice|container.BindWritable|container.BindOptional). + Etc(m("/etc/"), "8e2c76b066dabe574cf073bdb46eb5c1"). + Remount(m("/dev/"), syscall.MS_RDONLY). + Tmpfs(m("/run/user/"), 4096, 0755). + Bind(m("/tmp/hakurei.1971/runtime/1"), m("/run/user/1971"), container.BindWritable). + Bind(m("/tmp/hakurei.1971/tmpdir/1"), m("/tmp/"), container.BindWritable). + Bind(m("/var/lib/persist/module/hakurei/0/1"), m("/var/lib/persist/module/hakurei/0/1"), container.BindWritable). + Place(m("/etc/passwd"), []byte("u0_a1:x:1971:100:Hakurei:/var/lib/persist/module/hakurei/0/1:/run/current-system/sw/bin/zsh\n")). + Place(m("/etc/group"), []byte("hakurei:x:100:\n")). + Bind(m("/run/user/1971/wayland-0"), m("/run/user/1971/wayland-0"), 0). + Bind(m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1/pulse"), m("/run/user/1971/pulse/native"), 0). + Place(m(hst.Tmp+"/pulse-cookie"), nil). + Bind(m("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus"), m("/run/user/1971/bus"), 0). + Bind(m("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket"), m("/run/dbus/system_bus_socket"), 0). + Remount(m("/"), syscall.MS_RDONLY), SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyTTY | seccomp.PresetDenyDevel, HostNet: true, ForwardCancel: true, diff --git a/internal/app/app_pd_linux_test.go b/internal/app/app_pd_linux_test.go index 2dd83b70..cc738ed6 100644 --- a/internal/app/app_pd_linux_test.go +++ b/internal/app/app_pd_linux_test.go @@ -16,7 +16,7 @@ import ( var testCasesPd = []sealTestCase{ { "nixos permissive defaults no enablements", new(stubNixOS), - &hst.Config{Username: "chronos", Data: "/home/chronos"}, + &hst.Config{Username: "chronos", Data: m("/home/chronos")}, state.ID{ 0x4a, 0x45, 0x0b, 0x65, 0x96, 0xd7, 0xbc, 0x15, @@ -30,8 +30,8 @@ var testCasesPd = []sealTestCase{ Ensure("/tmp/hakurei.1971/tmpdir", 0700).UpdatePermType(system.User, "/tmp/hakurei.1971/tmpdir", acl.Execute). Ensure("/tmp/hakurei.1971/tmpdir/0", 01700).UpdatePermType(system.User, "/tmp/hakurei.1971/tmpdir/0", acl.Read, acl.Write, acl.Execute), &container.Params{ - Dir: "/home/chronos", - Path: "/run/current-system/sw/bin/zsh", + Dir: m("/home/chronos"), + Path: m("/run/current-system/sw/bin/zsh"), Args: []string{"/run/current-system/sw/bin/zsh"}, Env: []string{ "HOME=/home/chronos", @@ -43,23 +43,23 @@ var testCasesPd = []sealTestCase{ "XDG_SESSION_TYPE=tty", }, Ops: new(container.Ops). - Root("/", "4a450b6596d7bc15bd01780eb9a607ac", container.BindWritable). - Proc("/proc/"). - Tmpfs(hst.Tmp, 4096, 0755). - DevWritable("/dev/", true). - Bind("/dev/kvm", "/dev/kvm", container.BindWritable|container.BindDevice|container.BindOptional). - Readonly("/var/run/nscd", 0755). - Tmpfs("/run/user/1971", 8192, 0755). - Tmpfs("/run/dbus", 8192, 0755). - Etc("/etc/", "4a450b6596d7bc15bd01780eb9a607ac"). - Remount("/dev/", syscall.MS_RDONLY). - Tmpfs("/run/user/", 4096, 0755). - Bind("/tmp/hakurei.1971/runtime/0", "/run/user/65534", container.BindWritable). - Bind("/tmp/hakurei.1971/tmpdir/0", "/tmp/", container.BindWritable). - Bind("/home/chronos", "/home/chronos", container.BindWritable). - Place("/etc/passwd", []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")). - Place("/etc/group", []byte("hakurei:x:65534:\n")). - Remount("/", syscall.MS_RDONLY), + Root(m("/"), "4a450b6596d7bc15bd01780eb9a607ac", container.BindWritable). + Proc(m("/proc/")). + Tmpfs(hst.AbsTmp, 4096, 0755). + DevWritable(m("/dev/"), true). + Bind(m("/dev/kvm"), m("/dev/kvm"), container.BindWritable|container.BindDevice|container.BindOptional). + Readonly(m("/var/run/nscd"), 0755). + Tmpfs(m("/run/user/1971"), 8192, 0755). + Tmpfs(m("/run/dbus"), 8192, 0755). + Etc(m("/etc/"), "4a450b6596d7bc15bd01780eb9a607ac"). + Remount(m("/dev/"), syscall.MS_RDONLY). + Tmpfs(m("/run/user/"), 4096, 0755). + Bind(m("/tmp/hakurei.1971/runtime/0"), m("/run/user/65534"), container.BindWritable). + Bind(m("/tmp/hakurei.1971/tmpdir/0"), m("/tmp/"), container.BindWritable). + Bind(m("/home/chronos"), m("/home/chronos"), container.BindWritable). + Place(m("/etc/passwd"), []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")). + Place(m("/etc/group"), []byte("hakurei:x:65534:\n")). + Remount(m("/"), syscall.MS_RDONLY), SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyDevel, HostNet: true, RetainSession: true, @@ -74,7 +74,7 @@ var testCasesPd = []sealTestCase{ Identity: 9, Groups: []string{"video"}, Username: "chronos", - Data: "/home/chronos", + Data: m("/home/chronos"), SessionBus: &dbus.Config{ Talk: []string{ "org.freedesktop.Notifications", @@ -160,8 +160,8 @@ var testCasesPd = []sealTestCase{ UpdatePerm("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/bus", acl.Read, acl.Write). UpdatePerm("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket", acl.Read, acl.Write), &container.Params{ - Dir: "/home/chronos", - Path: "/run/current-system/sw/bin/zsh", + Dir: m("/home/chronos"), + Path: m("/run/current-system/sw/bin/zsh"), Args: []string{"zsh", "-c", "exec chromium "}, Env: []string{ "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus", @@ -178,29 +178,29 @@ var testCasesPd = []sealTestCase{ "XDG_SESSION_TYPE=tty", }, Ops: new(container.Ops). - Root("/", "ebf083d1b175911782d413369b64ce7c", container.BindWritable). - Proc("/proc/"). - Tmpfs(hst.Tmp, 4096, 0755). - DevWritable("/dev/", true). - Bind("/dev/dri", "/dev/dri", container.BindWritable|container.BindDevice|container.BindOptional). - Bind("/dev/kvm", "/dev/kvm", container.BindWritable|container.BindDevice|container.BindOptional). - Readonly("/var/run/nscd", 0755). - Tmpfs("/run/user/1971", 8192, 0755). - Tmpfs("/run/dbus", 8192, 0755). - Etc("/etc/", "ebf083d1b175911782d413369b64ce7c"). - Remount("/dev/", syscall.MS_RDONLY). - Tmpfs("/run/user/", 4096, 0755). - Bind("/tmp/hakurei.1971/runtime/9", "/run/user/65534", container.BindWritable). - Bind("/tmp/hakurei.1971/tmpdir/9", "/tmp/", container.BindWritable). - Bind("/home/chronos", "/home/chronos", container.BindWritable). - Place("/etc/passwd", []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")). - Place("/etc/group", []byte("hakurei:x:65534:\n")). - Bind("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", "/run/user/65534/wayland-0", 0). - Bind("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c/pulse", "/run/user/65534/pulse/native", 0). - Place(hst.Tmp+"/pulse-cookie", nil). - Bind("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/bus", "/run/user/65534/bus", 0). - Bind("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket", "/run/dbus/system_bus_socket", 0). - Remount("/", syscall.MS_RDONLY), + Root(m("/"), "ebf083d1b175911782d413369b64ce7c", container.BindWritable). + Proc(m("/proc/")). + Tmpfs(hst.AbsTmp, 4096, 0755). + DevWritable(m("/dev/"), true). + Bind(m("/dev/dri"), m("/dev/dri"), container.BindWritable|container.BindDevice|container.BindOptional). + Bind(m("/dev/kvm"), m("/dev/kvm"), container.BindWritable|container.BindDevice|container.BindOptional). + Readonly(m("/var/run/nscd"), 0755). + Tmpfs(m("/run/user/1971"), 8192, 0755). + Tmpfs(m("/run/dbus"), 8192, 0755). + Etc(m("/etc/"), "ebf083d1b175911782d413369b64ce7c"). + Remount(m("/dev/"), syscall.MS_RDONLY). + Tmpfs(m("/run/user/"), 4096, 0755). + Bind(m("/tmp/hakurei.1971/runtime/9"), m("/run/user/65534"), container.BindWritable). + Bind(m("/tmp/hakurei.1971/tmpdir/9"), m("/tmp/"), container.BindWritable). + Bind(m("/home/chronos"), m("/home/chronos"), container.BindWritable). + Place(m("/etc/passwd"), []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")). + Place(m("/etc/group"), []byte("hakurei:x:65534:\n")). + Bind(m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/65534/wayland-0"), 0). + Bind(m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c/pulse"), m("/run/user/65534/pulse/native"), 0). + Place(m(hst.Tmp+"/pulse-cookie"), nil). + Bind(m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/bus"), m("/run/user/65534/bus"), 0). + Bind(m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), m("/run/dbus/system_bus_socket"), 0). + Remount(m("/"), syscall.MS_RDONLY), SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyDevel, HostNet: true, RetainSession: true, diff --git a/internal/app/app_stub_linux_test.go b/internal/app/app_stub_linux_test.go index 0efc585d..f386eb01 100644 --- a/internal/app/app_stub_linux_test.go +++ b/internal/app/app_stub_linux_test.go @@ -127,8 +127,8 @@ func (s *stubNixOS) Open(name string) (fs.File, error) { func (s *stubNixOS) Paths() hst.Paths { return hst.Paths{ - SharePath: "/tmp/hakurei.1971", - RuntimePath: "/run/user/1971", - RunDirPath: "/run/user/1971/hakurei", + SharePath: m("/tmp/hakurei.1971"), + RuntimePath: m("/run/user/1971"), + RunDirPath: m("/run/user/1971/hakurei"), } } diff --git a/internal/app/container_linux.go b/internal/app/container_linux.go index 1ea23d44..f93cfcc2 100644 --- a/internal/app/container_linux.go +++ b/internal/app/container_linux.go @@ -12,6 +12,7 @@ import ( "hakurei.app/container" "hakurei.app/container/seccomp" "hakurei.app/hst" + "hakurei.app/internal/hlog" "hakurei.app/internal/sys" "hakurei.app/system/dbus" ) @@ -24,7 +25,7 @@ const preallocateOpsCount = 1 << 5 // Note that remaining container setup must be queued by the caller. func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid *int) (*container.Params, map[string]string, error) { if s == nil { - return nil, nil, syscall.EBADE + return nil, nil, hlog.WrapErr(syscall.EBADE, "invalid container configuration") } params := &container.Params{ @@ -73,21 +74,18 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid *gid = container.OverflowGid() } - if s.AutoRoot != "" { - if !path.IsAbs(s.AutoRoot) { - return nil, nil, fmt.Errorf("auto root target %q not absolute", s.AutoRoot) - } + if s.AutoRoot != nil { params.Root(s.AutoRoot, prefix, s.RootFlags) } params. - Proc(container.FHSProc). - Tmpfs(hst.Tmp, 1<<12, 0755) + Proc(container.AbsFHSProc). + Tmpfs(hst.AbsTmp, 1<<12, 0755) if !s.Device { - params.DevWritable(container.FHSDev, true) + params.DevWritable(container.AbsFHSDev, true) } else { - params.Bind(container.FHSDev, container.FHSDev, container.BindWritable|container.BindDevice) + params.Bind(container.AbsFHSDev, container.AbsFHSDev, container.BindWritable|container.BindDevice) } /* retrieve paths and hide them if they're made available in the sandbox; @@ -96,7 +94,7 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid and should not be treated as such, ALWAYS be careful with what you bind */ var hidePaths []string sc := os.Paths() - hidePaths = append(hidePaths, sc.RuntimePath, sc.SharePath) + hidePaths = append(hidePaths, sc.RuntimePath.String(), sc.SharePath.String()) _, systemBusAddr := dbus.Address() if entries, err := dbus.Parse([]byte(systemBusAddr)); err != nil { return nil, nil, err @@ -132,15 +130,15 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid hidePathSource := make([][2]string, 0, len(s.Filesystem)) // AutoRoot is a collection of many BindMountOp internally - if s.AutoRoot != "" { - if d, err := os.ReadDir(s.AutoRoot); err != nil { + if s.AutoRoot != nil { + if d, err := os.ReadDir(s.AutoRoot.String()); err != nil { return nil, nil, err } else { hidePathSource = slices.Grow(hidePathSource, len(d)) for _, ent := range d { name := ent.Name() if container.IsAutoRootBindable(name) { - name = path.Join(s.AutoRoot, name) + name = path.Join(s.AutoRoot.String(), name) srcP := [2]string{name, name} if err = evalSymlinks(os, &srcP[0]); err != nil { return nil, nil, err @@ -151,16 +149,16 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid } } - for _, c := range s.Filesystem { - if c == nil { - continue + for i, c := range s.Filesystem { + if c.Src == nil { + return nil, nil, fmt.Errorf("invalid filesystem at index %d", i) } // special filesystems - switch c.Src { - case hst.SourceTmpfs: - if !path.IsAbs(c.Dst) { - return nil, nil, fmt.Errorf("tmpfs dst %q is not absolute", c.Dst) + switch c.Src.String() { + case container.Nonexistent: + if c.Dst == nil { + return nil, nil, errors.New("tmpfs dst must not be nil") } if c.Write { params.Tmpfs(c.Dst, hst.TmpfsSize, hst.TmpfsPerm) @@ -170,18 +168,12 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid continue } - if !path.IsAbs(c.Src) { - return nil, nil, fmt.Errorf("src path %q is not absolute", c.Src) - } - - dest := c.Dst - if c.Dst == "" { - dest = c.Src - } else if !path.IsAbs(dest) { - return nil, nil, fmt.Errorf("dst path %q is not absolute", dest) + dst := c.Dst + if dst == nil { + dst = c.Src } - p := [2]string{c.Src, c.Src} + p := [2]string{c.Src.String(), c.Src.String()} if err := evalSymlinks(os, &p[0]); err != nil { return nil, nil, err } @@ -197,7 +189,7 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid if !c.Must { flags |= container.BindOptional } - params.Bind(c.Src, dest, flags) + params.Bind(c.Src, dst, flags) } for _, p := range hidePathSource { @@ -219,29 +211,49 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid // cover matched paths for i, ok := range hidePathMatch { if ok { - params.Tmpfs(hidePaths[i], 1<<13, 0755) + if a, err := container.NewAbs(hidePaths[i]); err != nil { + var absoluteError *container.AbsoluteError + if !errors.As(err, &absoluteError) { + return nil, nil, err + } + if absoluteError == nil { + return nil, nil, syscall.ENOTRECOVERABLE + } + return nil, nil, fmt.Errorf("invalid path hiding candidate %q", absoluteError.Pathname) + } else { + params.Tmpfs(a, 1<<13, 0755) + } } } - for _, l := range s.Link { - params.Link(l[0], l[1]) + for i, l := range s.Link { + if l.Target == nil || l.Linkname == "" { + return nil, nil, fmt.Errorf("invalid link at index %d", i) + } + linkname := l.Linkname + var dereference bool + if linkname[0] == '*' && path.IsAbs(linkname[1:]) { + linkname = linkname[1:] + dereference = true + } + params.Link(l.Target, linkname, dereference) } if !s.AutoEtc { - if s.Etc != "" { - params.Bind(s.Etc, container.FHSEtc, 0) + if s.Etc != nil { + params.Bind(s.Etc, container.AbsFHSEtc, 0) } } else { - etcPath := s.Etc - if etcPath == "" { - etcPath = container.FHSEtc + if s.Etc == nil { + params.Etc(container.AbsFHSEtc, prefix) + } else { + params.Etc(s.Etc, prefix) } - params.Etc(etcPath, prefix) } // no more ContainerConfig paths beyond this point if !s.Device { - params.Remount(container.FHSDev, syscall.MS_RDONLY) + params.Remount(container.AbsFHSDev, syscall.MS_RDONLY) } return params, maps.Clone(s.Env), nil diff --git a/internal/app/process_linux.go b/internal/app/process_linux.go index 088deab3..11d4c8e9 100644 --- a/internal/app/process_linux.go +++ b/internal/app/process_linux.go @@ -39,7 +39,7 @@ func (seal *outcome) Run(rs *RunState) error { if err := seal.sys.Commit(seal.ctx); err != nil { return err } - store := state.NewMulti(seal.runDirPath) + store := state.NewMulti(seal.runDirPath.String()) deferredStoreFunc := func(c state.Cursor) error { return nil } // noop until state in store defer func() { var revertErr error @@ -128,7 +128,7 @@ func (seal *outcome) Run(rs *RunState) error { os.Getpid(), seal.waitDelay, seal.container, - seal.user.data, + seal.user.data.String(), hlog.Load(), }) }() diff --git a/internal/app/seal_linux.go b/internal/app/seal_linux.go index 5b2b2c0e..e27fee10 100644 --- a/internal/app/seal_linux.go +++ b/internal/app/seal_linux.go @@ -49,10 +49,8 @@ const ( ) var ( - ErrConfig = errors.New("no configuration to seal") - ErrUser = errors.New("invalid aid") - ErrHome = errors.New("invalid home directory") - ErrName = errors.New("invalid username") + ErrIdent = errors.New("invalid identity") + ErrName = errors.New("invalid username") ErrXDisplay = errors.New(display + " unset") @@ -67,8 +65,8 @@ var posixUsername = regexp.MustCompilePOSIX("^[a-z_]([A-Za-z0-9_-]{0,31}|[A-Za-z type outcome struct { // copied from initialising [app] id *stringPair[state.ID] - // copied from [sys.State] response - runDirPath string + // copied from [sys.State] + runDirPath *container.Absolute // initial [hst.Config] gob stream for state data; // this is prepared ahead of time as config is clobbered during seal creation @@ -93,9 +91,9 @@ type shareHost struct { // whether XDG_RUNTIME_DIR is used post hsu useRuntimeDir bool // process-specific directory in tmpdir, empty if unused - sharePath string + sharePath *container.Absolute // process-specific directory in XDG_RUNTIME_DIR, empty if unused - runtimeSharePath string + runtimeSharePath *container.Absolute seal *outcome sc hst.Paths @@ -107,48 +105,48 @@ func (share *shareHost) ensureRuntimeDir() { return } share.useRuntimeDir = true - share.seal.sys.Ensure(share.sc.RunDirPath, 0700) - share.seal.sys.UpdatePermType(system.User, share.sc.RunDirPath, acl.Execute) - share.seal.sys.Ensure(share.sc.RuntimePath, 0700) // ensure this dir in case XDG_RUNTIME_DIR is unset - share.seal.sys.UpdatePermType(system.User, share.sc.RuntimePath, acl.Execute) + share.seal.sys.Ensure(share.sc.RunDirPath.String(), 0700) + share.seal.sys.UpdatePermType(system.User, share.sc.RunDirPath.String(), acl.Execute) + share.seal.sys.Ensure(share.sc.RuntimePath.String(), 0700) // ensure this dir in case XDG_RUNTIME_DIR is unset + share.seal.sys.UpdatePermType(system.User, share.sc.RuntimePath.String(), acl.Execute) } // instance returns a process-specific share path within tmpdir -func (share *shareHost) instance() string { - if share.sharePath != "" { +func (share *shareHost) instance() *container.Absolute { + if share.sharePath != nil { return share.sharePath } - share.sharePath = path.Join(share.sc.SharePath, share.seal.id.String()) - share.seal.sys.Ephemeral(system.Process, share.sharePath, 0711) + share.sharePath = share.sc.SharePath.Append(share.seal.id.String()) + share.seal.sys.Ephemeral(system.Process, share.sharePath.String(), 0711) return share.sharePath } // runtime returns a process-specific share path within XDG_RUNTIME_DIR -func (share *shareHost) runtime() string { - if share.runtimeSharePath != "" { +func (share *shareHost) runtime() *container.Absolute { + if share.runtimeSharePath != nil { return share.runtimeSharePath } share.ensureRuntimeDir() - share.runtimeSharePath = path.Join(share.sc.RunDirPath, share.seal.id.String()) - share.seal.sys.Ephemeral(system.Process, share.runtimeSharePath, 0700) - share.seal.sys.UpdatePerm(share.runtimeSharePath, acl.Execute) + share.runtimeSharePath = share.sc.RunDirPath.Append(share.seal.id.String()) + share.seal.sys.Ephemeral(system.Process, share.runtimeSharePath.String(), 0700) + share.seal.sys.UpdatePerm(share.runtimeSharePath.String(), acl.Execute) return share.runtimeSharePath } // hsuUser stores post-hsu credentials and metadata type hsuUser struct { - // application id + // identity aid *stringPair[int] - // target uid resolved by fid:aid + // target uid resolved by hid:aid uid *stringPair[int] // supplementary group ids supp []string // home directory host path - data string + data *container.Absolute // app user home directory - home string + home *container.Absolute // passwd database username username string } @@ -159,6 +157,13 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co } seal.ctx = ctx + if config == nil { + return hlog.WrapErr(syscall.EINVAL, syscall.EINVAL.Error()) + } + if config.Data == nil { + return hlog.WrapErr(os.ErrInvalid, "invalid data directory") + } + { // encode initial configuration for state tracking ct := new(bytes.Buffer) @@ -171,7 +176,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co // allowed aid range 0 to 9999, this is checked again in hsu if config.Identity < 0 || config.Identity > 9999 { - return hlog.WrapErr(ErrUser, + return hlog.WrapErr(ErrIdent, fmt.Sprintf("identity %d out of range", config.Identity)) } @@ -188,11 +193,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co return hlog.WrapErr(ErrName, fmt.Sprintf("invalid user name %q", seal.user.username)) } - if seal.user.data == "" || !path.IsAbs(seal.user.data) { - return hlog.WrapErr(ErrHome, - fmt.Sprintf("invalid home directory %q", seal.user.data)) - } - if seal.user.home == "" { + if seal.user.home == nil { seal.user.home = seal.user.data } if u, err := sys.Uid(seal.user.aid.unwrap()); err != nil { @@ -210,26 +211,25 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co } } - // this also falls back to host path if encountering an invalid path - if !path.IsAbs(config.Shell) { - config.Shell = "/bin/sh" - if s, ok := sys.LookupEnv(shell); ok && path.IsAbs(s) { - config.Shell = s - } - } - // do not use the value of shell before this point - // permissive defaults if config.Container == nil { hlog.Verbose("container configuration not supplied, PROCEED WITH CAUTION") + if config.Shell == nil { + config.Shell = container.AbsFHSRoot.Append("bin", "sh") + s, _ := sys.LookupEnv(shell) + if a, err := container.NewAbs(s); err == nil { + config.Shell = a + } + } + // hsu clears the environment so resolve paths early - if !path.IsAbs(config.Path) { + if config.Path == nil { if len(config.Args) > 0 { if p, err := sys.LookPath(config.Args[0]); err != nil { return hlog.WrapErr(err, err.Error()) - } else { - config.Path = p + } else if config.Path, err = container.NewAbs(p); err != nil { + return hlog.WrapErr(err, err.Error()) } } else { config.Path = config.Shell @@ -242,26 +242,34 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co Tty: true, AutoEtc: true, - AutoRoot: container.FHSRoot, + AutoRoot: container.AbsFHSRoot, RootFlags: container.BindWritable, } // bind GPU stuff if config.Enablements&(system.EX11|system.EWayland) != 0 { - conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Src: container.FHSDev + "dri", Device: true}) + conf.Filesystem = append(conf.Filesystem, hst.FilesystemConfig{Src: container.AbsFHSDev.Append("dri"), Device: true}) } // opportunistically bind kvm - conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Src: container.FHSDev + "kvm", Device: true}) + conf.Filesystem = append(conf.Filesystem, hst.FilesystemConfig{Src: container.AbsFHSDev.Append("kvm"), Device: true}) // hide nscd from container if present - const nscd = container.FHSVar + "run/nscd" - if _, err := sys.Stat(nscd); !errors.Is(err, fs.ErrNotExist) { - conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Dst: nscd, Src: hst.SourceTmpfs}) + nscd := container.AbsFHSVar.Append("run/nscd") + if _, err := sys.Stat(nscd.String()); !errors.Is(err, fs.ErrNotExist) { + conf.Filesystem = append(conf.Filesystem, hst.FilesystemConfig{Dst: nscd, Src: container.AbsNonexistent}) } config.Container = conf } + // late nil checks for pd behaviour + if config.Shell == nil { + return hlog.WrapErr(syscall.EINVAL, "invalid shell path") + } + if config.Path == nil { + return hlog.WrapErr(syscall.EINVAL, "invalid program path") + } + var mapuid, mapgid *stringPair[int] { var uid, gid int @@ -272,12 +280,8 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co return hlog.WrapErrSuffix(err, "cannot initialise container configuration:") } - if !path.IsAbs(config.Path) { - return hlog.WrapErr(syscall.EINVAL, - "invalid program path") - } if len(config.Args) == 0 { - config.Args = []string{config.Path} + config.Args = []string{config.Path.String()} } seal.container.Path = config.Path seal.container.Args = config.Args @@ -290,56 +294,52 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co } // inner XDG_RUNTIME_DIR default formatting of `/run/user/%d` as mapped uid - innerRuntimeDir := path.Join(container.FHSRunUser, mapuid.String()) - seal.env[xdgRuntimeDir] = innerRuntimeDir + innerRuntimeDir := container.AbsFHSRunUser.Append(mapuid.String()) + seal.env[xdgRuntimeDir] = innerRuntimeDir.String() seal.env[xdgSessionClass] = "user" seal.env[xdgSessionType] = "tty" share := &shareHost{seal: seal, sc: sys.Paths()} seal.runDirPath = share.sc.RunDirPath seal.sys = system.New(seal.user.uid.unwrap()) - seal.sys.Ensure(share.sc.SharePath, 0711) + seal.sys.Ensure(share.sc.SharePath.String(), 0711) { - runtimeDir := path.Join(share.sc.SharePath, "runtime") - seal.sys.Ensure(runtimeDir, 0700) - seal.sys.UpdatePermType(system.User, runtimeDir, acl.Execute) - runtimeDirInst := path.Join(runtimeDir, seal.user.aid.String()) - seal.sys.Ensure(runtimeDirInst, 0700) - seal.sys.UpdatePermType(system.User, runtimeDirInst, acl.Read, acl.Write, acl.Execute) - seal.container.Tmpfs(container.FHSRunUser, 1<<12, 0755) + runtimeDir := share.sc.SharePath.Append("runtime") + seal.sys.Ensure(runtimeDir.String(), 0700) + seal.sys.UpdatePermType(system.User, runtimeDir.String(), acl.Execute) + runtimeDirInst := runtimeDir.Append(seal.user.aid.String()) + seal.sys.Ensure(runtimeDirInst.String(), 0700) + seal.sys.UpdatePermType(system.User, runtimeDirInst.String(), acl.Read, acl.Write, acl.Execute) + seal.container.Tmpfs(container.AbsFHSRunUser, 1<<12, 0755) seal.container.Bind(runtimeDirInst, innerRuntimeDir, container.BindWritable) } { - tmpdir := path.Join(share.sc.SharePath, "tmpdir") - seal.sys.Ensure(tmpdir, 0700) - seal.sys.UpdatePermType(system.User, tmpdir, acl.Execute) - tmpdirInst := path.Join(tmpdir, seal.user.aid.String()) - seal.sys.Ensure(tmpdirInst, 01700) - seal.sys.UpdatePermType(system.User, tmpdirInst, acl.Read, acl.Write, acl.Execute) + tmpdir := share.sc.SharePath.Append("tmpdir") + seal.sys.Ensure(tmpdir.String(), 0700) + seal.sys.UpdatePermType(system.User, tmpdir.String(), acl.Execute) + tmpdirInst := tmpdir.Append(seal.user.aid.String()) + seal.sys.Ensure(tmpdirInst.String(), 01700) + seal.sys.UpdatePermType(system.User, tmpdirInst.String(), acl.Read, acl.Write, acl.Execute) // mount inner /tmp from share so it shares persistence and storage behaviour of host /tmp - seal.container.Bind(tmpdirInst, container.FHSTmp, container.BindWritable) + seal.container.Bind(tmpdirInst, container.AbsFHSTmp, container.BindWritable) } { - homeDir := container.FHSVarEmpty - if seal.user.home != "" { - homeDir = seal.user.home - } username := "chronos" if seal.user.username != "" { username = seal.user.username } - seal.container.Bind(seal.user.data, homeDir, container.BindWritable) - seal.container.Dir = homeDir - seal.env["HOME"] = homeDir + seal.container.Bind(seal.user.data, seal.user.home, container.BindWritable) + seal.container.Dir = seal.user.home + seal.env["HOME"] = seal.user.home.String() seal.env["USER"] = username - seal.env[shell] = config.Shell + seal.env[shell] = config.Shell.String() - seal.container.Place(container.FHSEtc+"passwd", - []byte(username+":x:"+mapuid.String()+":"+mapgid.String()+":Hakurei:"+homeDir+":"+config.Shell+"\n")) - seal.container.Place(container.FHSEtc+"group", + seal.container.Place(container.AbsFHSEtc.Append("passwd"), + []byte(username+":x:"+mapuid.String()+":"+mapgid.String()+":Hakurei:"+seal.user.home.String()+":"+config.Shell.String()+"\n")) + seal.container.Place(container.AbsFHSEtc.Append("group"), []byte("hakurei:x:"+mapgid.String()+":\n")) } @@ -350,17 +350,17 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co if config.Enablements&system.EWayland != 0 { // outer wayland socket (usually `/run/user/%d/wayland-%d`) - var socketPath string + var socketPath *container.Absolute if name, ok := sys.LookupEnv(wayland.WaylandDisplay); !ok { hlog.Verbose(wayland.WaylandDisplay + " is not set, assuming " + wayland.FallbackName) - socketPath = path.Join(share.sc.RuntimePath, wayland.FallbackName) - } else if !path.IsAbs(name) { - socketPath = path.Join(share.sc.RuntimePath, name) + socketPath = share.sc.RuntimePath.Append(wayland.FallbackName) + } else if a, err := container.NewAbs(name); err != nil { + socketPath = share.sc.RuntimePath.Append(name) } else { - socketPath = name + socketPath = a } - innerPath := path.Join(innerRuntimeDir, wayland.FallbackName) + innerPath := innerRuntimeDir.Append(wayland.FallbackName) seal.env[wayland.WaylandDisplay] = wayland.FallbackName if !config.DirectWayland { // set up security-context-v1 @@ -370,14 +370,14 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co appID = "app.hakurei." + seal.id.String() } // downstream socket paths - outerPath := path.Join(share.instance(), "wayland") - seal.sys.Wayland(&seal.sync, outerPath, socketPath, appID, seal.id.String()) + outerPath := share.instance().Append("wayland") + seal.sys.Wayland(&seal.sync, outerPath.String(), socketPath.String(), appID, seal.id.String()) seal.container.Bind(outerPath, innerPath, 0) } else { // bind mount wayland socket (insecure) hlog.Verbose("direct wayland access, PROCEED WITH CAUTION") share.ensureRuntimeDir() seal.container.Bind(socketPath, innerPath, 0) - seal.sys.UpdatePermType(system.EWayland, socketPath, acl.Read, acl.Write, acl.Execute) + seal.sys.UpdatePermType(system.EWayland, socketPath.String(), acl.Read, acl.Write, acl.Execute) } } @@ -388,17 +388,18 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co } else { seal.sys.ChangeHosts("#" + seal.user.uid.String()) seal.env[display] = d - seal.container.Bind(container.FHSTmp+".X11-unix", container.FHSTmp+".X11-unix", 0) + socketDir := container.AbsFHSTmp.Append(".X11-unix") + seal.container.Bind(socketDir, socketDir, 0) } } if config.Enablements&system.EPulse != 0 { // PulseAudio runtime directory (usually `/run/user/%d/pulse`) - pulseRuntimeDir := path.Join(share.sc.RuntimePath, "pulse") + pulseRuntimeDir := share.sc.RuntimePath.Append("pulse") // PulseAudio socket (usually `/run/user/%d/pulse/native`) - pulseSocket := path.Join(pulseRuntimeDir, "native") + pulseSocket := pulseRuntimeDir.Append("native") - if _, err := sys.Stat(pulseRuntimeDir); err != nil { + if _, err := sys.Stat(pulseRuntimeDir.String()); err != nil { if !errors.Is(err, fs.ErrNotExist) { return hlog.WrapErrSuffix(err, fmt.Sprintf("cannot access PulseAudio directory %q:", pulseRuntimeDir)) @@ -407,7 +408,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co fmt.Sprintf("PulseAudio directory %q not found", pulseRuntimeDir)) } - if s, err := sys.Stat(pulseSocket); err != nil { + if s, err := sys.Stat(pulseSocket.String()); err != nil { if !errors.Is(err, fs.ErrNotExist) { return hlog.WrapErrSuffix(err, fmt.Sprintf("cannot access PulseAudio socket %q:", pulseSocket)) @@ -422,19 +423,19 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co } // hard link pulse socket into target-executable share - innerPulseRuntimeDir := path.Join(share.runtime(), "pulse") - innerPulseSocket := path.Join(innerRuntimeDir, "pulse", "native") - seal.sys.Link(pulseSocket, innerPulseRuntimeDir) + innerPulseRuntimeDir := share.runtime().Append("pulse") + innerPulseSocket := innerRuntimeDir.Append("pulse", "native") + seal.sys.Link(pulseSocket.String(), innerPulseRuntimeDir.String()) seal.container.Bind(innerPulseRuntimeDir, innerPulseSocket, 0) - seal.env[pulseServer] = "unix:" + innerPulseSocket + seal.env[pulseServer] = "unix:" + innerPulseSocket.String() // publish current user's pulse cookie for target user if src, err := discoverPulseCookie(sys); err != nil { // not fatal hlog.Verbose(strings.TrimSpace(err.(*hlog.BaseError).Message())) } else { - innerDst := hst.Tmp + "/pulse-cookie" - seal.env[pulseCookie] = innerDst + innerDst := hst.AbsTmp.Append("/pulse-cookie") + seal.env[pulseCookie] = innerDst.String() var payload *[]byte seal.container.PlaceP(innerDst, &payload) seal.sys.CopyFile(payload, src, 256, 256) @@ -448,13 +449,12 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co } // downstream socket paths - sharePath := share.instance() - sessionPath, systemPath := path.Join(sharePath, "bus"), path.Join(sharePath, "system_bus_socket") + sessionPath, systemPath := share.instance().Append("bus"), share.instance().Append("system_bus_socket") // configure dbus proxy if f, err := seal.sys.ProxyDBus( config.SessionBus, config.SystemBus, - sessionPath, systemPath, + sessionPath.String(), systemPath.String(), ); err != nil { return err } else { @@ -462,20 +462,20 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co } // share proxy sockets - sessionInner := path.Join(innerRuntimeDir, "bus") - seal.env[dbusSessionBusAddress] = "unix:path=" + sessionInner + sessionInner := innerRuntimeDir.Append("bus") + seal.env[dbusSessionBusAddress] = "unix:path=" + sessionInner.String() seal.container.Bind(sessionPath, sessionInner, 0) - seal.sys.UpdatePerm(sessionPath, acl.Read, acl.Write) + seal.sys.UpdatePerm(sessionPath.String(), acl.Read, acl.Write) if config.SystemBus != nil { - systemInner := container.FHSRun + "dbus/system_bus_socket" - seal.env[dbusSystemBusAddress] = "unix:path=" + systemInner + systemInner := container.AbsFHSRun.Append("dbus/system_bus_socket") + seal.env[dbusSystemBusAddress] = "unix:path=" + systemInner.String() seal.container.Bind(systemPath, systemInner, 0) - seal.sys.UpdatePerm(systemPath, acl.Read, acl.Write) + seal.sys.UpdatePerm(systemPath.String(), acl.Read, acl.Write) } } // mount root read-only as the final setup Op - seal.container.Remount(container.FHSRoot, syscall.MS_RDONLY) + seal.container.Remount(container.AbsFHSRoot, syscall.MS_RDONLY) // append ExtraPerms last for _, p := range config.ExtraPerms { @@ -484,7 +484,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co } if p.Ensure { - seal.sys.Ensure(p.Path, 0700) + seal.sys.Ensure(p.Path.String(), 0700) } perms := make(acl.Perms, 0, 3) @@ -497,7 +497,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co if p.Execute { perms = append(perms, acl.Execute) } - seal.sys.UpdatePermType(system.User, p.Path, perms...) + seal.sys.UpdatePermType(system.User, p.Path.String(), perms...) } // flatten and sort env for deterministic behaviour diff --git a/internal/sys/interface.go b/internal/sys/interface.go index 04bfe13a..5dbe18e6 100644 --- a/internal/sys/interface.go +++ b/internal/sys/interface.go @@ -3,10 +3,11 @@ package sys import ( "io/fs" + "log" "os/user" - "path" "strconv" + "hakurei.app/container" "hakurei.app/hst" "hakurei.app/internal/hlog" ) @@ -50,18 +51,23 @@ type State interface { // CopyPaths is a generic implementation of [hst.Paths]. func CopyPaths(os State, v *hst.Paths) { - v.SharePath = path.Join(os.TempDir(), "hakurei."+strconv.Itoa(os.Getuid())) + if tempDir, err := container.NewAbs(os.TempDir()); err != nil { + log.Fatalf("invalid TMPDIR: %v", err) + } else { + v.TempDir = tempDir + } + v.SharePath = v.TempDir.Append("hakurei." + strconv.Itoa(os.Getuid())) hlog.Verbosef("process share directory at %q", v.SharePath) - if r, ok := os.LookupEnv(xdgRuntimeDir); !ok || r == "" || !path.IsAbs(r) { + r, _ := os.LookupEnv(xdgRuntimeDir) + if a, err := container.NewAbs(r); err != nil { // fall back to path in share since hakurei has no hard XDG dependency - v.RunDirPath = path.Join(v.SharePath, "run") - v.RuntimePath = path.Join(v.RunDirPath, "compat") + v.RunDirPath = v.SharePath.Append("run") + v.RuntimePath = v.RunDirPath.Append("compat") } else { - v.RuntimePath = r - v.RunDirPath = path.Join(v.RuntimePath, "hakurei") + v.RuntimePath = a + v.RunDirPath = v.RuntimePath.Append("hakurei") } - hlog.Verbosef("runtime directory at %q", v.RunDirPath) } |
