From c6be82bcf91fc4b6e61d9df40213e80367addbd8 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sun, 3 Aug 2025 20:46:41 +0900 Subject: [PATCH] container/path: fhs path constants This increases readability since this can help disambiguate absolute paths from similarly named path segments. Signed-off-by: Ophestra --- cmd/hakurei/command.go | 3 +- cmd/hakurei/print_test.go | 20 ++++++------- cmd/hpkg/app.go | 19 ++++++------ cmd/hpkg/main.go | 13 ++++---- cmd/hpkg/paths.go | 3 +- cmd/hpkg/with.go | 15 +++++----- container/autoetc.go | 8 ++--- container/autoroot.go | 2 +- container/container.go | 8 ++--- container/init.go | 16 +++++----- container/mount.go | 2 +- container/ops.go | 10 +++---- container/path.go | 44 ++++++++++++++++++++++++++-- container/sysctl.go | 6 ++-- hst/template.go | 28 +++++++++--------- hst/template_test.go | 4 +-- internal/app/app_nixos_linux_test.go | 16 +++++----- internal/app/app_pd_linux_test.go | 24 +++++++-------- internal/app/container_linux.go | 14 ++++----- internal/app/process_linux.go | 2 +- internal/app/seal_linux.go | 26 ++++++++-------- internal/sys/std.go | 2 +- ldd/exec.go | 2 +- 23 files changed, 164 insertions(+), 123 deletions(-) diff --git a/cmd/hakurei/command.go b/cmd/hakurei/command.go index bbe679f..8374975 100644 --- a/cmd/hakurei/command.go +++ b/cmd/hakurei/command.go @@ -14,6 +14,7 @@ import ( "time" "hakurei.app/command" + "hakurei.app/container" "hakurei.app/hst" "hakurei.app/internal" "hakurei.app/internal/app" @@ -94,7 +95,7 @@ func buildCommand(out io.Writer) command.Command { Gid: us, Username: "chronos", Name: "Hakurei Permissive Default", - HomeDir: "/var/empty", + HomeDir: container.FHSVarEmpty, } } else { passwd = u diff --git a/cmd/hakurei/print_test.go b/cmd/hakurei/print_test.go index 0a4e91b..4a79ba6 100644 --- a/cmd/hakurei/print_test.go +++ b/cmd/hakurei/print_test.go @@ -43,12 +43,12 @@ func Test_printShowInstance(t *testing.T) { Hostname: localhost Flags: userns devel net device tty mapuid autoetc Root: /var/lib/hakurei/base/org.debian (2) - Etc: /etc + Etc: /etc/ Path: /run/current-system/sw/bin/chromium Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland Filesystem - w+tmpfs:/tmp + w+tmpfs:/tmp/ +/nix/store +/run/current-system +/run/opengl-driver @@ -123,12 +123,12 @@ App Hostname: localhost Flags: userns devel net device tty mapuid autoetc Root: /var/lib/hakurei/base/org.debian (2) - Etc: /etc + Etc: /etc/ Path: /run/current-system/sw/bin/chromium Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland Filesystem - w+tmpfs:/tmp + w+tmpfs:/tmp/ +/nix/store +/run/current-system +/run/opengl-driver @@ -276,7 +276,7 @@ App "device": true, "filesystem": [ { - "dst": "/tmp", + "dst": "/tmp/", "src": "tmpfs", "write": true }, @@ -311,7 +311,7 @@ App ], "auto_root": "/var/lib/hakurei/base/org.debian", "root_flags": 2, - "etc": "/etc", + "etc": "/etc/", "auto_etc": true } }, @@ -408,7 +408,7 @@ App "device": true, "filesystem": [ { - "dst": "/tmp", + "dst": "/tmp/", "src": "tmpfs", "write": true }, @@ -443,7 +443,7 @@ App ], "auto_root": "/var/lib/hakurei/base/org.debian", "root_flags": 2, - "etc": "/etc", + "etc": "/etc/", "auto_etc": true } } @@ -594,7 +594,7 @@ func Test_printPs(t *testing.T) { "device": true, "filesystem": [ { - "dst": "/tmp", + "dst": "/tmp/", "src": "tmpfs", "write": true }, @@ -629,7 +629,7 @@ func Test_printPs(t *testing.T) { ], "auto_root": "/var/lib/hakurei/base/org.debian", "root_flags": 2, - "etc": "/etc", + "etc": "/etc/", "auto_etc": true } }, diff --git a/cmd/hpkg/app.go b/cmd/hpkg/app.go index 082bc28..fb784d7 100644 --- a/cmd/hpkg/app.go +++ b/cmd/hpkg/app.go @@ -6,6 +6,7 @@ import ( "os" "path" + "hakurei.app/container" "hakurei.app/container/seccomp" "hakurei.app/hst" "hakurei.app/system" @@ -94,17 +95,17 @@ func (app *appInfo) toFst(pathSet *appPathSet, argv []string, flagDropShell bool Filesystem: []*hst.FilesystemConfig{ {Src: path.Join(pathSet.nixPath, "store"), Dst: "/nix/store", Must: true}, {Src: pathSet.metaPath, Dst: path.Join(hst.Tmp, "app"), Must: true}, - {Src: "/etc/resolv.conf"}, - {Src: "/sys/block"}, - {Src: "/sys/bus"}, - {Src: "/sys/class"}, - {Src: "/sys/dev"}, - {Src: "/sys/devices"}, + {Src: container.FHSEtc + "resolv.conf"}, + {Src: container.FHSSys + "block"}, + {Src: container.FHSSys + "bus"}, + {Src: container.FHSSys + "class"}, + {Src: container.FHSSys + "dev"}, + {Src: container.FHSSys + "devices"}, }, Link: [][2]string{ - {app.CurrentSystem, "/run/current-system"}, - {"/run/current-system/sw/bin", "/bin"}, - {"/run/current-system/sw/bin", "/usr/bin"}, + {app.CurrentSystem, container.FHSRun + "current-system"}, + {container.FHSRun + "current-system/sw/bin", "/bin"}, + {container.FHSRun + "current-system/sw/bin", container.FHSUsrBin}, }, Etc: path.Join(pathSet.cacheDir, "etc"), AutoEtc: true, diff --git a/cmd/hpkg/main.go b/cmd/hpkg/main.go index d808aa0..728ee14 100644 --- a/cmd/hpkg/main.go +++ b/cmd/hpkg/main.go @@ -11,6 +11,7 @@ import ( "syscall" "hakurei.app/command" + "hakurei.app/container" "hakurei.app/hst" "hakurei.app/internal" "hakurei.app/internal/hlog" @@ -275,12 +276,12 @@ func main() { "path:" + a.NixGL + "#nixVulkanNvidia", }, true, func(config *hst.Config) *hst.Config { config.Container.Filesystem = append(config.Container.Filesystem, []*hst.FilesystemConfig{ - {Src: "/etc/resolv.conf"}, - {Src: "/sys/block"}, - {Src: "/sys/bus"}, - {Src: "/sys/class"}, - {Src: "/sys/dev"}, - {Src: "/sys/devices"}, + {Src: container.FHSEtc + "resolv.conf"}, + {Src: container.FHSSys + "block"}, + {Src: container.FHSSys + "bus"}, + {Src: container.FHSSys + "class"}, + {Src: container.FHSSys + "dev"}, + {Src: container.FHSSys + "devices"}, }...) appendGPUFilesystem(config) return config diff --git a/cmd/hpkg/paths.go b/cmd/hpkg/paths.go index 11715fb..b7cc926 100644 --- a/cmd/hpkg/paths.go +++ b/cmd/hpkg/paths.go @@ -8,6 +8,7 @@ import ( "strconv" "sync/atomic" + "hakurei.app/container" "hakurei.app/hst" "hakurei.app/internal/hlog" ) @@ -21,7 +22,7 @@ func init() { if p, ok := os.LookupEnv("HAKUREI_DATA_HOME"); ok { dataHome = p } else { - dataHome = "/var/lib/hakurei/" + strconv.Itoa(os.Getuid()) + dataHome = container.FHSVarLib + "hakurei/" + strconv.Itoa(os.Getuid()) } } diff --git a/cmd/hpkg/with.go b/cmd/hpkg/with.go index 7d9fc7b..444b4e7 100644 --- a/cmd/hpkg/with.go +++ b/cmd/hpkg/with.go @@ -5,6 +5,7 @@ import ( "path" "strings" + "hakurei.app/container" "hakurei.app/container/seccomp" "hakurei.app/hst" "hakurei.app/internal" @@ -52,9 +53,9 @@ func withNixDaemon( {Src: pathSet.nixPath, Dst: "/nix", Write: true, Must: true}, }, Link: [][2]string{ - {app.CurrentSystem, "/run/current-system"}, - {"/run/current-system/sw/bin", "/bin"}, - {"/run/current-system/sw/bin", "/usr/bin"}, + {app.CurrentSystem, container.FHSRun + "current-system"}, + {container.FHSRun + "current-system/sw/bin", "/bin"}, + {container.FHSRun + "current-system/sw/bin", container.FHSUsrBin}, }, Etc: path.Join(pathSet.cacheDir, "etc"), AutoEtc: true, @@ -93,11 +94,11 @@ func withCacheDir( {Src: workDir, Dst: path.Join(hst.Tmp, "bundle"), Must: true}, }, Link: [][2]string{ - {app.CurrentSystem, "/run/current-system"}, - {"/run/current-system/sw/bin", "/bin"}, - {"/run/current-system/sw/bin", "/usr/bin"}, + {app.CurrentSystem, container.FHSRun + "current-system"}, + {container.FHSRun + "current-system/sw/bin", "/bin"}, + {container.FHSRun + "current-system/sw/bin", container.FHSUsrBin}, }, - Etc: path.Join(workDir, "etc"), + Etc: path.Join(workDir, container.FHSEtc), AutoEtc: true, }, }, dropShell, beforeFail) diff --git a/container/autoetc.go b/container/autoetc.go index 103bd35..3e23c16 100644 --- a/container/autoetc.go +++ b/container/autoetc.go @@ -12,7 +12,7 @@ func init() { gob.Register(new(AutoEtcOp)) } // This is not a generic setup op. It is implemented here to reduce ipc overhead. func (f *Ops) Etc(host, prefix string) *Ops { e := &AutoEtcOp{prefix} - f.Mkdir("/etc", 0755) + f.Mkdir(FHSEtc, 0755) f.Bind(host, e.hostPath(), 0) *f = append(*f, e) return f @@ -22,7 +22,7 @@ type AutoEtcOp struct{ Prefix string } func (e *AutoEtcOp) early(*Params) error { return nil } func (e *AutoEtcOp) apply(*Params) error { - const target = sysrootPath + "/etc/" + const target = sysrootPath + FHSEtc rel := e.hostRel() + "/" if err := os.MkdirAll(target, 0755); err != nil { @@ -40,7 +40,7 @@ func (e *AutoEtcOp) apply(*Params) error { case "group": case "mtab": - if err = os.Symlink("/proc/mounts", target+n); err != nil { + if err = os.Symlink(FHSProc+"mounts", target+n); err != nil { return wrapErrSelf(err) } @@ -54,7 +54,7 @@ func (e *AutoEtcOp) apply(*Params) error { return nil } -func (e *AutoEtcOp) hostPath() string { return "/etc/" + e.hostRel() } +func (e *AutoEtcOp) hostPath() string { return FHSEtc + e.hostRel() } func (e *AutoEtcOp) hostRel() string { return ".host/" + e.Prefix } func (e *AutoEtcOp) Is(op Op) bool { diff --git a/container/autoroot.go b/container/autoroot.go index b6375b8..5ed9b6c 100644 --- a/container/autoroot.go +++ b/container/autoroot.go @@ -42,7 +42,7 @@ func (r *AutoRootOp) early(params *Params) error { if IsAutoRootBindable(name) { op := &BindMountOp{ Source: path.Join(r.Host, name), - Target: "/" + name, + Target: FHSRoot + name, Flags: r.Flags, } if err = op.early(params); err != nil { diff --git a/container/container.go b/container/container.go index efbfc21..5a7858f 100644 --- a/container/container.go +++ b/container/container.go @@ -18,10 +18,6 @@ import ( ) const ( - // Nonexistent is a path that cannot exist. - // /proc is chosen because a system with covered /proc is unsupported by this package. - Nonexistent = "/proc/nonexistent" - // CancelSignal is the signal expected by container init on context cancel. // A custom [Container.Cancel] function must eventually deliver this signal. CancelSignal = SIGTERM @@ -142,7 +138,7 @@ func (p *Container) Start() error { } else { p.cmd.Cancel = func() error { return p.cmd.Process.Signal(CancelSignal) } } - p.cmd.Dir = "/" + p.cmd.Dir = FHSRoot p.cmd.SysProcAttr = &SysProcAttr{ Setsid: !p.RetainSession, Pdeathsig: SIGKILL, @@ -251,6 +247,6 @@ func (p *Container) ProcessState() *os.ProcessState { func New(ctx context.Context, name string, args ...string) *Container { return &Container{name: name, ctx: ctx, - Params: Params{Args: append([]string{name}, args...), Dir: "/", Ops: new(Ops)}, + Params: Params{Args: append([]string{name}, args...), Dir: FHSRoot, Ops: new(Ops)}, } } diff --git a/container/init.go b/container/init.go index 8b178d8..6813243 100644 --- a/container/init.go +++ b/container/init.go @@ -31,7 +31,7 @@ const ( it should be noted that none of this should become relevant at any point since the resulting intermediate root tmpfs should be effectively anonymous */ - intermediateHostPath = "/proc/self/fd" + intermediateHostPath = FHSProc + "self/fd" // setup params file descriptor setupEnv = "HAKUREI_SETUP" @@ -88,17 +88,17 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) { if err := SetDumpable(SUID_DUMP_USER); err != nil { log.Fatalf("cannot set SUID_DUMP_USER: %s", err) } - if err := os.WriteFile("/proc/self/uid_map", + if err := os.WriteFile(FHSProc+"self/uid_map", append([]byte{}, strconv.Itoa(params.Uid)+" "+strconv.Itoa(params.HostUid)+" 1\n"...), 0); err != nil { log.Fatalf("%v", err) } - if err := os.WriteFile("/proc/self/setgroups", + if err := os.WriteFile(FHSProc+"self/setgroups", []byte("deny\n"), 0); err != nil && !os.IsNotExist(err) { log.Fatalf("%v", err) } - if err := os.WriteFile("/proc/self/gid_map", + if err := os.WriteFile(FHSProc+"self/gid_map", append([]byte{}, strconv.Itoa(params.Gid)+" "+strconv.Itoa(params.HostGid)+" 1\n"...), 0); err != nil { log.Fatalf("%v", err) @@ -117,7 +117,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) { // cache sysctl before pivot_root LastCap() - if err := Mount("", "/", "", MS_SILENT|MS_SLAVE|MS_REC, ""); err != nil { + if err := Mount("", FHSRoot, "", MS_SILENT|MS_SLAVE|MS_REC, ""); err != nil { log.Fatalf("cannot make / rslave: %v", err) } @@ -159,7 +159,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) { if err := PivotRoot(intermediateHostPath, hostDir); err != nil { log.Fatalf("cannot pivot into intermediate root: %v", err) } - if err := os.Chdir("/"); err != nil { + if err := os.Chdir(FHSRoot); err != nil { log.Fatalf("%v", err) } @@ -189,7 +189,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) { { var fd int if err := IgnoringEINTR(func() (err error) { - fd, err = Open("/", O_DIRECTORY|O_RDONLY, 0) + fd, err = Open(FHSRoot, O_DIRECTORY|O_RDONLY, 0) return }); err != nil { log.Fatalf("cannot open intermediate root: %v", err) @@ -207,7 +207,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) { if err := Unmount(".", MNT_DETACH); err != nil { log.Fatalf("cannot unmount intemediate root: %v", err) } - if err := os.Chdir("/"); err != nil { + if err := os.Chdir(FHSRoot); err != nil { log.Fatalf("%v", err) } diff --git a/container/mount.go b/container/mount.go index 30ef700..919b6e0 100644 --- a/container/mount.go +++ b/container/mount.go @@ -53,7 +53,7 @@ const ( FstypeNULL = zeroString // FstypeProc represents the proc pseudo-filesystem. // A fully visible instance of proc must be available in the mount namespace for proc to be mounted. - // This filesystem type is usually mounted on /proc. + // This filesystem type is usually mounted on [FHSProc]. FstypeProc = "proc" // FstypeDevpts represents the devpts pseudo-filesystem. // This type of filesystem is usually mounted on /dev/pts. diff --git a/container/ops.go b/container/ops.go index e3eb45f..8d9d169 100644 --- a/container/ops.go +++ b/container/ops.go @@ -215,7 +215,7 @@ func (d *MountDevOp) apply(params *Params) error { return err } if err := hostProc.bindMount( - toHost("/dev/"+name), + toHost(FHSDev+name), targetPath, 0, true, @@ -225,15 +225,15 @@ func (d *MountDevOp) apply(params *Params) error { } for i, name := range []string{"stdin", "stdout", "stderr"} { if err := os.Symlink( - "/proc/self/fd/"+string(rune(i+'0')), + FHSProc+"self/fd/"+string(rune(i+'0')), path.Join(target, name), ); err != nil { return wrapErrSelf(err) } } for _, pair := range [][2]string{ - {"/proc/self/fd", "fd"}, - {"/proc/kcore", "core"}, + {FHSProc + "self/fd", "fd"}, + {FHSProc + "kcore", "core"}, {"pts/ptmx", "ptmx"}, } { if err := os.Symlink(pair[0], path.Join(target, pair[1])); err != nil { @@ -436,7 +436,7 @@ func (t *TmpfileOp) apply(params *Params) error { } var tmpPath string - if f, err := os.CreateTemp("/", "tmp.*"); err != nil { + if f, err := os.CreateTemp(FHSRoot, "tmp.*"); err != nil { return wrapErrSelf(err) } else if _, err = f.Write(t.Data); err != nil { return wrapErrSuffix(err, diff --git a/container/path.go b/container/path.go index 632892a..b1126fd 100644 --- a/container/path.go +++ b/container/path.go @@ -14,9 +14,49 @@ import ( ) const ( - hostPath = "/" + hostDir + // FHSRoot points to the file system root. + FHSRoot = "/" + // FHSEtc points to the directory for system-specific configuration. + FHSEtc = "/etc/" + // FHSTmp points to the place for small temporary files. + FHSTmp = "/tmp/" + + // FHSRun points to a "tmpfs" file system for system packages to place runtime data, socket files, and similar. + FHSRun = "/run/" + // FHSRunUser points to a directory containing per-user runtime directories, + // each usually individually mounted "tmpfs" instances. + FHSRunUser = FHSRun + "user/" + + // FHSUsr points to vendor-supplied operating system resources. + FHSUsr = "/usr/" + // FHSUsrBin points to binaries and executables for user commands that shall appear in the $PATH search path. + FHSUsrBin = FHSUsr + "bin/" + + // FHSVar points to persistent, variable system data. Writable during normal system operation. + FHSVar = "/var/" + // FHSVarLib points to persistent system data. + FHSVarLib = FHSVar + "lib/" + // FHSVarEmpty points to a nonstandard directory that is usually empty. + FHSVarEmpty = FHSVar + "empty/" + + // FHSDev points to the root directory for device nodes. + FHSDev = "/dev/" + // FHSProc points to a virtual kernel file system exposing the process list and other functionality. + FHSProc = "/proc/" + // FHSProcSys points to a hierarchy below /proc/ that exposes a number of kernel tunables. + FHSProcSys = FHSProc + "sys/" + // FHSSys points to a virtual kernel file system exposing discovered devices and other functionality. + FHSSys = "/sys/" +) + +const ( + // Nonexistent is a path that cannot exist. + // /proc is chosen because a system with covered /proc is unsupported by this package. + Nonexistent = FHSProc + "nonexistent" + + hostPath = FHSRoot + hostDir hostDir = "host" - sysrootPath = "/" + sysrootDir + sysrootPath = FHSRoot + sysrootDir sysrootDir = "sysroot" ) diff --git a/container/sysctl.go b/container/sysctl.go index 7fcb94b..bfcf278 100644 --- a/container/sysctl.go +++ b/container/sysctl.go @@ -17,9 +17,9 @@ var ( ) const ( - kernelOverflowuidPath = "/proc/sys/kernel/overflowuid" - kernelOverflowgidPath = "/proc/sys/kernel/overflowgid" - kernelCapLastCapPath = "/proc/sys/kernel/cap_last_cap" + kernelOverflowuidPath = FHSProcSys + "kernel/overflowuid" + kernelOverflowgidPath = FHSProcSys + "kernel/overflowgid" + kernelCapLastCapPath = FHSProcSys + "kernel/cap_last_cap" ) func mustReadSysctl() { diff --git a/hst/template.go b/hst/template.go index 5758462..32d84cd 100644 --- a/hst/template.go +++ b/hst/template.go @@ -12,7 +12,7 @@ func Template() *Config { return &Config{ ID: "org.chromium.Chromium", - Path: "/run/current-system/sw/bin/chromium", + Path: container.FHSRun + "current-system/sw/bin/chromium", Args: []string{ "chromium", "--ignore-gpu-blocklist", @@ -46,12 +46,12 @@ func Template() *Config { DirectWayland: false, Username: "chronos", - Shell: "/run/current-system/sw/bin/zsh", - Data: "/var/lib/hakurei/u0/org.chromium.Chromium", + Shell: container.FHSRun + "current-system/sw/bin/zsh", + Data: container.FHSVarLib + "hakurei/u0/org.chromium.Chromium", Dir: "/data/data/org.chromium.Chromium", ExtraPerms: []*ExtraPermConfig{ - {Path: "/var/lib/hakurei/u0", Ensure: true, Execute: true}, - {Path: "/var/lib/hakurei/u0/org.chromium.Chromium", Read: true, Write: true, Execute: true}, + {Path: container.FHSVarLib + "hakurei/u0", Ensure: true, Execute: true}, + {Path: container.FHSVarLib + "hakurei/u0/org.chromium.Chromium", Read: true, Write: true, Execute: true}, }, Identity: 9, @@ -78,19 +78,19 @@ func Template() *Config { "GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT", }, Filesystem: []*FilesystemConfig{ - {Dst: "/tmp", Src: SourceTmpfs, Write: true}, + {Dst: container.FHSTmp, Src: SourceTmpfs, Write: true}, {Src: "/nix/store"}, - {Src: "/run/current-system"}, - {Src: "/run/opengl-driver"}, - {Src: "/var/db/nix-channels"}, - {Src: "/var/lib/hakurei/u0/org.chromium.Chromium", + {Src: container.FHSRun + "current-system"}, + {Src: container.FHSRun + "opengl-driver"}, + {Src: container.FHSVar + "db/nix-channels"}, + {Src: container.FHSVarLib + "hakurei/u0/org.chromium.Chromium", Dst: "/data/data/org.chromium.Chromium", Write: true, Must: true}, - {Src: "/dev/dri", Device: true}, + {Src: container.FHSDev + "dri", Device: true}, }, - Link: [][2]string{{"/run/user/65534", "/run/user/150"}}, - AutoRoot: "/var/lib/hakurei/base/org.debian", + Link: [][2]string{{container.FHSRunUser + "65534", container.FHSRunUser + "150"}}, + AutoRoot: container.FHSVarLib + "hakurei/base/org.debian", RootFlags: container.BindWritable, - Etc: "/etc", + Etc: container.FHSEtc, AutoEtc: true, }, } diff --git a/hst/template_test.go b/hst/template_test.go index 02ec774..cf8ab81 100644 --- a/hst/template_test.go +++ b/hst/template_test.go @@ -98,7 +98,7 @@ func TestTemplate(t *testing.T) { "device": true, "filesystem": [ { - "dst": "/tmp", + "dst": "/tmp/", "src": "tmpfs", "write": true }, @@ -133,7 +133,7 @@ func TestTemplate(t *testing.T) { ], "auto_root": "/var/lib/hakurei/base/org.debian", "root_flags": 2, - "etc": "/etc", + "etc": "/etc/", "auto_etc": true } }` diff --git a/internal/app/app_nixos_linux_test.go b/internal/app/app_nixos_linux_test.go index a0da52c..4b7b3d3 100644 --- a/internal/app/app_nixos_linux_test.go +++ b/internal/app/app_nixos_linux_test.go @@ -23,7 +23,7 @@ var testCasesNixos = []sealTestCase{ 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: "/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}, @@ -116,11 +116,11 @@ var testCasesNixos = []sealTestCase{ "XDG_SESSION_TYPE=tty", }, Ops: new(container.Ops). - Proc("/proc"). + Proc("/proc/"). Tmpfs(hst.Tmp, 4096, 0755). - DevWritable("/dev", true). + DevWritable("/dev/", true). Bind("/bin", "/bin", 0). - Bind("/usr/bin", "/usr/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). @@ -130,11 +130,11 @@ var testCasesNixos = []sealTestCase{ 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). + 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("/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")). diff --git a/internal/app/app_pd_linux_test.go b/internal/app/app_pd_linux_test.go index 31a99f8..2dd83b7 100644 --- a/internal/app/app_pd_linux_test.go +++ b/internal/app/app_pd_linux_test.go @@ -44,18 +44,18 @@ var testCasesPd = []sealTestCase{ }, Ops: new(container.Ops). Root("/", "4a450b6596d7bc15bd01780eb9a607ac", container.BindWritable). - Proc("/proc"). + Proc("/proc/"). Tmpfs(hst.Tmp, 4096, 0755). - DevWritable("/dev", true). + 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). + 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("/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")). @@ -179,19 +179,19 @@ var testCasesPd = []sealTestCase{ }, Ops: new(container.Ops). Root("/", "ebf083d1b175911782d413369b64ce7c", container.BindWritable). - Proc("/proc"). + Proc("/proc/"). Tmpfs(hst.Tmp, 4096, 0755). - DevWritable("/dev", true). + 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). + 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("/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")). diff --git a/internal/app/container_linux.go b/internal/app/container_linux.go index a0c81d7..1ea23d4 100644 --- a/internal/app/container_linux.go +++ b/internal/app/container_linux.go @@ -81,13 +81,13 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid } params. - Proc("/proc"). + Proc(container.FHSProc). Tmpfs(hst.Tmp, 1<<12, 0755) if !s.Device { - params.DevWritable("/dev", true) + params.DevWritable(container.FHSDev, true) } else { - params.Bind("/dev", "/dev", container.BindWritable|container.BindDevice) + params.Bind(container.FHSDev, container.FHSDev, container.BindWritable|container.BindDevice) } /* retrieve paths and hide them if they're made available in the sandbox; @@ -111,7 +111,7 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid if path.IsAbs(pair[1]) { // get parent dir of socket dir := path.Dir(pair[1]) - if dir == "." || dir == "/" { + if dir == "." || dir == container.FHSRoot { os.Printf("dbus socket %q is in an unusual location", pair[1]) } hidePaths = append(hidePaths, dir) @@ -229,19 +229,19 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid if !s.AutoEtc { if s.Etc != "" { - params.Bind(s.Etc, "/etc", 0) + params.Bind(s.Etc, container.FHSEtc, 0) } } else { etcPath := s.Etc if etcPath == "" { - etcPath = "/etc" + etcPath = container.FHSEtc } params.Etc(etcPath, prefix) } // no more ContainerConfig paths beyond this point if !s.Device { - params.Remount("/dev", syscall.MS_RDONLY) + params.Remount(container.FHSDev, 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 516beb4..088deab 100644 --- a/internal/app/process_linux.go +++ b/internal/app/process_linux.go @@ -88,7 +88,7 @@ func (seal *outcome) Run(rs *RunState) error { defer cancel() cmd := exec.CommandContext(ctx, hsuPath) cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr - cmd.Dir = "/" // container init enters final working directory + cmd.Dir = container.FHSRoot // container init enters final working directory // shim runs in the same session as monitor; see shim.go for behaviour cmd.Cancel = func() error { return cmd.Process.Signal(syscall.SIGCONT) } diff --git a/internal/app/seal_linux.go b/internal/app/seal_linux.go index 6bf6c09..5b2b2c0 100644 --- a/internal/app/seal_linux.go +++ b/internal/app/seal_linux.go @@ -242,19 +242,19 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co Tty: true, AutoEtc: true, - AutoRoot: "/", + AutoRoot: container.FHSRoot, RootFlags: container.BindWritable, } // bind GPU stuff if config.Enablements&(system.EX11|system.EWayland) != 0 { - conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Src: "/dev/dri", Device: true}) + conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Src: container.FHSDev + "dri", Device: true}) } // opportunistically bind kvm - conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Src: "/dev/kvm", Device: true}) + conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Src: container.FHSDev + "kvm", Device: true}) // hide nscd from container if present - const nscd = "/var/run/nscd" + 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}) } @@ -290,7 +290,7 @@ 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("/run/user", mapuid.String()) + innerRuntimeDir := path.Join(container.FHSRunUser, mapuid.String()) seal.env[xdgRuntimeDir] = innerRuntimeDir seal.env[xdgSessionClass] = "user" seal.env[xdgSessionType] = "tty" @@ -307,7 +307,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co 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("/run/user", 1<<12, 0755) + seal.container.Tmpfs(container.FHSRunUser, 1<<12, 0755) seal.container.Bind(runtimeDirInst, innerRuntimeDir, container.BindWritable) } @@ -319,11 +319,11 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co seal.sys.Ensure(tmpdirInst, 01700) seal.sys.UpdatePermType(system.User, tmpdirInst, 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, "/tmp", container.BindWritable) + seal.container.Bind(tmpdirInst, container.FHSTmp, container.BindWritable) } { - homeDir := "/var/empty" + homeDir := container.FHSVarEmpty if seal.user.home != "" { homeDir = seal.user.home } @@ -337,9 +337,9 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co seal.env["USER"] = username seal.env[shell] = config.Shell - seal.container.Place("/etc/passwd", + seal.container.Place(container.FHSEtc+"passwd", []byte(username+":x:"+mapuid.String()+":"+mapgid.String()+":Hakurei:"+homeDir+":"+config.Shell+"\n")) - seal.container.Place("/etc/group", + seal.container.Place(container.FHSEtc+"group", []byte("hakurei:x:"+mapgid.String()+":\n")) } @@ -388,7 +388,7 @@ 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("/tmp/.X11-unix", "/tmp/.X11-unix", 0) + seal.container.Bind(container.FHSTmp+".X11-unix", container.FHSTmp+".X11-unix", 0) } } @@ -467,7 +467,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co seal.container.Bind(sessionPath, sessionInner, 0) seal.sys.UpdatePerm(sessionPath, acl.Read, acl.Write) if config.SystemBus != nil { - systemInner := "/run/dbus/system_bus_socket" + systemInner := container.FHSRun + "dbus/system_bus_socket" seal.env[dbusSystemBusAddress] = "unix:path=" + systemInner seal.container.Bind(systemPath, systemInner, 0) seal.sys.UpdatePerm(systemPath, acl.Read, acl.Write) @@ -475,7 +475,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co } // mount root read-only as the final setup Op - seal.container.Remount("/", syscall.MS_RDONLY) + seal.container.Remount(container.FHSRoot, syscall.MS_RDONLY) // append ExtraPerms last for _, p := range config.ExtraPerms { diff --git a/internal/sys/std.go b/internal/sys/std.go index 60baac1..89fdd28 100644 --- a/internal/sys/std.go +++ b/internal/sys/std.go @@ -86,7 +86,7 @@ func (s *Std) Uid(aid int) (int, error) { cmd.Path = hsuPath cmd.Stderr = os.Stderr // pass through fatal messages cmd.Env = []string{"HAKUREI_APP_ID=" + strconv.Itoa(aid)} - cmd.Dir = "/" + cmd.Dir = container.FHSRoot var ( p []byte exitError *exec.ExitError diff --git a/ldd/exec.go b/ldd/exec.go index 538d544..2abec46 100644 --- a/ldd/exec.go +++ b/ldd/exec.go @@ -28,7 +28,7 @@ func Exec(ctx context.Context, p string) ([]*Entry, error) { stdout, stderr := new(bytes.Buffer), new(bytes.Buffer) z.Stdout = stdout z.Stderr = stderr - z.Bind("/", "/", 0).Proc("/proc").Dev("/dev", false) + z.Bind(container.FHSRoot, container.FHSRoot, 0).Proc(container.FHSProc).Dev(container.FHSProc, false) if err := z.Start(); err != nil { return nil, err