Compare commits

..

No commits in common. "f13dca184cf45565b14f4403187627a9031dd28e" and "cb513bb1cd7b098bbe8a60cd105cc764648db410" have entirely different histories.

41 changed files with 231 additions and 663 deletions

View File

@ -73,20 +73,20 @@ jobs:
path: result/* path: result/*
retention-days: 1 retention-days: 1
hpkg: planterette:
name: Hpkg name: Planterette
runs-on: nix runs-on: nix
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Run NixOS test - name: Run NixOS test
run: nix build --out-link "result" --print-out-paths --print-build-logs .#checks.x86_64-linux.hpkg run: nix build --out-link "result" --print-out-paths --print-build-logs .#checks.x86_64-linux.planterette
- name: Upload test output - name: Upload test output
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: "hpkg-vm-output" name: "planterette-vm-output"
path: result/* path: result/*
retention-days: 1 retention-days: 1
@ -97,7 +97,7 @@ jobs:
- race - race
- sandbox - sandbox
- sandbox-race - sandbox-race
- hpkg - planterette
runs-on: nix runs-on: nix
steps: steps:
- name: Checkout - name: Checkout

View File

@ -16,8 +16,7 @@
</p> </p>
Hakurei is a tool for running sandboxed graphical applications as dedicated subordinate users on the Linux kernel. Hakurei is a tool for running sandboxed graphical applications as dedicated subordinate users on the Linux kernel.
It implements the application container of [planterette (WIP)](https://git.gensokyo.uk/security/planterette), It also implements [planterette (WIP)](cmd/planterette), a self-contained Android-like package manager with modern security features.
a self-contained Android-like package manager with modern security features.
## NixOS Module usage ## NixOS Module usage

View File

@ -104,10 +104,6 @@ func printShowInstance(
} }
t.Printf(" Flags:\t%s\n", strings.Join(flags, " ")) t.Printf(" Flags:\t%s\n", strings.Join(flags, " "))
if container.AutoRoot != "" {
t.Printf(" Root:\t%s (%d)\n", container.AutoRoot, container.RootFlags)
}
etc := container.Etc etc := container.Etc
if etc == "" { if etc == "" {
etc = "/etc" etc = "/etc"

View File

@ -42,7 +42,6 @@ func Test_printShowInstance(t *testing.T) {
Data: /var/lib/hakurei/u0/org.chromium.Chromium Data: /var/lib/hakurei/u0/org.chromium.Chromium
Hostname: localhost Hostname: localhost
Flags: userns devel net device tty mapuid autoetc Flags: userns devel net device tty mapuid autoetc
Root: /var/lib/hakurei/base/org.debian (2)
Etc: /etc Etc: /etc
Cover: /var/run/nscd Cover: /var/run/nscd
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
@ -122,7 +121,6 @@ App
Data: /var/lib/hakurei/u0/org.chromium.Chromium Data: /var/lib/hakurei/u0/org.chromium.Chromium
Hostname: localhost Hostname: localhost
Flags: userns devel net device tty mapuid autoetc Flags: userns devel net device tty mapuid autoetc
Root: /var/lib/hakurei/base/org.debian (2)
Etc: /etc Etc: /etc
Cover: /var/run/nscd Cover: /var/run/nscd
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
@ -304,8 +302,6 @@ App
"/run/user/150" "/run/user/150"
] ]
], ],
"auto_root": "/var/lib/hakurei/base/org.debian",
"root_flags": 2,
"etc": "/etc", "etc": "/etc",
"auto_etc": true, "auto_etc": true,
"cover": [ "cover": [
@ -434,8 +430,6 @@ App
"/run/user/150" "/run/user/150"
] ]
], ],
"auto_root": "/var/lib/hakurei/base/org.debian",
"root_flags": 2,
"etc": "/etc", "etc": "/etc",
"auto_etc": true, "auto_etc": true,
"cover": [ "cover": [
@ -618,8 +612,6 @@ func Test_printPs(t *testing.T) {
"/run/user/150" "/run/user/150"
] ]
], ],
"auto_root": "/var/lib/hakurei/base/org.debian",
"root_flags": 2,
"etc": "/etc", "etc": "/etc",
"auto_etc": true, "auto_etc": true,
"cover": [ "cover": [

View File

@ -23,7 +23,7 @@ var (
) )
func init() { func init() {
hlog.Prepare("hpkg") hlog.Prepare("planterette")
if err := os.Setenv("SHELL", shellPath); err != nil { if err := os.Setenv("SHELL", shellPath); err != nil {
log.Fatalf("cannot set $SHELL: %v", err) log.Fatalf("cannot set $SHELL: %v", err)
} }
@ -42,7 +42,7 @@ func main() {
flagVerbose bool flagVerbose bool
flagDropShell bool flagDropShell bool
) )
c := command.New(os.Stderr, log.Printf, "hpkg", func([]string) error { internal.InstallOutput(flagVerbose); return nil }). c := command.New(os.Stderr, log.Printf, "planterette", func([]string) error { internal.InstallOutput(flagVerbose); return nil }).
Flag(&flagVerbose, "v", command.BoolFlag(false), "Print debug messages to the console"). Flag(&flagVerbose, "v", command.BoolFlag(false), "Print debug messages to the console").
Flag(&flagDropShell, "s", command.BoolFlag(false), "Drop to a shell in place of next hakurei action") Flag(&flagDropShell, "s", command.BoolFlag(false), "Drop to a shell in place of next hakurei action")
@ -66,7 +66,7 @@ func main() {
} }
/* /*
Look up paths to programs started by hpkg. Look up paths to programs started by planterette.
This is done here to ease error handling as cleanup is not yet required. This is done here to ease error handling as cleanup is not yet required.
*/ */
@ -82,7 +82,7 @@ func main() {
*/ */
var workDir string var workDir string
if p, err := os.MkdirTemp("", "hpkg.*"); err != nil { if p, err := os.MkdirTemp("", "planterette.*"); err != nil {
log.Printf("cannot create temporary directory: %v", err) log.Printf("cannot create temporary directory: %v", err)
return err return err
} else { } else {

View File

@ -9,7 +9,7 @@ let
buildPackage = self.buildPackage.${system}; buildPackage = self.buildPackage.${system};
in in
nixosTest { nixosTest {
name = "hpkg"; name = "planterette";
nodes.machine = { nodes.machine = {
environment.etc = { environment.etc = {
"foot.pkg".source = callPackage ./foot.nix { inherit buildPackage; }; "foot.pkg".source = callPackage ./foot.nix { inherit buildPackage; };

View File

@ -79,15 +79,15 @@ print(machine.succeed("sudo -u alice -i hakurei version"))
machine.wait_for_file("/run/user/1000/wayland-1") machine.wait_for_file("/run/user/1000/wayland-1")
machine.wait_for_file("/tmp/sway-ipc.sock") machine.wait_for_file("/tmp/sway-ipc.sock")
# Prepare hpkg directory: # Prepare planterette directory:
machine.succeed("install -dm 0700 -o alice -g users /var/lib/hakurei/1000") machine.succeed("install -dm 0700 -o alice -g users /var/lib/hakurei/1000")
# Install hpkg app: # Install planterette app:
swaymsg("exec hpkg -v install /etc/foot.pkg && touch /tmp/hpkg-install-ok") swaymsg("exec planterette -v install /etc/foot.pkg && touch /tmp/planterette-install-ok")
machine.wait_for_file("/tmp/hpkg-install-ok") machine.wait_for_file("/tmp/planterette-install-ok")
# Start app (foot) with Wayland enablement: # Start app (foot) with Wayland enablement:
swaymsg("exec hpkg -v start org.codeberg.dnkl.foot") swaymsg("exec planterette -v start org.codeberg.dnkl.foot")
wait_for_window("hakurei@machine-foot") wait_for_window("hakurei@machine-foot")
machine.send_chars("clear; wayland-info && touch /tmp/success-client\n") machine.send_chars("clear; wayland-info && touch /tmp/success-client\n")
machine.wait_for_file("/tmp/hakurei.1000/tmpdir/2/success-client") machine.wait_for_file("/tmp/hakurei.1000/tmpdir/2/success-client")

View File

@ -1,65 +0,0 @@
package container
import (
"encoding/gob"
"fmt"
"os"
)
func init() { gob.Register(new(AutoEtcOp)) }
// Etc appends an [Op] that expands host /etc into a toplevel symlink mirror with /etc semantics.
// 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.Bind(host, e.hostPath(), 0)
*f = append(*f, e)
return f
}
type AutoEtcOp struct{ Prefix string }
func (e *AutoEtcOp) early(*Params) error { return nil }
func (e *AutoEtcOp) apply(*Params) error {
const target = sysrootPath + "/etc/"
rel := e.hostRel() + "/"
if err := os.MkdirAll(target, 0755); err != nil {
return wrapErrSelf(err)
}
if d, err := os.ReadDir(toSysroot(e.hostPath())); err != nil {
return wrapErrSelf(err)
} else {
for _, ent := range d {
n := ent.Name()
switch n {
case ".host":
case "passwd":
case "group":
case "mtab":
if err = os.Symlink("/proc/mounts", target+n); err != nil {
return wrapErrSelf(err)
}
default:
if err = os.Symlink(rel+n, target+n); err != nil {
return wrapErrSelf(err)
}
}
}
}
return nil
}
func (e *AutoEtcOp) hostPath() string { return "/etc/" + e.hostRel() }
func (e *AutoEtcOp) hostRel() string { return ".host/" + e.Prefix }
func (e *AutoEtcOp) Is(op Op) bool {
ve, ok := op.(*AutoEtcOp)
return ok && ((e == nil && ve == nil) || (e != nil && ve != nil && *e == *ve))
}
func (*AutoEtcOp) prefix() string { return "setting up" }
func (e *AutoEtcOp) String() string { return fmt.Sprintf("auto etc %s", e.Prefix) }

View File

@ -1,91 +0,0 @@
package container
import (
"encoding/gob"
"fmt"
"os"
"path"
. "syscall"
)
func init() { gob.Register(new(AutoRootOp)) }
// Root appends an [Op] that expands a directory into a toplevel bind mount mirror on container root.
// This is not a generic setup op. It is implemented here to reduce ipc overhead.
func (f *Ops) Root(host, prefix string, flags int) *Ops {
*f = append(*f, &AutoRootOp{host, prefix, flags, nil})
return f
}
type AutoRootOp struct {
Host, Prefix string
// passed through to bindMount
Flags int
// obtained during early;
// these wrap the underlying Op because BindMountOp is relatively complex,
// so duplicating that code would be unwise
resolved []Op
}
func (r *AutoRootOp) early(params *Params) error {
if !path.IsAbs(r.Host) {
return msg.WrapErr(EBADE, fmt.Sprintf("path %q is not absolute", r.Host))
}
if d, err := os.ReadDir(r.Host); err != nil {
return wrapErrSelf(err)
} else {
r.resolved = make([]Op, 0, len(d))
for _, ent := range d {
name := ent.Name()
if IsAutoRootBindable(name) {
op := &BindMountOp{
Source: path.Join(r.Host, name),
Target: "/" + name,
Flags: r.Flags,
}
if err = op.early(params); err != nil {
return err
}
r.resolved = append(r.resolved, op)
}
}
return nil
}
}
func (r *AutoRootOp) apply(params *Params) error {
for _, op := range r.resolved {
msg.Verbosef("%s %s", op.prefix(), op)
if err := op.apply(params); err != nil {
return err
}
}
return nil
}
func (r *AutoRootOp) Is(op Op) bool {
vr, ok := op.(*AutoRootOp)
return ok && ((r == nil && vr == nil) || (r != nil && vr != nil &&
r.Host == vr.Host && r.Prefix == vr.Prefix && r.Flags == vr.Flags))
}
func (*AutoRootOp) prefix() string { return "setting up" }
func (r *AutoRootOp) String() string {
return fmt.Sprintf("auto root %q prefix %s flags %#x", r.Host, r.Prefix, r.Flags)
}
// IsAutoRootBindable returns whether a dir entry name is selected for AutoRoot.
func IsAutoRootBindable(name string) bool {
switch name {
case "proc":
case "dev":
case "tmp":
case "mnt":
case "etc":
default:
return true
}
return false
}

View File

@ -28,9 +28,7 @@ const (
ignore = "\x00" ignore = "\x00"
ignoreV = -1 ignoreV = -1
pathPrefix = "/etc/hakurei/" pathWantMnt = "/etc/hakurei/want-mnt"
pathWantMnt = pathPrefix + "want-mnt"
pathReadonly = pathPrefix + "readonly"
) )
var containerTestCases = []struct { var containerTestCases = []struct {
@ -38,7 +36,6 @@ var containerTestCases = []struct {
filter bool filter bool
session bool session bool
net bool net bool
ro bool
ops *container.Ops ops *container.Ops
mnt []*vfs.MountInfoEntry mnt []*vfs.MountInfoEntry
@ -49,26 +46,26 @@ var containerTestCases = []struct {
flags seccomp.ExportFlag flags seccomp.ExportFlag
presets seccomp.FilterPreset presets seccomp.FilterPreset
}{ }{
{"minimal", true, false, false, true, {"minimal", true, false, false,
new(container.Ops), nil, new(container.Ops), nil,
1000, 100, nil, 0, seccomp.PresetStrict}, 1000, 100, nil, 0, seccomp.PresetStrict},
{"allow", true, true, true, false, {"allow", true, true, true,
new(container.Ops), nil, new(container.Ops), nil,
1000, 100, nil, 0, seccomp.PresetExt | seccomp.PresetDenyDevel}, 1000, 100, nil, 0, seccomp.PresetExt | seccomp.PresetDenyDevel},
{"no filter", false, true, true, true, {"no filter", false, true, true,
new(container.Ops), nil, new(container.Ops), nil,
1000, 100, nil, 0, seccomp.PresetExt}, 1000, 100, nil, 0, seccomp.PresetExt},
{"custom rules", true, true, true, false, {"custom rules", true, true, true,
new(container.Ops), nil, new(container.Ops), nil,
1, 31, []seccomp.NativeRule{{seccomp.ScmpSyscall(syscall.SYS_SETUID), seccomp.ScmpErrno(syscall.EPERM), nil}}, 0, seccomp.PresetExt}, 1, 31, []seccomp.NativeRule{{seccomp.ScmpSyscall(syscall.SYS_SETUID), seccomp.ScmpErrno(syscall.EPERM), nil}}, 0, seccomp.PresetExt},
{"tmpfs", true, false, false, true, {"tmpfs", true, false, false,
new(container.Ops). new(container.Ops).
Tmpfs(hst.Tmp, 0, 0755), Tmpfs(hst.Tmp, 0, 0755),
[]*vfs.MountInfoEntry{ []*vfs.MountInfoEntry{
ent("/", hst.Tmp, "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", ignore), ent("/", hst.Tmp, "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
}, },
9, 9, nil, 0, seccomp.PresetStrict}, 9, 9, nil, 0, seccomp.PresetStrict},
{"dev", true, true /* go test output is not a tty */, false, false, {"dev", true, true /* go test output is not a tty */, false,
new(container.Ops). new(container.Ops).
Dev("/dev"). Dev("/dev").
Mqueue("/dev/mqueue"), Mqueue("/dev/mqueue"),
@ -143,7 +140,6 @@ func TestContainer(t *testing.T) {
c.HostNet = tc.net c.HostNet = tc.net
c. c.
Readonly(pathReadonly, 0755).
Tmpfs("/tmp", 0, 0755). Tmpfs("/tmp", 0, 0755).
Place("/etc/hostname", []byte(c.Hostname)) Place("/etc/hostname", []byte(c.Hostname))
// needs /proc to check mountinfo // needs /proc to check mountinfo
@ -162,10 +158,8 @@ func TestContainer(t *testing.T) {
} }
mnt = append(mnt, tc.mnt...) mnt = append(mnt, tc.mnt...)
mnt = append(mnt, mnt = append(mnt,
// Readonly(pathReadonly, 0755)
ent("/", pathReadonly, "ro,nosuid,nodev", "tmpfs", "readonly", ignore),
// Tmpfs("/tmp", 0, 0755) // Tmpfs("/tmp", 0, 0755)
ent("/", "/tmp", "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", ignore), ent("/", "/tmp", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
// Place("/etc/hostname", []byte(hostname)) // Place("/etc/hostname", []byte(hostname))
ent(ignore, "/etc/hostname", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", ignore), ent(ignore, "/etc/hostname", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", ignore),
// Proc("/proc") // Proc("/proc")
@ -179,10 +173,6 @@ func TestContainer(t *testing.T) {
} }
c.Place(pathWantMnt, want.Bytes()) c.Place(pathWantMnt, want.Bytes())
if tc.ro {
c.Remount("/", syscall.MS_RDONLY)
}
if err := c.Start(); err != nil { if err := c.Start(); err != nil {
hlog.PrintBaseError(err, "start:") hlog.PrintBaseError(err, "start:")
t.Fatalf("cannot start container: %v", err) t.Fatalf("cannot start container: %v", err)
@ -319,10 +309,6 @@ func init() {
return fmt.Errorf("/etc/hostname: %q, want %q", string(p), wantHost) return fmt.Errorf("/etc/hostname: %q, want %q", string(p), wantHost)
} }
if _, err := os.Create(pathReadonly + "/nonexistent"); !errors.Is(err, syscall.EROFS) {
return err
}
{ {
var fail bool var fail bool
@ -335,11 +321,6 @@ func init() {
return fmt.Errorf("cannot close expected mount points: %v", err) return fmt.Errorf("cannot close expected mount points: %v", err)
} }
if tc.ro && len(mnt) > 0 {
// Remount("/", syscall.MS_RDONLY)
mnt[0].VfsOptstr = "ro,nosuid,nodev"
}
var d *vfs.MountInfoDecoder var d *vfs.MountInfoDecoder
if f, err := os.Open("/proc/self/mountinfo"); err != nil { if f, err := os.Open("/proc/self/mountinfo"); err != nil {
return fmt.Errorf("cannot open mountinfo: %v", err) return fmt.Errorf("cannot open mountinfo: %v", err)

View File

@ -121,10 +121,6 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
log.Fatalf("cannot make / rslave: %v", err) log.Fatalf("cannot make / rslave: %v", err)
} }
/* early is called right before pivot_root into intermediate root;
this step is mostly for gathering information that would otherwise be difficult to obtain
via library functions after pivot_root, and implementations are expected to avoid changing
the state of the mount namespace */
for i, op := range *params.Ops { for i, op := range *params.Ops {
if op == nil { if op == nil {
log.Fatalf("invalid op %d", i) log.Fatalf("invalid op %d", i)
@ -163,10 +159,6 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
log.Fatalf("%v", err) log.Fatalf("%v", err)
} }
/* apply is called right after pivot_root and entering the new root;
this step sets up the container filesystem, and implementations are expected to keep the host root
and sysroot mount points intact but otherwise can do whatever they need to;
chdir is allowed but discouraged */
for i, op := range *params.Ops { for i, op := range *params.Ops {
// ops already checked during early setup // ops already checked during early setup
msg.Verbosef("%s %s", op.prefix(), op) msg.Verbosef("%s %s", op.prefix(), op)

View File

@ -10,7 +10,6 @@ import (
"hakurei.app/container/vfs" "hakurei.app/container/vfs"
) )
// bindMount mounts source on target and recursively applies flags if MS_REC is set.
func (p *procPaths) bindMount(source, target string, flags uintptr, eq bool) error { func (p *procPaths) bindMount(source, target string, flags uintptr, eq bool) error {
if eq { if eq {
msg.Verbosef("resolved %q flags %#x", target, flags) msg.Verbosef("resolved %q flags %#x", target, flags)
@ -23,11 +22,6 @@ func (p *procPaths) bindMount(source, target string, flags uintptr, eq bool) err
fmt.Sprintf("cannot mount %q on %q:", source, target)) fmt.Sprintf("cannot mount %q on %q:", source, target))
} }
return p.remount(target, flags)
}
// remount applies flags on target, recursively if MS_REC is set.
func (p *procPaths) remount(target string, flags uintptr) error {
var targetFinal string var targetFinal string
if v, err := filepath.EvalSymlinks(target); err != nil { if v, err := filepath.EvalSymlinks(target); err != nil {
return wrapErrSelf(err) return wrapErrSelf(err)
@ -89,7 +83,6 @@ func (p *procPaths) remount(target string, flags uintptr) error {
}) })
} }
// remountWithFlags remounts mount point described by [vfs.MountInfoNode].
func remountWithFlags(n *vfs.MountInfoNode, mf uintptr) error { func remountWithFlags(n *vfs.MountInfoNode, mf uintptr) error {
kf, unmatched := n.Flags() kf, unmatched := n.Flags()
if len(unmatched) != 0 { if len(unmatched) != 0 {
@ -104,7 +97,7 @@ func remountWithFlags(n *vfs.MountInfoNode, mf uintptr) error {
return nil return nil
} }
func mountTmpfs(fsname, name string, flags uintptr, size int, perm os.FileMode) error { func mountTmpfs(fsname, name string, size int, perm os.FileMode) error {
target := toSysroot(name) target := toSysroot(name)
if err := os.MkdirAll(target, parentPerm(perm)); err != nil { if err := os.MkdirAll(target, parentPerm(perm)); err != nil {
return wrapErrSelf(err) return wrapErrSelf(err)
@ -114,7 +107,7 @@ func mountTmpfs(fsname, name string, flags uintptr, size int, perm os.FileMode)
opt += fmt.Sprintf(",size=%d", size) opt += fmt.Sprintf(",size=%d", size)
} }
return wrapErrSuffix( return wrapErrSuffix(
Mount(fsname, target, "tmpfs", flags, opt), Mount(fsname, target, "tmpfs", MS_NOSUID|MS_NODEV, opt),
fmt.Sprintf("cannot mount tmpfs on %q:", name)) fmt.Sprintf("cannot mount tmpfs on %q:", name))
} }

View File

@ -33,32 +33,6 @@ type (
// Grow grows the slice Ops points to using [slices.Grow]. // Grow grows the slice Ops points to using [slices.Grow].
func (f *Ops) Grow(n int) { *f = slices.Grow(*f, n) } func (f *Ops) Grow(n int) { *f = slices.Grow(*f, n) }
func init() { gob.Register(new(RemountOp)) }
// Remount appends an [Op] that applies [RemountOp.Flags] on container path [RemountOp.Target].
func (f *Ops) Remount(target string, flags uintptr) *Ops {
*f = append(*f, &RemountOp{target, flags})
return f
}
type RemountOp struct {
Target string
Flags uintptr
}
func (*RemountOp) early(*Params) error { return nil }
func (r *RemountOp) apply(*Params) error {
if !path.IsAbs(r.Target) {
return msg.WrapErr(EBADE, fmt.Sprintf("path %q is not absolute", r.Target))
}
return wrapErrSuffix(hostProc.remount(toSysroot(r.Target), r.Flags),
fmt.Sprintf("cannot remount %q:", r.Target))
}
func (r *RemountOp) Is(op Op) bool { vr, ok := op.(*RemountOp); return ok && *r == *vr }
func (*RemountOp) prefix() string { return "remounting" }
func (r *RemountOp) String() string { return fmt.Sprintf("%q flags %#x", r.Target, r.Flags) }
func init() { gob.Register(new(BindMountOp)) } func init() { gob.Register(new(BindMountOp)) }
// Bind appends an [Op] that bind mounts host path [BindMountOp.Source] on container path [BindMountOp.Target]. // Bind appends an [Op] that bind mounts host path [BindMountOp.Source] on container path [BindMountOp.Target].
@ -144,7 +118,7 @@ func (b *BindMountOp) String() string {
if b.Source == b.Target { if b.Source == b.Target {
return fmt.Sprintf("%q flags %#x", b.Source, b.Flags) return fmt.Sprintf("%q flags %#x", b.Source, b.Flags)
} }
return fmt.Sprintf("%q on %q flags %#x", b.Source, b.Target, b.Flags) return fmt.Sprintf("%q on %q flags %#x", b.Source, b.Target, b.Flags&BindWritable)
} }
func init() { gob.Register(new(MountProcOp)) } func init() { gob.Register(new(MountProcOp)) }
@ -196,7 +170,7 @@ func (d MountDevOp) apply(params *Params) error {
} }
target := toSysroot(v) target := toSysroot(v)
if err := mountTmpfs("devtmpfs", v, MS_NOSUID|MS_NODEV, 0, params.ParentPerm); err != nil { if err := mountTmpfs("devtmpfs", v, 0, params.ParentPerm); err != nil {
return err return err
} }
@ -306,20 +280,12 @@ func init() { gob.Register(new(MountTmpfsOp)) }
// Tmpfs appends an [Op] that mounts tmpfs on container path [MountTmpfsOp.Path]. // Tmpfs appends an [Op] that mounts tmpfs on container path [MountTmpfsOp.Path].
func (f *Ops) Tmpfs(dest string, size int, perm os.FileMode) *Ops { func (f *Ops) Tmpfs(dest string, size int, perm os.FileMode) *Ops {
*f = append(*f, &MountTmpfsOp{"ephemeral", dest, MS_NOSUID | MS_NODEV, size, perm}) *f = append(*f, &MountTmpfsOp{dest, size, perm})
return f
}
// Readonly appends an [Op] that mounts read-only tmpfs on container path [MountTmpfsOp.Path].
func (f *Ops) Readonly(dest string, perm os.FileMode) *Ops {
*f = append(*f, &MountTmpfsOp{"readonly", dest, MS_RDONLY | MS_NOSUID | MS_NODEV, 0, perm})
return f return f
} }
type MountTmpfsOp struct { type MountTmpfsOp struct {
FSName string
Path string Path string
Flags uintptr
Size int Size int
Perm os.FileMode Perm os.FileMode
} }
@ -332,7 +298,7 @@ func (t *MountTmpfsOp) apply(*Params) error {
if t.Size < 0 || t.Size > math.MaxUint>>1 { if t.Size < 0 || t.Size > math.MaxUint>>1 {
return msg.WrapErr(EBADE, fmt.Sprintf("size %d out of bounds", t.Size)) return msg.WrapErr(EBADE, fmt.Sprintf("size %d out of bounds", t.Size))
} }
return mountTmpfs(t.FSName, t.Path, t.Flags, t.Size, t.Perm) return mountTmpfs("tmpfs", t.Path, t.Size, t.Perm)
} }
func (t *MountTmpfsOp) Is(op Op) bool { vt, ok := op.(*MountTmpfsOp); return ok && *t == *vt } func (t *MountTmpfsOp) Is(op Op) bool { vt, ok := op.(*MountTmpfsOp); return ok && *t == *vt }
@ -474,3 +440,61 @@ func (*TmpfileOp) prefix() string { return "placing" }
func (t *TmpfileOp) String() string { func (t *TmpfileOp) String() string {
return fmt.Sprintf("tmpfile %q (%d bytes)", t.Path, len(t.Data)) return fmt.Sprintf("tmpfile %q (%d bytes)", t.Path, len(t.Data))
} }
func init() { gob.Register(new(AutoEtcOp)) }
// Etc appends an [Op] that expands host /etc into a toplevel symlink mirror with /etc semantics.
// 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.Bind(host, e.hostPath(), 0)
*f = append(*f, e)
return f
}
type AutoEtcOp struct{ Prefix string }
func (e *AutoEtcOp) early(*Params) error { return nil }
func (e *AutoEtcOp) apply(*Params) error {
const target = sysrootPath + "/etc/"
rel := e.hostRel() + "/"
if err := os.MkdirAll(target, 0755); err != nil {
return wrapErrSelf(err)
}
if d, err := os.ReadDir(toSysroot(e.hostPath())); err != nil {
return wrapErrSelf(err)
} else {
for _, ent := range d {
n := ent.Name()
switch n {
case ".host":
case "passwd":
case "group":
case "mtab":
if err = os.Symlink("/proc/mounts", target+n); err != nil {
return wrapErrSelf(err)
}
default:
if err = os.Symlink(rel+n, target+n); err != nil {
return wrapErrSelf(err)
}
}
}
}
return nil
}
func (e *AutoEtcOp) hostPath() string { return "/etc/" + e.hostRel() }
func (e *AutoEtcOp) hostRel() string { return ".host/" + e.Prefix }
func (e *AutoEtcOp) Is(op Op) bool {
ve, ok := op.(*AutoEtcOp)
return ok && ((e == nil && ve == nil) || (e != nil && ve != nil && *e == *ve))
}
func (*AutoEtcOp) prefix() string { return "setting up" }
func (e *AutoEtcOp) String() string { return fmt.Sprintf("auto etc %s", e.Prefix) }

2
dist/install.sh vendored
View File

@ -2,7 +2,7 @@
cd "$(dirname -- "$0")" || exit 1 cd "$(dirname -- "$0")" || exit 1
install -vDm0755 "bin/hakurei" "${HAKUREI_INSTALL_PREFIX}/usr/bin/hakurei" install -vDm0755 "bin/hakurei" "${HAKUREI_INSTALL_PREFIX}/usr/bin/hakurei"
install -vDm0755 "bin/hpkg" "${HAKUREI_INSTALL_PREFIX}/usr/bin/hpkg" install -vDm0755 "bin/planterette" "${HAKUREI_INSTALL_PREFIX}/usr/bin/planterette"
install -vDm6511 "bin/hsu" "${HAKUREI_INSTALL_PREFIX}/usr/bin/hsu" install -vDm6511 "bin/hsu" "${HAKUREI_INSTALL_PREFIX}/usr/bin/hsu"
if [ ! -f "${HAKUREI_INSTALL_PREFIX}/etc/hsurc" ]; then if [ ! -f "${HAKUREI_INSTALL_PREFIX}/etc/hsurc" ]; then

View File

@ -32,7 +32,7 @@
buildPackage = forAllSystems ( buildPackage = forAllSystems (
system: system:
nixpkgsFor.${system}.callPackage ( nixpkgsFor.${system}.callPackage (
import ./cmd/hpkg/build.nix { import ./cmd/planterette/build.nix {
inherit inherit
nixpkgsFor nixpkgsFor
system system
@ -69,7 +69,7 @@
withRace = true; withRace = true;
}; };
hpkg = callPackage ./cmd/hpkg/test { inherit system self; }; planterette = callPackage ./cmd/planterette/test { inherit system self; };
formatting = runCommandLocal "check-formatting" { nativeBuildInputs = [ nixfmt-rfc-style ]; } '' formatting = runCommandLocal "check-formatting" { nativeBuildInputs = [ nixfmt-rfc-style ]; } ''
cd ${./.} cd ${./.}
@ -125,7 +125,7 @@
glibc glibc
xdg-dbus-proxy xdg-dbus-proxy
# hpkg # planterette
zstd zstd
gnutar gnutar
coreutils coreutils

View File

@ -6,20 +6,6 @@ import (
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
) )
const (
// SourceTmpfs causes tmpfs to be mounted on [FilesystemConfig.Dst]
// when assigned to [FilesystemConfig.Src].
SourceTmpfs = "tmpfs"
// TmpfsPerm is the permission bits for tmpfs mount points
// configured through [FilesystemConfig].
TmpfsPerm = 0755
// TmpfsSize is the size for tmpfs mount points
// configured through [FilesystemConfig].
TmpfsSize = 0
)
type ( type (
// ContainerConfig describes the container configuration baseline to which the app implementation adds upon. // ContainerConfig describes the container configuration baseline to which the app implementation adds upon.
ContainerConfig struct { ContainerConfig struct {
@ -59,17 +45,10 @@ type (
// create symlinks inside container filesystem // create symlinks inside container filesystem
Link [][2]string `json:"symlink"` Link [][2]string `json:"symlink"`
// automatically bind mount top-level directories to container root;
// the zero value disables this behaviour
AutoRoot string `json:"auto_root,omitempty"`
// extra flags for AutoRoot
RootFlags int `json:"root_flags,omitempty"`
// read-only /etc directory // read-only /etc directory
Etc string `json:"etc,omitempty"` Etc string `json:"etc,omitempty"`
// automatically set up /etc symlinks // automatically set up /etc symlinks
AutoEtc bool `json:"auto_etc"` AutoEtc bool `json:"auto_etc"`
// cover these paths or create them if they do not already exist // cover these paths or create them if they do not already exist
Cover []string `json:"cover"` Cover []string `json:"cover"`
} }

View File

@ -1,7 +1,6 @@
package hst package hst
import ( import (
"hakurei.app/container"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/system" "hakurei.app/system"
"hakurei.app/system/dbus" "hakurei.app/system/dbus"
@ -87,8 +86,6 @@ func Template() *Config {
{Src: "/dev/dri", Device: true}, {Src: "/dev/dri", Device: true},
}, },
Link: [][2]string{{"/run/user/65534", "/run/user/150"}}, Link: [][2]string{{"/run/user/65534", "/run/user/150"}},
AutoRoot: "/var/lib/hakurei/base/org.debian",
RootFlags: container.BindWritable,
Etc: "/etc", Etc: "/etc",
AutoEtc: true, AutoEtc: true,
Cover: []string{"/var/run/nscd"}, Cover: []string{"/var/run/nscd"},

View File

@ -126,8 +126,6 @@ func TestTemplate(t *testing.T) {
"/run/user/150" "/run/user/150"
] ]
], ],
"auto_root": "/var/lib/hakurei/base/org.debian",
"root_flags": 2,
"etc": "/etc", "etc": "/etc",
"auto_etc": true, "auto_etc": true,
"cover": [ "cover": [

View File

@ -1,8 +1,6 @@
package app_test package app_test
import ( import (
"syscall"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/hst" "hakurei.app/hst"
@ -143,8 +141,7 @@ var testCasesNixos = []sealTestCase{
Place(hst.Tmp+"/pulse-cookie", nil). Place(hst.Tmp+"/pulse-cookie", nil).
Bind("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", "/run/user/1971/bus", 0). 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). Bind("/tmp/hakurei.1971/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", "/run/dbus/system_bus_socket", 0).
Tmpfs("/var/run/nscd", 8192, 0755). Tmpfs("/var/run/nscd", 8192, 0755),
Remount("/", syscall.MS_RDONLY),
SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyTTY | seccomp.PresetDenyDevel, SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyTTY | seccomp.PresetDenyDevel,
HostNet: true, HostNet: true,
ForwardCancel: true, ForwardCancel: true,

View File

@ -2,7 +2,6 @@ package app_test
import ( import (
"os" "os"
"syscall"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
@ -43,12 +42,22 @@ var testCasesPd = []sealTestCase{
"XDG_SESSION_TYPE=tty", "XDG_SESSION_TYPE=tty",
}, },
Ops: new(container.Ops). Ops: new(container.Ops).
Root("/", "4a450b6596d7bc15bd01780eb9a607ac", container.BindWritable).
Proc("/proc"). Proc("/proc").
Tmpfs(hst.Tmp, 4096, 0755). Tmpfs(hst.Tmp, 4096, 0755).
Dev("/dev").Mqueue("/dev/mqueue"). Dev("/dev").Mqueue("/dev/mqueue").
Bind("/bin", "/bin", container.BindWritable).
Bind("/boot", "/boot", container.BindWritable).
Bind("/home", "/home", container.BindWritable).
Bind("/lib", "/lib", container.BindWritable).
Bind("/lib64", "/lib64", container.BindWritable).
Bind("/nix", "/nix", container.BindWritable).
Bind("/root", "/root", container.BindWritable).
Bind("/run", "/run", container.BindWritable).
Bind("/srv", "/srv", container.BindWritable).
Bind("/sys", "/sys", container.BindWritable).
Bind("/usr", "/usr", container.BindWritable).
Bind("/var", "/var", container.BindWritable).
Bind("/dev/kvm", "/dev/kvm", 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/user/1971", 8192, 0755).
Tmpfs("/run/dbus", 8192, 0755). Tmpfs("/run/dbus", 8192, 0755).
Etc("/etc", "4a450b6596d7bc15bd01780eb9a607ac"). Etc("/etc", "4a450b6596d7bc15bd01780eb9a607ac").
@ -58,7 +67,7 @@ var testCasesPd = []sealTestCase{
Bind("/home/chronos", "/home/chronos", 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/passwd", []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")).
Place("/etc/group", []byte("hakurei:x:65534:\n")). Place("/etc/group", []byte("hakurei:x:65534:\n")).
Remount("/", syscall.MS_RDONLY), Tmpfs("/var/run/nscd", 8192, 0755),
SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyDevel, SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyDevel,
HostNet: true, HostNet: true,
RetainSession: true, RetainSession: true,
@ -177,13 +186,23 @@ var testCasesPd = []sealTestCase{
"XDG_SESSION_TYPE=tty", "XDG_SESSION_TYPE=tty",
}, },
Ops: new(container.Ops). Ops: new(container.Ops).
Root("/", "ebf083d1b175911782d413369b64ce7c", container.BindWritable).
Proc("/proc"). Proc("/proc").
Tmpfs(hst.Tmp, 4096, 0755). Tmpfs(hst.Tmp, 4096, 0755).
Dev("/dev").Mqueue("/dev/mqueue"). Dev("/dev").Mqueue("/dev/mqueue").
Bind("/bin", "/bin", container.BindWritable).
Bind("/boot", "/boot", container.BindWritable).
Bind("/home", "/home", container.BindWritable).
Bind("/lib", "/lib", container.BindWritable).
Bind("/lib64", "/lib64", container.BindWritable).
Bind("/nix", "/nix", container.BindWritable).
Bind("/root", "/root", container.BindWritable).
Bind("/run", "/run", container.BindWritable).
Bind("/srv", "/srv", container.BindWritable).
Bind("/sys", "/sys", container.BindWritable).
Bind("/usr", "/usr", container.BindWritable).
Bind("/var", "/var", container.BindWritable).
Bind("/dev/dri", "/dev/dri", container.BindWritable|container.BindDevice|container.BindOptional). Bind("/dev/dri", "/dev/dri", container.BindWritable|container.BindDevice|container.BindOptional).
Bind("/dev/kvm", "/dev/kvm", 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/user/1971", 8192, 0755).
Tmpfs("/run/dbus", 8192, 0755). Tmpfs("/run/dbus", 8192, 0755).
Etc("/etc", "ebf083d1b175911782d413369b64ce7c"). Etc("/etc", "ebf083d1b175911782d413369b64ce7c").
@ -198,7 +217,7 @@ var testCasesPd = []sealTestCase{
Place(hst.Tmp+"/pulse-cookie", nil). Place(hst.Tmp+"/pulse-cookie", nil).
Bind("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/bus", "/run/user/65534/bus", 0). 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). Bind("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket", "/run/dbus/system_bus_socket", 0).
Remount("/", syscall.MS_RDONLY), Tmpfs("/var/run/nscd", 8192, 0755),
SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyDevel, SeccompPresets: seccomp.PresetExt | seccomp.PresetDenyDevel,
HostNet: true, HostNet: true,
RetainSession: true, RetainSession: true,

View File

@ -6,7 +6,6 @@ import (
"io/fs" "io/fs"
"maps" "maps"
"path" "path"
"slices"
"syscall" "syscall"
"hakurei.app/container" "hakurei.app/container"
@ -22,7 +21,7 @@ const preallocateOpsCount = 1 << 5
// newContainer initialises [container.Params] via [hst.ContainerConfig]. // newContainer initialises [container.Params] via [hst.ContainerConfig].
// Note that remaining container setup must be queued by the caller. // 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) { func newContainer(s *hst.ContainerConfig, os sys.State, uid, gid *int) (*container.Params, map[string]string, error) {
if s == nil { if s == nil {
return nil, nil, syscall.EBADE return nil, nil, syscall.EBADE
} }
@ -73,13 +72,6 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid
*gid = container.OverflowGid() *gid = container.OverflowGid()
} }
if s.AutoRoot != "" {
if !path.IsAbs(s.AutoRoot) {
return nil, nil, fmt.Errorf("auto root target %q not absolute", s.AutoRoot)
}
params.Root(s.AutoRoot, prefix, s.RootFlags)
}
params. params.
Proc("/proc"). Proc("/proc").
Tmpfs(hst.Tmp, 1<<12, 0755) Tmpfs(hst.Tmp, 1<<12, 0755)
@ -128,48 +120,12 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid
return nil, nil, err return nil, nil, err
} }
} }
// evaluated path, input path
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 {
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)
srcP := [2]string{name, name}
if err = evalSymlinks(os, &srcP[0]); err != nil {
return nil, nil, err
}
hidePathSource = append(hidePathSource, srcP)
}
}
}
}
for _, c := range s.Filesystem { for _, c := range s.Filesystem {
if c == nil { if c == nil {
continue continue
} }
// 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)
}
if c.Write {
params.Tmpfs(c.Dst, hst.TmpfsSize, hst.TmpfsPerm)
} else {
params.Readonly(c.Dst, hst.TmpfsPerm)
}
continue
}
if !path.IsAbs(c.Src) { if !path.IsAbs(c.Src) {
return nil, nil, fmt.Errorf("src path %q is not absolute", c.Src) return nil, nil, fmt.Errorf("src path %q is not absolute", c.Src)
} }
@ -181,11 +137,24 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid
return nil, nil, fmt.Errorf("dst path %q is not absolute", dest) return nil, nil, fmt.Errorf("dst path %q is not absolute", dest)
} }
p := [2]string{c.Src, c.Src} srcH := c.Src
if err := evalSymlinks(os, &p[0]); err != nil { if err := evalSymlinks(os, &srcH); err != nil {
return nil, nil, err return nil, nil, err
} }
hidePathSource = append(hidePathSource, p)
for i := range hidePaths {
// skip matched entries
if hidePathMatch[i] {
continue
}
if ok, err := deepContainsH(srcH, hidePaths[i]); err != nil {
return nil, nil, err
} else if ok {
hidePathMatch[i] = true
os.Printf("hiding paths from %q", c.Src)
}
}
var flags int var flags int
if c.Write { if c.Write {
@ -200,22 +169,6 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid
params.Bind(c.Src, dest, flags) params.Bind(c.Src, dest, flags)
} }
for _, p := range hidePathSource {
for i := range hidePaths {
// skip matched entries
if hidePathMatch[i] {
continue
}
if ok, err := deepContainsH(p[0], hidePaths[i]); err != nil {
return nil, nil, err
} else if ok {
hidePathMatch[i] = true
os.Printf("hiding path %q from %q", hidePaths[i], p[1])
}
}
}
// cover matched paths // cover matched paths
for i, ok := range hidePathMatch { for i, ok := range hidePathMatch {
if ok { if ok {
@ -227,18 +180,6 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid
params.Link(l[0], l[1]) params.Link(l[0], l[1])
} }
if !s.AutoEtc {
if s.Etc != "" {
params.Bind(s.Etc, "/etc", 0)
}
} else {
etcPath := s.Etc
if etcPath == "" {
etcPath = "/etc"
}
params.Etc(etcPath, prefix)
}
return params, maps.Clone(s.Env), nil return params, maps.Clone(s.Env), nil
} }

View File

@ -241,11 +241,33 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
Net: true, Net: true,
Tty: true, Tty: true,
AutoEtc: true, AutoEtc: true,
}
// bind entries in /
if d, err := sys.ReadDir("/"); err != nil {
return err
} else {
b := make([]*hst.FilesystemConfig, 0, len(d))
for _, ent := range d {
p := "/" + ent.Name()
switch p {
case "/proc":
case "/dev":
case "/tmp":
case "/mnt":
case "/etc":
AutoRoot: "/", default:
RootFlags: container.BindWritable, b = append(b, &hst.FilesystemConfig{Src: p, Write: true, Must: true})
}
}
conf.Filesystem = append(conf.Filesystem, b...)
} }
// hide nscd from sandbox if present
nscd := "/var/run/nscd"
if _, err := sys.Stat(nscd); !errors.Is(err, fs.ErrNotExist) {
conf.Cover = append(conf.Cover, nscd)
}
// bind GPU stuff // bind GPU stuff
if config.Enablements&(system.EX11|system.EWayland) != 0 { 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: "/dev/dri", Device: true})
@ -253,12 +275,6 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
// opportunistically bind kvm // opportunistically bind kvm
conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Src: "/dev/kvm", Device: true}) conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Src: "/dev/kvm", Device: true})
// hide nscd from container if present
const nscd = "/var/run/nscd"
if _, err := sys.Stat(nscd); !errors.Is(err, fs.ErrNotExist) {
conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Dst: nscd, Src: hst.SourceTmpfs})
}
config.Container = conf config.Container = conf
} }
@ -266,7 +282,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
{ {
var uid, gid int var uid, gid int
var err error var err error
seal.container, seal.env, err = newContainer(config.Container, sys, seal.id.String(), &uid, &gid) seal.container, seal.env, err = newContainer(config.Container, sys, &uid, &gid)
seal.waitDelay = config.Container.WaitDelay seal.waitDelay = config.Container.WaitDelay
if err != nil { if err != nil {
return hlog.WrapErrSuffix(err, return hlog.WrapErrSuffix(err,
@ -289,6 +305,18 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
} }
} }
if !config.Container.AutoEtc {
if config.Container.Etc != "" {
seal.container.Bind(config.Container.Etc, "/etc", 0)
}
} else {
etcPath := config.Container.Etc
if etcPath == "" {
etcPath = "/etc"
}
seal.container.Etc(etcPath, seal.id.String())
}
// inner XDG_RUNTIME_DIR default formatting of `/run/user/%d` as mapped uid // inner XDG_RUNTIME_DIR default formatting of `/run/user/%d` as mapped uid
innerRuntimeDir := path.Join("/run/user", mapuid.String()) innerRuntimeDir := path.Join("/run/user", mapuid.String())
seal.env[xdgRuntimeDir] = innerRuntimeDir seal.env[xdgRuntimeDir] = innerRuntimeDir
@ -478,9 +506,6 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
seal.container.Tmpfs(dest, 1<<13, 0755) seal.container.Tmpfs(dest, 1<<13, 0755)
} }
// mount root read-only as the final setup Op
seal.container.Remount("/", syscall.MS_RDONLY)
// append ExtraPerms last // append ExtraPerms last
for _, p := range config.ExtraPerms { for _, p := range config.ExtraPerms {
if p == nil { if p == nil {

View File

@ -175,6 +175,7 @@ in
++ optionals app.useCommonPaths cfg.commonPaths ++ optionals app.useCommonPaths cfg.commonPaths
++ app.extraPaths; ++ app.extraPaths;
auto_etc = true; auto_etc = true;
cover = [ "/var/run/nscd" ];
symlink = [ symlink = [
[ [

View File

@ -13,7 +13,7 @@
wayland-scanner, wayland-scanner,
xorg, xorg,
# for hpkg # for planterette
zstd, zstd,
gnutar, gnutar,
coreutils, coreutils,
@ -31,7 +31,7 @@
buildGoModule rec { buildGoModule rec {
pname = "hakurei"; pname = "hakurei";
version = "0.1.3"; version = "0.1.2";
srcFiltered = builtins.path { srcFiltered = builtins.path {
name = "${pname}-src"; name = "${pname}-src";
@ -116,7 +116,7 @@ buildGoModule rec {
makeBinaryWrapper "$out/libexec/hakurei" "$out/bin/hakurei" \ makeBinaryWrapper "$out/libexec/hakurei" "$out/bin/hakurei" \
--inherit-argv0 --prefix PATH : ${lib.makeBinPath appPackages} --inherit-argv0 --prefix PATH : ${lib.makeBinPath appPackages}
makeBinaryWrapper "$out/libexec/hpkg" "$out/bin/hpkg" \ makeBinaryWrapper "$out/libexec/planterette" "$out/bin/planterette" \
--inherit-argv0 --prefix PATH : ${ --inherit-argv0 --prefix PATH : ${
lib.makeBinPath ( lib.makeBinPath (
appPackages appPackages

View File

@ -82,18 +82,13 @@
jack.enable = true; jack.enable = true;
}; };
virtualisation = { virtualisation.qemu.options = [
# Hopefully reduces spurious test failures:
memorySize = 4096;
qemu.options = [
# Need to switch to a different GPU driver than the default one (-vga std) so that Sway can launch: # Need to switch to a different GPU driver than the default one (-vga std) so that Sway can launch:
"-vga none -device virtio-gpu-pci" "-vga none -device virtio-gpu-pci"
# Increase Go test compiler performance: # Increase Go test compiler performance:
"-smp 8" "-smp 8"
]; ];
};
environment.hakurei = { environment.hakurei = {
enable = true; enable = true;

View File

@ -23,9 +23,10 @@ let
; ;
}; };
importTestCase = callTestCase =
path: path: identity:
import path { let
tc = import path {
inherit inherit
fs fs
ent ent
@ -33,11 +34,6 @@ let
system system
; ;
}; };
callTestCase =
path: identity:
let
tc = importTestCase path;
in in
{ {
name = "check-sandbox-${tc.name}"; name = "check-sandbox-${tc.name}";
@ -65,13 +61,9 @@ let
testCaseName = name: "cat.gensokyo.hakurei.test." + name; testCaseName = name: "cat.gensokyo.hakurei.test." + name;
in in
{ {
apps = {
${testCaseName "preset"} = callTestCase ./preset.nix 1; ${testCaseName "preset"} = callTestCase ./preset.nix 1;
${testCaseName "tty"} = callTestCase ./tty.nix 2; ${testCaseName "tty"} = callTestCase ./tty.nix 2;
${testCaseName "mapuid"} = callTestCase ./mapuid.nix 3; ${testCaseName "mapuid"} = callTestCase ./mapuid.nix 3;
${testCaseName "device"} = callTestCase ./device.nix 4; ${testCaseName "device"} = callTestCase ./device.nix 4;
${testCaseName "pdlike"} = callTestCase ./pdlike.nix 5; ${testCaseName "pdlike"} = callTestCase ./pdlike.nix 5;
};
pd = importTestCase ./pd.nix;
} }

View File

@ -195,14 +195,15 @@ in
} null; } null;
} null; } null;
} null; } null;
run = fs "800001ed" { nscd = fs "800001ed" { } null; } null;
cache = fs "800001ed" { private = fs "800001c0" null null; } null; cache = fs "800001ed" { private = fs "800001c0" null null; } null;
} null; } null;
} null; } null;
mount = [ mount = [
(ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000004,gid=1000004") (ent "/sysroot" "/" "rw,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000004,gid=1000004")
(ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw") (ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
(ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000004,gid=1000004") (ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000004,gid=1000004")
(ent "/" "/dev" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/" "/dev" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666") (ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666")
(ent "/" "/dev/shm" "rw,nosuid,nodev" "tmpfs" "tmpfs" ignore) (ent "/" "/dev/shm" "rw,nosuid,nodev" "tmpfs" "tmpfs" ignore)
@ -219,7 +220,7 @@ in
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(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" "tmpfs" "rw,size=4k,mode=755,uid=1000004,gid=1000004")
(ent "/tmp/hakurei.1000/runtime/4" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.1000/runtime/4" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/tmp/hakurei.1000/tmpdir/4" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.1000/tmpdir/4" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var/lib/hakurei/u0/a4" "/var/lib/hakurei/u0/a4" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/var/lib/hakurei/u0/a4" "/var/lib/hakurei/u0/a4" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
@ -228,6 +229,7 @@ in
(ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore) (ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/var/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8k,mode=755,uid=1000004,gid=1000004")
]; ];
seccomp = true; seccomp = true;

View File

@ -221,14 +221,15 @@ in
} null; } null;
} null; } null;
} null; } null;
run = fs "800001ed" { nscd = fs "800001ed" { } null; } null;
cache = fs "800001ed" { private = fs "800001c0" null null; } null; cache = fs "800001ed" { private = fs "800001c0" null null; } null;
} null; } null;
} null; } null;
mount = [ mount = [
(ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000003,gid=1000003") (ent "/sysroot" "/" "rw,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000003,gid=1000003")
(ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw") (ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
(ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000003,gid=1000003") (ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000003,gid=1000003")
(ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000003,gid=1000003") (ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000003,gid=1000003")
(ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
@ -249,7 +250,7 @@ in
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000003,gid=1000003") (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000003,gid=1000003")
(ent "/tmp/hakurei.1000/runtime/3" "/run/user/1000" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.1000/runtime/3" "/run/user/1000" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/tmp/hakurei.1000/tmpdir/3" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.1000/tmpdir/3" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var/lib/hakurei/u0/a3" "/var/lib/hakurei/u0/a3" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/var/lib/hakurei/u0/a3" "/var/lib/hakurei/u0/a3" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
@ -258,6 +259,7 @@ in
(ent ignore "/run/user/1000/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/1000/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/run/user/1000/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore) (ent ignore "/run/user/1000/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
(ent ignore "/run/user/1000/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/1000/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/var/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8k,mode=755,uid=1000003,gid=1000003")
]; ];
seccomp = true; seccomp = true;

View File

@ -1,198 +0,0 @@
{
fs,
ent,
ignore,
...
}:
{
# 0, PresetExt | PresetDenyDevel
expectedFilter = {
x86_64-linux = "c698b081ff957afe17a6d94374537d37f2a63f6f9dd75da7546542407a9e32476ebda3312ba7785d7f618542bcfaf27ca27dcc2dddba852069d28bcfe8cad39a";
aarch64-linux = "433ce9b911282d6dcc8029319fb79b816b60d5a795ec8fc94344dd027614d68f023166a91bb881faaeeedd26e3d89474e141e5a69a97e93b8984ca8f14999980";
};
want = {
env = [
"HOME=/var/lib/hakurei/u0/a0"
"SHELL=/run/current-system/sw/bin/bash"
"TERM=linux"
"USER=u0_a0"
"XDG_RUNTIME_DIR=/run/user/65534"
"XDG_SESSION_CLASS=user"
"XDG_SESSION_TYPE=tty"
];
fs = fs "dead" {
".hakurei" = fs "800001ed" { } null;
bin = fs "800001ed" { sh = fs "80001ff" null null; } null;
dev = fs "800001ed" {
console = fs "4200190" null null;
core = fs "80001ff" null null;
fd = fs "80001ff" null null;
full = fs "42001b6" null null;
kvm = fs "42001b6" null null;
mqueue = fs "801001ff" { } null;
null = fs "42001b6" null "";
ptmx = fs "80001ff" null null;
pts = fs "800001ed" { ptmx = fs "42001b6" null null; } null;
random = fs "42001b6" null null;
shm = fs "800001ed" { } null;
stderr = fs "80001ff" null null;
stdin = fs "80001ff" null null;
stdout = fs "80001ff" null null;
tty = fs "42001b6" null null;
urandom = fs "42001b6" null null;
zero = fs "42001b6" null null;
} null;
etc = fs "800001ed" {
".clean" = fs "80001ff" null null;
".host" = fs "800001c0" null null;
".updated" = fs "80001ff" null null;
"NIXOS" = fs "80001ff" null null;
"X11" = fs "80001ff" null null;
"alsa" = fs "80001ff" null null;
"bash_logout" = fs "80001ff" null null;
"bashrc" = fs "80001ff" null null;
"binfmt.d" = fs "80001ff" null null;
"dbus-1" = fs "80001ff" null null;
"default" = fs "80001ff" null null;
"dhcpcd.exit-hook" = fs "80001ff" null null;
"fonts" = fs "80001ff" null null;
"fstab" = fs "80001ff" null null;
"hsurc" = fs "80001ff" null null;
"fuse.conf" = fs "80001ff" null null;
"group" = fs "180" null "hakurei:x:65534:\n";
"host.conf" = fs "80001ff" null null;
"hostname" = fs "80001ff" null null;
"hosts" = fs "80001ff" null null;
"inputrc" = fs "80001ff" null null;
"issue" = fs "80001ff" null null;
"kbd" = fs "80001ff" null null;
"locale.conf" = fs "80001ff" null null;
"login.defs" = fs "80001ff" null null;
"lsb-release" = fs "80001ff" null null;
"lvm" = fs "80001ff" null null;
"machine-id" = fs "80001ff" null null;
"man_db.conf" = fs "80001ff" null null;
"modprobe.d" = fs "80001ff" null null;
"modules-load.d" = fs "80001ff" null null;
"mtab" = fs "80001ff" null null;
"nanorc" = fs "80001ff" null null;
"netgroup" = fs "80001ff" null null;
"nix" = fs "80001ff" null null;
"nixos" = fs "80001ff" null null;
"nscd.conf" = fs "80001ff" null null;
"nsswitch.conf" = fs "80001ff" null null;
"os-release" = fs "80001ff" null null;
"pam" = fs "80001ff" null null;
"pam.d" = fs "80001ff" null null;
"passwd" = fs "180" null "u0_a0:x:65534:65534:Hakurei:/var/lib/hakurei/u0/a0:/run/current-system/sw/bin/bash\n";
"pipewire" = fs "80001ff" null null;
"pki" = fs "80001ff" null null;
"polkit-1" = fs "80001ff" null null;
"profile" = fs "80001ff" null null;
"protocols" = fs "80001ff" null null;
"resolv.conf" = fs "80001ff" null null;
"resolvconf.conf" = fs "80001ff" null null;
"rpc" = fs "80001ff" null null;
"services" = fs "80001ff" null null;
"set-environment" = fs "80001ff" null null;
"shadow" = fs "80001ff" null null;
"shells" = fs "80001ff" null null;
"ssh" = fs "80001ff" null null;
"ssl" = fs "80001ff" null null;
"static" = fs "80001ff" null null;
"subgid" = fs "80001ff" null null;
"subuid" = fs "80001ff" null null;
"sudoers" = fs "80001ff" null null;
"sway" = fs "80001ff" null null;
"sysctl.d" = fs "80001ff" null null;
"systemd" = fs "80001ff" null null;
"terminfo" = fs "80001ff" null null;
"tmpfiles.d" = fs "80001ff" null null;
"udev" = fs "80001ff" null null;
"vconsole.conf" = fs "80001ff" null null;
"xdg" = fs "80001ff" null null;
"zoneinfo" = fs "80001ff" null null;
} null;
home = fs "800001ed" { alice = fs "800001c0" null null; } null;
lib64 = fs "800001ed" { "ld-linux-x86-64.so.2" = fs "80001ff" null null; } null;
"lost+found" = fs "800001c0" null null;
nix = fs "800001ed" {
".ro-store" = fs "801001fd" null null;
".rw-store" = fs "800001ed" null null;
store = fs "801001fd" null null;
var = fs "800001ed" {
log = fs "800001ed" null null;
nix = fs "800001ed" null null;
} null;
} null;
proc = fs "8000016d" null null;
root = fs "800001c0" null null;
run = fs "800001ed" null null;
srv = fs "800001ed" { } null;
sys = fs "8000016d" null null;
tmp = fs "800001f8" { } null;
usr = fs "800001ed" { bin = fs "800001ed" { env = fs "80001ff" null null; } null; } null;
var = fs "800001ed" null null;
} null;
mount = [
(ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000000,gid=1000000")
(ent "/bin" "/bin" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/home" "/home" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/lib64" "/lib64" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/lost+found" "/lost+found" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/nix" "/nix" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/nix/.ro-store" "rw,nosuid,nodev,relatime" "9p" "nix-store" ignore)
(ent "/" "/nix/.rw-store" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,mode=755")
(ent "/" "/nix/store" "rw,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
(ent "/root" "/root" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/run" "rw,nosuid,nodev" "tmpfs" "tmpfs" ignore)
(ent "/" "/run/keys" "rw,nosuid,nodev,relatime" "ramfs" "ramfs" "rw,mode=750")
(ent "/" "/run/credentials/systemd-journald.service" "ro,nosuid,nodev,noexec,relatime,nosymfollow" "tmpfs" "tmpfs" "rw,size=1024k,nr_inodes=1024,mode=700,noswap")
(ent "/" "/run/wrappers" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
(ent "/" "/run/credentials/getty@tty1.service" "ro,nosuid,nodev,noexec,relatime,nosymfollow" "tmpfs" "tmpfs" "rw,size=1024k,nr_inodes=1024,mode=700,noswap")
(ent "/" "/run/user/1000" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
(ent "/srv" "/srv" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/sys" "rw,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
(ent "/" "/sys/kernel/security" "rw,nosuid,nodev,noexec,relatime" "securityfs" "securityfs" "rw")
(ent "/../../.." "/sys/fs/cgroup" "rw,nosuid,nodev,noexec,relatime" "cgroup2" "cgroup2" "rw,nsdelegate,memory_recursiveprot")
(ent "/" "/sys/fs/pstore" "rw,nosuid,nodev,noexec,relatime" "pstore" "pstore" "rw")
(ent "/" "/sys/fs/bpf" "rw,nosuid,nodev,noexec,relatime" "bpf" "bpf" "rw,mode=700")
# systemd race: tracefs debugfs configfs fusectl
(ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
(ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
(ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
(ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
(ent "/usr" "/usr" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var" "/var" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
(ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000000,gid=1000000")
(ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000000,gid=1000000")
(ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/full" "/dev/full" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/random" "/dev/random" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/urandom" "/dev/urandom" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/tty" "/dev/tty" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,mode=620,ptmxmode=666")
(ent ignore "/dev/console" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666")
(ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
(ent "/kvm" "/dev/kvm" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/" "/run/nscd" "ro,nosuid,nodev,relatime" "tmpfs" "readonly" "ro,mode=755,uid=1000000,gid=1000000")
(ent "/" "/run/user/1000" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=1000000,gid=1000000")
(ent "/" "/run/dbus" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=1000000,gid=1000000")
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000000,gid=1000000")
(ent "/tmp/hakurei.1000/runtime/0" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/tmp/hakurei.1000/tmpdir/0" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var/lib/hakurei/u0/a0" "/var/lib/hakurei/u0/a0" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000000,gid=1000000")
(ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000000,gid=1000000")
];
seccomp = true;
};
}

View File

@ -222,13 +222,14 @@ in
} null; } null;
} null; } null;
} null; } null;
run = fs "800001ed" { nscd = fs "800001ed" { } null; } null;
} null; } null;
} null; } null;
mount = [ mount = [
(ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000005,gid=1000005") (ent "/sysroot" "/" "rw,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000005,gid=1000005")
(ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw") (ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
(ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000005,gid=1000005") (ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000005,gid=1000005")
(ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000005,gid=1000005") (ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000005,gid=1000005")
(ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
@ -249,7 +250,7 @@ in
(ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw") (ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000005,gid=1000005") (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000005,gid=1000005")
(ent "/tmp/hakurei.1000/runtime/5" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.1000/runtime/5" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/tmp/hakurei.1000/tmpdir/5" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.1000/tmpdir/5" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var/lib/hakurei/u0/a5" "/var/lib/hakurei/u0/a5" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/var/lib/hakurei/u0/a5" "/var/lib/hakurei/u0/a5" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
@ -258,6 +259,7 @@ in
(ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore) (ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/var/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8k,mode=755,uid=1000005,gid=1000005")
]; ];
seccomp = true; seccomp = true;

View File

@ -221,13 +221,14 @@ in
} null; } null;
} null; } null;
} null; } null;
run = fs "800001ed" { nscd = fs "800001ed" { } null; } null;
} null; } null;
} null; } null;
mount = [ mount = [
(ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000001,gid=1000001") (ent "/sysroot" "/" "rw,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000001,gid=1000001")
(ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw") (ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
(ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000001,gid=1000001") (ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000001,gid=1000001")
(ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000001,gid=1000001") (ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000001,gid=1000001")
(ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
@ -247,7 +248,7 @@ in
(ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw") (ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(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" "tmpfs" "rw,size=4k,mode=755,uid=1000001,gid=1000001")
(ent "/tmp/hakurei.1000/runtime/1" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.1000/runtime/1" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/tmp/hakurei.1000/tmpdir/1" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.1000/tmpdir/1" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var/lib/hakurei/u0/a1" "/var/lib/hakurei/u0/a1" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/var/lib/hakurei/u0/a1" "/var/lib/hakurei/u0/a1" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
@ -256,6 +257,7 @@ in
(ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore) (ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/var/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8k,mode=755,uid=1000001,gid=1000001")
]; ];
seccomp = true; seccomp = true;

View File

@ -222,14 +222,15 @@ in
} null; } null;
} null; } null;
} null; } null;
run = fs "800001ed" { nscd = fs "800001ed" { } null; } null;
cache = fs "800001ed" { private = fs "800001c0" null null; } null; cache = fs "800001ed" { private = fs "800001c0" null null; } null;
} null; } null;
} null; } null;
mount = [ mount = [
(ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000002,gid=1000002") (ent "/sysroot" "/" "rw,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000002,gid=1000002")
(ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw") (ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
(ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=1000002,gid=1000002") (ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000002,gid=1000002")
(ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000002,gid=1000002") (ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000002,gid=1000002")
(ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
@ -251,7 +252,7 @@ in
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore) (ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(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" "tmpfs" "rw,size=4k,mode=755,uid=1000002,gid=1000002")
(ent "/tmp/hakurei.1000/runtime/2" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.1000/runtime/2" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/tmp/hakurei.1000/tmpdir/2" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.1000/tmpdir/2" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var/lib/hakurei/u0/a2" "/var/lib/hakurei/u0/a2" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/var/lib/hakurei/u0/a2" "/var/lib/hakurei/u0/a2" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
@ -260,6 +261,7 @@ in
(ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore) (ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/var/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8k,mode=755,uid=1000002,gid=1000002")
]; ];
seccomp = true; seccomp = true;

View File

@ -6,7 +6,6 @@
}: }:
let let
testProgram = pkgs.callPackage ./tool/package.nix { inherit (config.environment.hakurei.package) version; }; testProgram = pkgs.callPackage ./tool/package.nix { inherit (config.environment.hakurei.package) version; };
testCases = import ./case pkgs.system lib testProgram;
in in
{ {
users.users = { users.users = {
@ -27,13 +26,6 @@ in
systemPackages = [ systemPackages = [
# For checking seccomp outcome: # For checking seccomp outcome:
testProgram testProgram
# For checking pd outcome:
(pkgs.writeShellScriptBin "check-sandbox-pd" ''
hakurei -v run hakurei-test \
-t ${toString (builtins.toFile "hakurei-pd-want.json" (builtins.toJSON testCases.pd.want))} \
-s ${testCases.pd.expectedFilter.${pkgs.system}} "$@"
'')
]; ];
variables = { variables = {
@ -83,6 +75,6 @@ in
} }
]; ];
inherit (testCases) apps; apps = import ./case pkgs.system lib testProgram;
}; };
} }

View File

@ -58,13 +58,12 @@ print(machine.fail("sudo -u alice -i hakurei run umount -R /dev"))
check_offset = 0 check_offset = 0
def check_sandbox(name): def check_sandbox(name):
global check_offset global check_offset
check_offset += 1
swaymsg(f"exec script /dev/null -E always -qec check-sandbox-{name}") swaymsg(f"exec script /dev/null -E always -qec check-sandbox-{name}")
machine.wait_for_file(f"/tmp/hakurei.1000/tmpdir/{check_offset}/sandbox-ok", timeout=15) machine.wait_for_file(f"/tmp/hakurei.1000/tmpdir/{check_offset}/sandbox-ok", timeout=15)
check_filter(check_offset, name, "hakurei-test") check_filter(check_offset, name, "hakurei-test")
check_offset += 1
check_sandbox("pd")
check_sandbox("preset") check_sandbox("preset")
check_sandbox("tty") check_sandbox("tty")
check_sandbox("mapuid") check_sandbox("mapuid")