13 Commits

Author SHA1 Message Date
c5aefe5e9d internal/app/shim: check behaviour
All checks were successful
Test / Create distribution (push) Successful in 25s
Test / Sandbox (push) Successful in 39s
Test / Sandbox (race detector) (push) Successful in 39s
Test / Hakurei (race detector) (push) Successful in 43s
Test / Hakurei (push) Successful in 44s
Test / Hpkg (push) Successful in 41s
Test / Flake checks (push) Successful in 1m20s
This does not yet have full coverage. Test cases covering failsafe paths and error injection will be added eventually.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-23 06:07:41 +09:00
0f8ffee44d internal/app: test case for hst template
All checks were successful
Test / Create distribution (push) Successful in 32s
Test / Sandbox (push) Successful in 2m8s
Test / Hakurei (push) Successful in 3m5s
Test / Hpkg (push) Successful in 3m58s
Test / Sandbox (race detector) (push) Successful in 4m2s
Test / Hakurei (race detector) (push) Successful in 4m42s
Test / Flake checks (push) Successful in 1m21s
This helps with other areas of the test suite as they're all based on hst.Template. This also helps contributors understand the behaviour of internal/app as hst.Template covers almost every aspect of it.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-23 04:46:58 +09:00
1685a4d000 cmd/hsu: reduce excessive test range
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 1m40s
Test / Sandbox (race detector) (push) Successful in 2m25s
Test / Hakurei (push) Successful in 2m36s
Test / Hakurei (race detector) (push) Successful in 3m13s
Test / Hpkg (push) Successful in 3m33s
Test / Flake checks (push) Successful in 1m24s
This is quite a simple piece of code, this many test cases is excessive and wastes time in the integration vm.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-23 04:32:32 +09:00
6c338b433a internal/app: reduce test case indentation
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m13s
Test / Hakurei (push) Successful in 3m9s
Test / Sandbox (race detector) (push) Successful in 4m3s
Test / Hpkg (push) Successful in 4m4s
Test / Hakurei (race detector) (push) Successful in 4m44s
Test / Flake checks (push) Successful in 1m28s
This improves readability on narrower displays.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-22 07:40:32 +09:00
8accd3b219 internal/app/shim: use syscall dispatcher
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m14s
Test / Hakurei (push) Successful in 3m9s
Test / Sandbox (race detector) (push) Successful in 3m58s
Test / Hpkg (push) Successful in 4m5s
Test / Hakurei (race detector) (push) Successful in 4m46s
Test / Flake checks (push) Successful in 1m28s
This enables instrumented testing of the shim.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-22 06:58:45 +09:00
c5f59c5488 container/syscall: export prctl wrapper
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m13s
Test / Hakurei (push) Successful in 3m3s
Test / Sandbox (race detector) (push) Successful in 3m58s
Test / Hpkg (push) Successful in 4m4s
Test / Hakurei (race detector) (push) Successful in 4m46s
Test / Flake checks (push) Successful in 1m27s
This is useful as package "syscall" does not provide such a wrapper. This change also improves error handling to fully conform to the manpage.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-22 05:26:54 +09:00
fcd9becf9a cmd/hsu: run in locked thread
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 1m41s
Test / Sandbox (race detector) (push) Successful in 2m23s
Test / Hakurei (push) Successful in 2m35s
Test / Hakurei (race detector) (push) Successful in 3m14s
Test / Hpkg (push) Successful in 3m39s
Test / Flake checks (push) Successful in 1m27s
Goroutine scheduling is not helpful in the setuid wrapper, it is not particularly harmful but lock here anyway.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-22 05:09:08 +09:00
622f945c22 container/init: check msg in entrypoint
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m14s
Test / Hakurei (push) Successful in 3m10s
Test / Sandbox (race detector) (push) Successful in 3m59s
Test / Hpkg (push) Successful in 4m8s
Test / Hakurei (race detector) (push) Successful in 4m46s
Test / Flake checks (push) Successful in 1m27s
This covers invalid call to Init.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-22 04:20:08 +09:00
e94acc424c container/comp: rename from bits
All checks were successful
Test / Create distribution (push) Successful in 32s
Test / Sandbox (push) Successful in 2m19s
Test / Hakurei (push) Successful in 3m9s
Test / Hpkg (push) Successful in 3m53s
Test / Sandbox (race detector) (push) Successful in 4m2s
Test / Hakurei (race detector) (push) Successful in 4m43s
Test / Flake checks (push) Successful in 1m23s
This package will also hold syscall lookup tables for seccomp.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 20:54:03 +09:00
b1a4d801be hst/container: flags string representation
All checks were successful
Test / Create distribution (push) Successful in 32s
Test / Sandbox (push) Successful in 2m9s
Test / Sandbox (race detector) (push) Successful in 3m56s
Test / Hpkg (push) Successful in 4m5s
Test / Hakurei (race detector) (push) Successful in 4m42s
Test / Hakurei (push) Successful in 2m9s
Test / Flake checks (push) Successful in 1m28s
This is useful for a user-facing representation other than JSON. This also gets rid of the ugly, outdated flags string builder in cmd/hakurei.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 20:29:52 +09:00
56beae17fe test: assert hst CGO_ENABLED=0
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 40s
Test / Sandbox (race detector) (push) Successful in 39s
Test / Hpkg (push) Successful in 41s
Test / Hakurei (push) Successful in 2m29s
Test / Hakurei (race detector) (push) Successful in 3m7s
Test / Flake checks (push) Successful in 1m24s
The hst package only deals with data serialisation, however since many parts of hakurei make use of C libraries in some way it can be easy to inadvertently depend on cgo.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 19:49:04 +09:00
ea978101b1 cmd/hakurei/parse: close config fd
All checks were successful
Test / Create distribution (push) Successful in 32s
Test / Sandbox (push) Successful in 2m9s
Test / Hakurei (push) Successful in 3m5s
Test / Sandbox (race detector) (push) Successful in 3m54s
Test / Hpkg (push) Successful in 3m57s
Test / Hakurei (race detector) (push) Successful in 4m43s
Test / Flake checks (push) Successful in 1m20s
This is cleaner than relying on the finalizer.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 06:05:36 +09:00
fbd1638e7f test/interactive/trace: update nix attribute
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 40s
Test / Sandbox (race detector) (push) Successful in 40s
Test / Hakurei (race detector) (push) Successful in 44s
Test / Hakurei (push) Successful in 45s
Test / Hpkg (push) Successful in 42s
Test / Flake checks (push) Successful in 1m28s
Updated according to evaluation warning.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 06:03:09 +09:00
43 changed files with 1354 additions and 755 deletions

View File

@@ -50,7 +50,7 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr
Flag(&flagVerbose, "v", command.BoolFlag(false), "Increase log verbosity"). Flag(&flagVerbose, "v", command.BoolFlag(false), "Increase log verbosity").
Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output in JSON when applicable") Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output in JSON when applicable")
c.Command("shim", command.UsageInternal, func([]string) error { app.ShimMain(); return errSuccess }) c.Command("shim", command.UsageInternal, func([]string) error { app.Shim(msg); return errSuccess })
c.Command("app", "Load and start container from configuration file", func(args []string) error { c.Command("app", "Load and start container from configuration file", func(args []string) error {
if len(args) < 1 { if len(args) < 1 {

View File

@@ -16,7 +16,7 @@ import (
) )
func tryPath(msg message.Msg, name string) (config *hst.Config) { func tryPath(msg message.Msg, name string) (config *hst.Config) {
var r io.Reader var r io.ReadCloser
config = new(hst.Config) config = new(hst.Config)
if name != "-" { if name != "-" {
@@ -25,23 +25,20 @@ func tryPath(msg message.Msg, name string) (config *hst.Config) {
msg.Verbose("load configuration from file") msg.Verbose("load configuration from file")
if f, err := os.Open(name); err != nil { if f, err := os.Open(name); err != nil {
log.Fatalf("cannot access configuration file %q: %s", name, err) log.Fatal(err.Error())
return
} else { } else {
// finalizer closes f
r = f r = f
} }
} else {
defer func() {
if err := r.(io.ReadCloser).Close(); err != nil {
log.Printf("cannot close config fd: %v", err)
}
}()
} }
} else { } else {
r = os.Stdin r = os.Stdin
} }
decodeJSON(log.Fatal, "load configuration", r, &config) decodeJSON(log.Fatal, "load configuration", r, &config)
if err := r.Close(); err != nil {
log.Fatal(err.Error())
}
return return
} }

View File

@@ -89,24 +89,19 @@ func printShowInstance(
if params.Hostname != "" { if params.Hostname != "" {
t.Printf(" Hostname:\t%s\n", params.Hostname) t.Printf(" Hostname:\t%s\n", params.Hostname)
} }
flags := make([]string, 0, 7) flags := params.Flags.String()
writeFlag := func(name string, flag uintptr, force bool) {
if params.Flags&flag != 0 || force { // this is included in the upper hst.Config struct but is relevant here
flags = append(flags, name) const flagDirectWayland = "directwl"
if config.DirectWayland {
// hardcoded value when every flag is unset
if flags == "none" {
flags = flagDirectWayland
} else {
flags += ", " + flagDirectWayland
} }
} }
writeFlag("userns", hst.FUserns, false) t.Printf(" Flags:\t%s\n", flags)
writeFlag("devel", hst.FDevel, false)
writeFlag("net", hst.FHostNet, false)
writeFlag("abstract", hst.FHostAbstract, false)
writeFlag("device", hst.FDevice, false)
writeFlag("tty", hst.FTty, false)
writeFlag("mapuid", hst.FMapRealUID, false)
writeFlag("directwl", 0, config.DirectWayland)
if len(flags) == 0 {
flags = append(flags, "none")
}
t.Printf(" Flags:\t%s\n", strings.Join(flags, " "))
if params.Path != nil { if params.Path != nil {
t.Printf(" Path:\t%s\n", params.Path) t.Printf(" Path:\t%s\n", params.Path)

View File

@@ -43,7 +43,7 @@ func TestPrintShowInstance(t *testing.T) {
Groups: video, dialout, plugdev Groups: video, dialout, plugdev
Home: /data/data/org.chromium.Chromium Home: /data/data/org.chromium.Chromium
Hostname: localhost Hostname: localhost
Flags: userns devel net abstract device tty mapuid Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland
@@ -87,6 +87,22 @@ App
Enablements: (no enablements) Enablements: (no enablements)
Flags: none Flags: none
`, false},
{"config flag none directwl", nil, &hst.Config{DirectWayland: true, Container: new(hst.ContainerConfig)}, false, false, `Error: container configuration missing path to home directory!
App
Identity: 0
Enablements: (no enablements)
Flags: directwl
`, false},
{"config flag directwl", nil, &hst.Config{DirectWayland: true, Container: &hst.ContainerConfig{Flags: hst.FMultiarch}}, false, false, `Error: container configuration missing path to home directory!
App
Identity: 0
Enablements: (no enablements)
Flags: multiarch, directwl
`, false}, `, false},
{"config nil entries", nil, &hst.Config{Container: &hst.ContainerConfig{Filesystem: make([]hst.FilesystemConfigJSON, 1)}, ExtraPerms: make([]hst.ExtraPermConfig, 1)}, false, false, `Error: container configuration missing path to home directory! {"config nil entries", nil, &hst.Config{Container: &hst.ContainerConfig{Filesystem: make([]hst.FilesystemConfigJSON, 1)}, ExtraPerms: make([]hst.ExtraPermConfig, 1)}, false, false, `Error: container configuration missing path to home directory!
@@ -124,7 +140,7 @@ App
Groups: video, dialout, plugdev Groups: video, dialout, plugdev
Home: /data/data/org.chromium.Chromium Home: /data/data/org.chromium.Chromium
Hostname: localhost Hostname: localhost
Flags: userns devel net abstract device tty mapuid Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland

View File

@@ -8,6 +8,7 @@ import (
"log" "log"
"os" "os"
"path" "path"
"runtime"
"slices" "slices"
"strconv" "strconv"
"strings" "strings"
@@ -32,6 +33,8 @@ const (
var hakureiPath string var hakureiPath string
func main() { func main() {
runtime.LockOSThread()
log.SetFlags(0) log.SetFlags(0)
log.SetPrefix("hsu: ") log.SetPrefix("hsu: ")
log.SetOutput(os.Stderr) log.SetOutput(os.Stderr)

View File

@@ -36,7 +36,7 @@ func TestParseUint32Fast(t *testing.T) {
} }
}) })
t.Run("full range", func(t *testing.T) { t.Run("range", func(t *testing.T) {
t.Parallel() t.Parallel()
testRange := func(i, end int) { testRange := func(i, end int) {
@@ -61,11 +61,9 @@ func TestParseUint32Fast(t *testing.T) {
} }
} }
testRange(0, 5000) testRange(0, 2500)
testRange(105000, 110000) testRange(23002500, 23005000)
testRange(23005000, 23010000) testRange(7890002500, 7890005000)
testRange(456005000, 456010000)
testRange(7890005000, 7890010000)
}) })
} }

View File

@@ -5,8 +5,8 @@ import (
"os" "os"
"testing" "testing"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/message" "hakurei.app/message"
) )
@@ -23,14 +23,14 @@ func TestAutoRootOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{ checkOpBehaviour(t, []opBehaviourTestCase{
{"readdir", &Params{ParentPerm: 0750}, &AutoRootOp{ {"readdir", &Params{ParentPerm: 0750}, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, []stub.Call{ }, []stub.Call{
call("readdir", stub.ExpectArgs{"/"}, stubDir(), stub.UniqueError(2)), call("readdir", stub.ExpectArgs{"/"}, stubDir(), stub.UniqueError(2)),
}, stub.UniqueError(2), nil, nil}, }, stub.UniqueError(2), nil, nil},
{"early", &Params{ParentPerm: 0750}, &AutoRootOp{ {"early", &Params{ParentPerm: 0750}, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, []stub.Call{ }, []stub.Call{
call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64",
"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil), "lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil),
@@ -39,7 +39,7 @@ func TestAutoRootOp(t *testing.T) {
{"apply", &Params{ParentPerm: 0750}, &AutoRootOp{ {"apply", &Params{ParentPerm: 0750}, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, []stub.Call{ }, []stub.Call{
call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64",
"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil), "lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil),
@@ -60,7 +60,7 @@ func TestAutoRootOp(t *testing.T) {
{"success pd", &Params{ParentPerm: 0750}, &AutoRootOp{ {"success pd", &Params{ParentPerm: 0750}, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, []stub.Call{ }, []stub.Call{
call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64",
"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil), "lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil),
@@ -127,10 +127,10 @@ func TestAutoRootOp(t *testing.T) {
}) })
checkOpsBuilder(t, []opsBuilderTestCase{ checkOpsBuilder(t, []opsBuilderTestCase{
{"pd", new(Ops).Root(check.MustAbs("/"), bits.BindWritable), Ops{ {"pd", new(Ops).Root(check.MustAbs("/"), comp.BindWritable), Ops{
&AutoRootOp{ &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, },
}}, }},
}) })
@@ -140,42 +140,42 @@ func TestAutoRootOp(t *testing.T) {
{"internal ne", &AutoRootOp{ {"internal ne", &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, &AutoRootOp{ }, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
resolved: []*BindMountOp{new(BindMountOp)}, resolved: []*BindMountOp{new(BindMountOp)},
}, true}, }, true},
{"flags differs", &AutoRootOp{ {"flags differs", &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable | bits.BindDevice, Flags: comp.BindWritable | comp.BindDevice,
}, &AutoRootOp{ }, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, false}, }, false},
{"host differs", &AutoRootOp{ {"host differs", &AutoRootOp{
Host: check.MustAbs("/tmp/"), Host: check.MustAbs("/tmp/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, &AutoRootOp{ }, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, false}, }, false},
{"equals", &AutoRootOp{ {"equals", &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, &AutoRootOp{ }, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, true}, }, true},
}) })
checkOpMeta(t, []opMetaTestCase{ checkOpMeta(t, []opMetaTestCase{
{"root", &AutoRootOp{ {"root", &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, "setting up", `auto root "/" flags 0x2`}, }, "setting up", `auto root "/" flags 0x2`},
}) })
} }

View File

@@ -1,13 +0,0 @@
// Package bits contains constants for configuring the container.
package bits
const (
// BindOptional skips nonexistent host paths.
BindOptional = 1 << iota
// BindWritable mounts filesystem read-write.
BindWritable
// BindDevice allows access to devices (special files) on this filesystem.
BindDevice
// BindEnsure attempts to create the host path if it does not exist.
BindEnsure
)

View File

@@ -49,41 +49,10 @@ func capset(hdrp *capHeader, datap *[2]capData) error {
} }
// capBoundingSetDrop drops a capability from the calling thread's capability bounding set. // capBoundingSetDrop drops a capability from the calling thread's capability bounding set.
func capBoundingSetDrop(cap uintptr) error { func capBoundingSetDrop(cap uintptr) error { return Prctl(syscall.PR_CAPBSET_DROP, cap, 0) }
r, _, errno := syscall.Syscall(
syscall.SYS_PRCTL,
syscall.PR_CAPBSET_DROP,
cap, 0,
)
if r != 0 {
return errno
}
return nil
}
// capAmbientClearAll clears the ambient capability set of the calling thread. // capAmbientClearAll clears the ambient capability set of the calling thread.
func capAmbientClearAll() error { func capAmbientClearAll() error { return Prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0) }
r, _, errno := syscall.Syscall(
syscall.SYS_PRCTL,
PR_CAP_AMBIENT,
PR_CAP_AMBIENT_CLEAR_ALL, 0,
)
if r != 0 {
return errno
}
return nil
}
// capAmbientRaise adds to the ambient capability set of the calling thread. // capAmbientRaise adds to the ambient capability set of the calling thread.
func capAmbientRaise(cap uintptr) error { func capAmbientRaise(cap uintptr) error { return Prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap) }
r, _, errno := syscall.Syscall(
syscall.SYS_PRCTL,
PR_CAP_AMBIENT,
PR_CAP_AMBIENT_RAISE,
cap,
)
if r != 0 {
return errno
}
return nil
}

View File

@@ -1,4 +1,16 @@
package bits // Package comp contains constants from container packages without depending on cgo.
package comp
const (
// BindOptional skips nonexistent host paths.
BindOptional = 1 << iota
// BindWritable mounts filesystem read-write.
BindWritable
// BindDevice allows access to devices (special files) on this filesystem.
BindDevice
// BindEnsure attempts to create the host path if it does not exist.
BindEnsure
)
// FilterPreset specifies parts of the syscall filter preset to enable. // FilterPreset specifies parts of the syscall filter preset to enable.
type FilterPreset int type FilterPreset int

View File

@@ -14,8 +14,8 @@ import (
. "syscall" . "syscall"
"time" "time"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/message" "hakurei.app/message"
@@ -86,7 +86,7 @@ type (
// Extra seccomp flags. // Extra seccomp flags.
SeccompFlags seccomp.ExportFlag SeccompFlags seccomp.ExportFlag
// Seccomp presets. Has no effect unless SeccompRules is zero-length. // Seccomp presets. Has no effect unless SeccompRules is zero-length.
SeccompPresets bits.FilterPreset SeccompPresets comp.FilterPreset
// Do not load seccomp program. // Do not load seccomp program.
SeccompDisable bool SeccompDisable bool
@@ -174,7 +174,7 @@ func (p *Container) Start() error {
} }
if !p.RetainSession { if !p.RetainSession {
p.SeccompPresets |= bits.PresetDenyTTY p.SeccompPresets |= comp.PresetDenyTTY
} }
if p.AdoptWaitDelay == 0 { if p.AdoptWaitDelay == 0 {

View File

@@ -20,8 +20,8 @@ import (
"hakurei.app/command" "hakurei.app/command"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/container/vfs" "hakurei.app/container/vfs"
"hakurei.app/hst" "hakurei.app/hst"
@@ -206,20 +206,20 @@ var containerTestCases = []struct {
rules []seccomp.NativeRule rules []seccomp.NativeRule
flags seccomp.ExportFlag flags seccomp.ExportFlag
presets bits.FilterPreset presets comp.FilterPreset
}{ }{
{"minimal", true, false, false, true, {"minimal", true, false, false, true,
emptyOps, emptyMnt, emptyOps, emptyMnt,
1000, 100, nil, 0, bits.PresetStrict}, 1000, 100, nil, 0, comp.PresetStrict},
{"allow", true, true, true, false, {"allow", true, true, true, false,
emptyOps, emptyMnt, emptyOps, emptyMnt,
1000, 100, nil, 0, bits.PresetExt | bits.PresetDenyDevel}, 1000, 100, nil, 0, comp.PresetExt | comp.PresetDenyDevel},
{"no filter", false, true, true, true, {"no filter", false, true, true, true,
emptyOps, emptyMnt, emptyOps, emptyMnt,
1000, 100, nil, 0, bits.PresetExt}, 1000, 100, nil, 0, comp.PresetExt},
{"custom rules", true, true, true, false, {"custom rules", true, true, true, false,
emptyOps, emptyMnt, emptyOps, emptyMnt,
1, 31, []seccomp.NativeRule{{Syscall: seccomp.ScmpSyscall(syscall.SYS_SETUID), Errno: seccomp.ScmpErrno(syscall.EPERM)}}, 0, bits.PresetExt}, 1, 31, []seccomp.NativeRule{{Syscall: seccomp.ScmpSyscall(syscall.SYS_SETUID), Errno: seccomp.ScmpErrno(syscall.EPERM)}}, 0, comp.PresetExt},
{"tmpfs", true, false, false, true, {"tmpfs", true, false, false, true,
earlyOps(new(container.Ops). earlyOps(new(container.Ops).
@@ -228,7 +228,7 @@ var containerTestCases = []struct {
earlyMnt( earlyMnt(
ent("/", hst.PrivateTmp, "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", ignore), ent("/", hst.PrivateTmp, "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", ignore),
), ),
9, 9, nil, 0, bits.PresetStrict}, 9, 9, nil, 0, comp.PresetStrict},
{"dev", true, true /* go test output is not a tty */, false, false, {"dev", true, true /* go test output is not a tty */, false, false,
earlyOps(new(container.Ops). earlyOps(new(container.Ops).
@@ -246,7 +246,7 @@ var containerTestCases = []struct {
ent("/", "/dev/mqueue", "rw,nosuid,nodev,noexec,relatime", "mqueue", "mqueue", "rw"), ent("/", "/dev/mqueue", "rw,nosuid,nodev,noexec,relatime", "mqueue", "mqueue", "rw"),
ent("/", "/dev/shm", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore), ent("/", "/dev/shm", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
), ),
1971, 100, nil, 0, bits.PresetStrict}, 1971, 100, nil, 0, comp.PresetStrict},
{"dev no mqueue", true, true /* go test output is not a tty */, false, false, {"dev no mqueue", true, true /* go test output is not a tty */, false, false,
earlyOps(new(container.Ops). earlyOps(new(container.Ops).
@@ -263,7 +263,7 @@ var containerTestCases = []struct {
ent("/", "/dev/pts", "rw,nosuid,noexec,relatime", "devpts", "devpts", "rw,mode=620,ptmxmode=666"), ent("/", "/dev/pts", "rw,nosuid,noexec,relatime", "devpts", "devpts", "rw,mode=620,ptmxmode=666"),
ent("/", "/dev/shm", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore), ent("/", "/dev/shm", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
), ),
1971, 100, nil, 0, bits.PresetStrict}, 1971, 100, nil, 0, comp.PresetStrict},
{"overlay", true, false, false, true, {"overlay", true, false, false, true,
func(t *testing.T) (*container.Ops, context.Context) { func(t *testing.T) (*container.Ops, context.Context) {
@@ -300,7 +300,7 @@ var containerTestCases = []struct {
",redirect_dir=nofollow,uuid=on,userxattr"), ",redirect_dir=nofollow,uuid=on,userxattr"),
} }
}, },
1 << 3, 1 << 14, nil, 0, bits.PresetStrict}, 1 << 3, 1 << 14, nil, 0, comp.PresetStrict},
{"overlay ephemeral", true, false, false, true, {"overlay ephemeral", true, false, false, true,
func(t *testing.T) (*container.Ops, context.Context) { func(t *testing.T) (*container.Ops, context.Context) {
@@ -324,7 +324,7 @@ var containerTestCases = []struct {
ent("/", hst.PrivateTmp, "rw", "overlay", "overlay", ignore), ent("/", hst.PrivateTmp, "rw", "overlay", "overlay", ignore),
} }
}, },
1 << 3, 1 << 14, nil, 0, bits.PresetStrict}, 1 << 3, 1 << 14, nil, 0, comp.PresetStrict},
{"overlay readonly", true, false, false, true, {"overlay readonly", true, false, false, true,
func(t *testing.T) (*container.Ops, context.Context) { func(t *testing.T) (*container.Ops, context.Context) {
@@ -352,7 +352,7 @@ var containerTestCases = []struct {
",redirect_dir=nofollow,userxattr"), ",redirect_dir=nofollow,userxattr"),
} }
}, },
1 << 3, 1 << 14, nil, 0, bits.PresetStrict}, 1 << 3, 1 << 14, nil, 0, comp.PresetStrict},
} }
func TestContainer(t *testing.T) { func TestContainer(t *testing.T) {
@@ -560,9 +560,9 @@ func TestContainerString(t *testing.T) {
c := container.NewCommand(t.Context(), msg, check.MustAbs("/run/current-system/sw/bin/ldd"), "ldd", "/usr/bin/env") c := container.NewCommand(t.Context(), msg, check.MustAbs("/run/current-system/sw/bin/ldd"), "ldd", "/usr/bin/env")
c.SeccompFlags |= seccomp.AllowMultiarch c.SeccompFlags |= seccomp.AllowMultiarch
c.SeccompRules = seccomp.Preset( c.SeccompRules = seccomp.Preset(
bits.PresetExt|bits.PresetDenyNS|bits.PresetDenyTTY, comp.PresetExt|comp.PresetDenyNS|comp.PresetDenyTTY,
c.SeccompFlags) c.SeccompFlags)
c.SeccompPresets = bits.PresetStrict c.SeccompPresets = comp.PresetStrict
want := `argv: ["ldd" "/usr/bin/env"], filter: true, rules: 65, flags: 0x1, presets: 0xf` want := `argv: ["ldd" "/usr/bin/env"], filter: true, rules: 65, flags: 0x1, presets: 0xf`
if got := c.String(); got != want { if got := c.String(); got != want {
t.Errorf("String: %s, want %s", got, want) t.Errorf("String: %s, want %s", got, want)

View File

@@ -96,16 +96,15 @@ type initParams struct {
} }
// Init is called by [TryArgv0] if the current process is the container init. // Init is called by [TryArgv0] if the current process is the container init.
func Init(msg message.Msg) { func Init(msg message.Msg) { initEntrypoint(direct{}, msg) }
if msg == nil {
panic("attempting to call initEntrypoint with nil msg")
}
initEntrypoint(direct{}, msg)
}
func initEntrypoint(k syscallDispatcher, msg message.Msg) { func initEntrypoint(k syscallDispatcher, msg message.Msg) {
k.lockOSThread() k.lockOSThread()
if msg == nil {
panic("attempting to call initEntrypoint with nil msg")
}
if k.getpid() != 1 { if k.getpid() != 1 {
k.fatal(msg, "this process must run as pid 1") k.fatal(msg, "this process must run as pid 1")
} }
@@ -451,6 +450,7 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
} }
} }
// initName is the prefix used by log.std in the init process.
const initName = "init" const initName = "init"
// TryArgv0 calls [Init] if the last element of argv0 is "init". // TryArgv0 calls [Init] if the last element of argv0 is "init".

View File

@@ -6,8 +6,8 @@ import (
"testing" "testing"
"time" "time"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
@@ -70,7 +70,7 @@ func TestInitEntrypoint(t *testing.T) {
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(79), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(79), nil),
@@ -95,7 +95,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(78), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(78), nil),
@@ -123,7 +123,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(76), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(76), nil),
@@ -152,7 +152,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(74), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(74), nil),
@@ -182,7 +182,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(72), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(72), nil),
@@ -213,7 +213,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(70), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(70), nil),
@@ -245,7 +245,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(68), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(68), nil),
@@ -279,7 +279,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(66), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(66), nil),
@@ -315,7 +315,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(64), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(64), nil),
@@ -351,9 +351,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(nil, nil, bits.BindDevice), Ops: new(Ops).Bind(nil, nil, comp.BindDevice),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(63), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(63), nil),
@@ -389,9 +389,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(62), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(62), nil),
@@ -428,9 +428,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(60), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(60), nil),
@@ -467,9 +467,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(59), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(59), nil),
@@ -507,9 +507,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(57), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(57), nil),
@@ -548,9 +548,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(55), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(55), nil),
@@ -590,9 +590,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(53), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(53), nil),
@@ -633,9 +633,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(51), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(51), nil),
@@ -677,9 +677,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(49), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(49), nil),
@@ -722,9 +722,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(47), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(47), nil),
@@ -768,9 +768,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(45), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(45), nil),
@@ -823,9 +823,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(43), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(43), nil),
@@ -878,9 +878,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(42), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(42), nil),
@@ -934,9 +934,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(40), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(40), nil),
@@ -991,9 +991,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(38), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(38), nil),
@@ -1050,9 +1050,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(36), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(36), nil),
@@ -1110,9 +1110,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(34), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(34), nil),
@@ -1171,9 +1171,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(32), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(32), nil),
@@ -1233,9 +1233,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(30), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(30), nil),
@@ -1296,9 +1296,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(28), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(28), nil),
@@ -1360,9 +1360,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(26), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(26), nil),
@@ -1425,9 +1425,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(24), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(24), nil),
@@ -1491,9 +1491,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(22), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(22), nil),
@@ -1565,9 +1565,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(20), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(20), nil),
@@ -1672,9 +1672,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(18), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(18), nil),
@@ -1780,9 +1780,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(16), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(16), nil),
@@ -1869,7 +1869,7 @@ func TestInitEntrypoint(t *testing.T) {
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil), call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil),
call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, nil), call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, nil),
call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0, 0x200000, 0x200000}, {0, 0, 0}}}, nil, nil), call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0, 0x200000, 0x200000}, {0, 0, 0}}}, nil, nil),
call("verbosef", stub.ExpectArgs{"resolving presets %#x", []any{bits.FilterPreset(0xf)}}, nil, nil), call("verbosef", stub.ExpectArgs{"resolving presets %#x", []any{comp.FilterPreset(0xf)}}, nil, nil),
call("seccompLoad", stub.ExpectArgs{seccomp.Preset(0xf, 0), seccomp.ExportFlag(0)}, nil, stub.UniqueError(15)), call("seccompLoad", stub.ExpectArgs{seccomp.Preset(0xf, 0), seccomp.ExportFlag(0)}, nil, stub.UniqueError(15)),
call("fatalf", stub.ExpectArgs{"cannot load syscall filter: %v", []any{stub.UniqueError(15)}}, nil, nil), call("fatalf", stub.ExpectArgs{"cannot load syscall filter: %v", []any{stub.UniqueError(15)}}, nil, nil),
}, },
@@ -1890,7 +1890,7 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@@ -2004,7 +2004,7 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@@ -2106,7 +2106,7 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@@ -2199,7 +2199,7 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@@ -2294,7 +2294,7 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@@ -2396,7 +2396,7 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@@ -2534,9 +2534,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(0), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(0), nil),
@@ -2623,7 +2623,7 @@ func TestInitEntrypoint(t *testing.T) {
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil), call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil),
call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, nil), call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, nil),
call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0, 0x200000, 0x200000}, {0, 0, 0}}}, nil, nil), call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0, 0x200000, 0x200000}, {0, 0, 0}}}, nil, nil),
call("verbosef", stub.ExpectArgs{"resolving presets %#x", []any{bits.FilterPreset(0xf)}}, nil, nil), call("verbosef", stub.ExpectArgs{"resolving presets %#x", []any{comp.FilterPreset(0xf)}}, nil, nil),
call("seccompLoad", stub.ExpectArgs{seccomp.Preset(0xf, 0), seccomp.ExportFlag(0)}, nil, nil), call("seccompLoad", stub.ExpectArgs{seccomp.Preset(0xf, 0), seccomp.ExportFlag(0)}, nil, nil),
call("verbosef", stub.ExpectArgs{"%d filter rules loaded", []any{73}}, nil, nil), call("verbosef", stub.ExpectArgs{"%d filter rules loaded", []any{73}}, nil, nil),
call("newFile", stub.ExpectArgs{uintptr(10), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(10), "extra file 0"}, (*os.File)(nil), nil),

View File

@@ -6,8 +6,8 @@ import (
"os" "os"
"syscall" "syscall"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
) )
func init() { gob.Register(new(BindMountOp)) } func init() { gob.Register(new(BindMountOp)) }
@@ -29,18 +29,18 @@ type BindMountOp struct {
func (b *BindMountOp) Valid() bool { func (b *BindMountOp) Valid() bool {
return b != nil && return b != nil &&
b.Source != nil && b.Target != nil && b.Source != nil && b.Target != nil &&
b.Flags&(bits.BindOptional|bits.BindEnsure) != (bits.BindOptional|bits.BindEnsure) b.Flags&(comp.BindOptional|comp.BindEnsure) != (comp.BindOptional|comp.BindEnsure)
} }
func (b *BindMountOp) early(_ *setupState, k syscallDispatcher) error { func (b *BindMountOp) early(_ *setupState, k syscallDispatcher) error {
if b.Flags&bits.BindEnsure != 0 { if b.Flags&comp.BindEnsure != 0 {
if err := k.mkdirAll(b.Source.String(), 0700); err != nil { if err := k.mkdirAll(b.Source.String(), 0700); err != nil {
return err return err
} }
} }
if pathname, err := k.evalSymlinks(b.Source.String()); err != nil { if pathname, err := k.evalSymlinks(b.Source.String()); err != nil {
if os.IsNotExist(err) && b.Flags&bits.BindOptional != 0 { if os.IsNotExist(err) && b.Flags&comp.BindOptional != 0 {
// leave sourceFinal as nil // leave sourceFinal as nil
return nil return nil
} }
@@ -53,7 +53,7 @@ func (b *BindMountOp) early(_ *setupState, k syscallDispatcher) error {
func (b *BindMountOp) apply(state *setupState, k syscallDispatcher) error { func (b *BindMountOp) apply(state *setupState, k syscallDispatcher) error {
if b.sourceFinal == nil { if b.sourceFinal == nil {
if b.Flags&bits.BindOptional == 0 { if b.Flags&comp.BindOptional == 0 {
// unreachable // unreachable
return OpStateError("bind") return OpStateError("bind")
} }
@@ -76,10 +76,10 @@ func (b *BindMountOp) apply(state *setupState, k syscallDispatcher) error {
} }
var flags uintptr = syscall.MS_REC var flags uintptr = syscall.MS_REC
if b.Flags&bits.BindWritable == 0 { if b.Flags&comp.BindWritable == 0 {
flags |= syscall.MS_RDONLY flags |= syscall.MS_RDONLY
} }
if b.Flags&bits.BindDevice == 0 { if b.Flags&comp.BindDevice == 0 {
flags |= syscall.MS_NODEV flags |= syscall.MS_NODEV
} }

View File

@@ -6,8 +6,8 @@ import (
"syscall" "syscall"
"testing" "testing"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
@@ -25,7 +25,7 @@ func TestBindMountOp(t *testing.T) {
{"skip optional", new(Params), &BindMountOp{ {"skip optional", new(Params), &BindMountOp{
Source: check.MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: check.MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
Flags: bits.BindOptional, Flags: comp.BindOptional,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "", syscall.ENOENT), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "", syscall.ENOENT),
}, nil, nil, nil}, }, nil, nil, nil},
@@ -33,7 +33,7 @@ func TestBindMountOp(t *testing.T) {
{"success optional", new(Params), &BindMountOp{ {"success optional", new(Params), &BindMountOp{
Source: check.MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: check.MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
Flags: bits.BindOptional, Flags: comp.BindOptional,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
@@ -46,7 +46,7 @@ func TestBindMountOp(t *testing.T) {
{"ensureFile device", new(Params), &BindMountOp{ {"ensureFile device", new(Params), &BindMountOp{
Source: check.MustAbs("/dev/null"), Source: check.MustAbs("/dev/null"),
Target: check.MustAbs("/dev/null"), Target: check.MustAbs("/dev/null"),
Flags: bits.BindWritable | bits.BindDevice, Flags: comp.BindWritable | comp.BindDevice,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil), call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
@@ -57,7 +57,7 @@ func TestBindMountOp(t *testing.T) {
{"mkdirAll ensure", new(Params), &BindMountOp{ {"mkdirAll ensure", new(Params), &BindMountOp{
Source: check.MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: check.MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
Flags: bits.BindEnsure, Flags: comp.BindEnsure,
}, []stub.Call{ }, []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, stub.UniqueError(4)), call("mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, stub.UniqueError(4)),
}, stub.UniqueError(4), nil, nil}, }, stub.UniqueError(4), nil, nil},
@@ -65,7 +65,7 @@ func TestBindMountOp(t *testing.T) {
{"success ensure", new(Params), &BindMountOp{ {"success ensure", new(Params), &BindMountOp{
Source: check.MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: check.MustAbs("/usr/bin/"), Target: check.MustAbs("/usr/bin/"),
Flags: bits.BindEnsure, Flags: comp.BindEnsure,
}, []stub.Call{ }, []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, nil),
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil),
@@ -79,7 +79,7 @@ func TestBindMountOp(t *testing.T) {
{"success device ro", new(Params), &BindMountOp{ {"success device ro", new(Params), &BindMountOp{
Source: check.MustAbs("/dev/null"), Source: check.MustAbs("/dev/null"),
Target: check.MustAbs("/dev/null"), Target: check.MustAbs("/dev/null"),
Flags: bits.BindDevice, Flags: comp.BindDevice,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil), call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
@@ -92,7 +92,7 @@ func TestBindMountOp(t *testing.T) {
{"success device", new(Params), &BindMountOp{ {"success device", new(Params), &BindMountOp{
Source: check.MustAbs("/dev/null"), Source: check.MustAbs("/dev/null"),
Target: check.MustAbs("/dev/null"), Target: check.MustAbs("/dev/null"),
Flags: bits.BindWritable | bits.BindDevice, Flags: comp.BindWritable | comp.BindDevice,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil), call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
@@ -182,7 +182,7 @@ func TestBindMountOp(t *testing.T) {
{"zero", new(BindMountOp), false}, {"zero", new(BindMountOp), false},
{"nil source", &BindMountOp{Target: check.MustAbs("/")}, false}, {"nil source", &BindMountOp{Target: check.MustAbs("/")}, false},
{"nil target", &BindMountOp{Source: check.MustAbs("/")}, false}, {"nil target", &BindMountOp{Source: check.MustAbs("/")}, false},
{"flag optional ensure", &BindMountOp{Source: check.MustAbs("/"), Target: check.MustAbs("/"), Flags: bits.BindOptional | bits.BindEnsure}, false}, {"flag optional ensure", &BindMountOp{Source: check.MustAbs("/"), Target: check.MustAbs("/"), Flags: comp.BindOptional | comp.BindEnsure}, false},
{"valid", &BindMountOp{Source: check.MustAbs("/"), Target: check.MustAbs("/")}, true}, {"valid", &BindMountOp{Source: check.MustAbs("/"), Target: check.MustAbs("/")}, true},
}) })
@@ -217,7 +217,7 @@ func TestBindMountOp(t *testing.T) {
}, &BindMountOp{ }, &BindMountOp{
Source: check.MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
Flags: bits.BindOptional, Flags: comp.BindOptional,
}, false}, }, false},
{"source differs", &BindMountOp{ {"source differs", &BindMountOp{
@@ -256,7 +256,7 @@ func TestBindMountOp(t *testing.T) {
{"hostdev", &BindMountOp{ {"hostdev", &BindMountOp{
Source: check.MustAbs("/dev/"), Source: check.MustAbs("/dev/"),
Target: check.MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Flags: bits.BindWritable | bits.BindDevice, Flags: comp.BindWritable | comp.BindDevice,
}, "mounting", `"/dev/" flags 0x6`}, }, "mounting", `"/dev/" flags 0x6`},
}) })
} }

View File

@@ -1,7 +1,7 @@
package seccomp_test package seccomp_test
import ( import (
. "hakurei.app/container/bits" . "hakurei.app/container/comp"
. "hakurei.app/container/seccomp" . "hakurei.app/container/seccomp"
) )

View File

@@ -1,7 +1,7 @@
package seccomp_test package seccomp_test
import ( import (
. "hakurei.app/container/bits" . "hakurei.app/container/comp"
. "hakurei.app/container/seccomp" . "hakurei.app/container/seccomp"
) )

View File

@@ -4,14 +4,14 @@ import (
"crypto/sha512" "crypto/sha512"
"encoding/hex" "encoding/hex"
"hakurei.app/container/bits" "hakurei.app/container/comp"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
) )
type ( type (
bpfPreset = struct { bpfPreset = struct {
seccomp.ExportFlag seccomp.ExportFlag
bits.FilterPreset comp.FilterPreset
} }
bpfLookup map[bpfPreset][sha512.Size]byte bpfLookup map[bpfPreset][sha512.Size]byte
) )

View File

@@ -6,7 +6,7 @@ import (
"syscall" "syscall"
"testing" "testing"
. "hakurei.app/container/bits" . "hakurei.app/container/comp"
. "hakurei.app/container/seccomp" . "hakurei.app/container/seccomp"
) )

View File

@@ -5,32 +5,32 @@ package seccomp
import ( import (
. "syscall" . "syscall"
"hakurei.app/container/bits" "hakurei.app/container/comp"
) )
func Preset(presets bits.FilterPreset, flags ExportFlag) (rules []NativeRule) { func Preset(presets comp.FilterPreset, flags ExportFlag) (rules []NativeRule) {
allowedPersonality := PersonaLinux allowedPersonality := PersonaLinux
if presets&bits.PresetLinux32 != 0 { if presets&comp.PresetLinux32 != 0 {
allowedPersonality = PersonaLinux32 allowedPersonality = PersonaLinux32
} }
presetDevelFinal := presetDevel(ScmpDatum(allowedPersonality)) presetDevelFinal := presetDevel(ScmpDatum(allowedPersonality))
l := len(presetCommon) l := len(presetCommon)
if presets&bits.PresetDenyNS != 0 { if presets&comp.PresetDenyNS != 0 {
l += len(presetNamespace) l += len(presetNamespace)
} }
if presets&bits.PresetDenyTTY != 0 { if presets&comp.PresetDenyTTY != 0 {
l += len(presetTTY) l += len(presetTTY)
} }
if presets&bits.PresetDenyDevel != 0 { if presets&comp.PresetDenyDevel != 0 {
l += len(presetDevelFinal) l += len(presetDevelFinal)
} }
if flags&AllowMultiarch == 0 { if flags&AllowMultiarch == 0 {
l += len(presetEmu) l += len(presetEmu)
} }
if presets&bits.PresetExt != 0 { if presets&comp.PresetExt != 0 {
l += len(presetCommonExt) l += len(presetCommonExt)
if presets&bits.PresetDenyNS != 0 { if presets&comp.PresetDenyNS != 0 {
l += len(presetNamespaceExt) l += len(presetNamespaceExt)
} }
if flags&AllowMultiarch == 0 { if flags&AllowMultiarch == 0 {
@@ -40,21 +40,21 @@ func Preset(presets bits.FilterPreset, flags ExportFlag) (rules []NativeRule) {
rules = make([]NativeRule, 0, l) rules = make([]NativeRule, 0, l)
rules = append(rules, presetCommon...) rules = append(rules, presetCommon...)
if presets&bits.PresetDenyNS != 0 { if presets&comp.PresetDenyNS != 0 {
rules = append(rules, presetNamespace...) rules = append(rules, presetNamespace...)
} }
if presets&bits.PresetDenyTTY != 0 { if presets&comp.PresetDenyTTY != 0 {
rules = append(rules, presetTTY...) rules = append(rules, presetTTY...)
} }
if presets&bits.PresetDenyDevel != 0 { if presets&comp.PresetDenyDevel != 0 {
rules = append(rules, presetDevelFinal...) rules = append(rules, presetDevelFinal...)
} }
if flags&AllowMultiarch == 0 { if flags&AllowMultiarch == 0 {
rules = append(rules, presetEmu...) rules = append(rules, presetEmu...)
} }
if presets&bits.PresetExt != 0 { if presets&comp.PresetExt != 0 {
rules = append(rules, presetCommonExt...) rules = append(rules, presetCommonExt...)
if presets&bits.PresetDenyNS != 0 { if presets&comp.PresetDenyNS != 0 {
rules = append(rules, presetNamespaceExt...) rules = append(rules, presetNamespaceExt...)
} }
if flags&AllowMultiarch == 0 { if flags&AllowMultiarch == 0 {

View File

@@ -5,38 +5,29 @@ import (
"unsafe" "unsafe"
) )
// SetPtracer allows processes to ptrace(2) the calling process. // Prctl manipulates various aspects of the behavior of the calling thread or process.
func SetPtracer(pid uintptr) error { func Prctl(op, arg2, arg3 uintptr) error {
_, _, errno := Syscall(SYS_PRCTL, PR_SET_PTRACER, pid, 0) r, _, errno := Syscall(SYS_PRCTL, op, arg2, arg3)
if errno == 0 { if r < 0 {
return nil
}
return errno return errno
} }
return nil
}
// SetPtracer allows processes to ptrace(2) the calling process.
func SetPtracer(pid uintptr) error { return Prctl(PR_SET_PTRACER, pid, 0) }
// linux/sched/coredump.h
const ( const (
SUID_DUMP_DISABLE = iota SUID_DUMP_DISABLE = iota
SUID_DUMP_USER SUID_DUMP_USER
) )
// SetDumpable sets the "dumpable" attribute of the calling process. // SetDumpable sets the "dumpable" attribute of the calling process.
func SetDumpable(dumpable uintptr) error { func SetDumpable(dumpable uintptr) error { return Prctl(PR_SET_DUMPABLE, dumpable, 0) }
// linux/sched/coredump.h
if _, _, errno := Syscall(SYS_PRCTL, PR_SET_DUMPABLE, dumpable, 0); errno != 0 {
return errno
}
return nil
}
// SetNoNewPrivs sets the calling thread's no_new_privs attribute. // SetNoNewPrivs sets the calling thread's no_new_privs attribute.
func SetNoNewPrivs() error { func SetNoNewPrivs() error { return Prctl(PR_SET_NO_NEW_PRIVS, 1, 0) }
_, _, errno := Syscall(SYS_PRCTL, PR_SET_NO_NEW_PRIVS, 1, 0)
if errno == 0 {
return nil
}
return errno
}
// Isatty tests whether a file descriptor refers to a terminal. // Isatty tests whether a file descriptor refers to a terminal.
func Isatty(fd int) bool { func Isatty(fd int) bool {

View File

@@ -2,6 +2,7 @@ package hst
import ( import (
"encoding/json" "encoding/json"
"strings"
"syscall" "syscall"
"time" "time"
@@ -38,9 +39,12 @@ const (
ExitRequest = 254 ExitRequest = 254
) )
// Flags are options held by [ContainerConfig].
type Flags uintptr
const ( const (
// FMultiarch unblocks syscalls required for multiarch to work on applicable targets. // FMultiarch unblocks syscalls required for multiarch to work on applicable targets.
FMultiarch uintptr = 1 << iota FMultiarch Flags = 1 << iota
// FSeccompCompat changes emitted seccomp filter programs to be identical to that of Flatpak. // FSeccompCompat changes emitted seccomp filter programs to be identical to that of Flatpak.
FSeccompCompat FSeccompCompat
@@ -74,6 +78,45 @@ const (
FAll = fMax - 1 FAll = fMax - 1
) )
func (flags Flags) String() string {
switch flags {
case FMultiarch:
return "multiarch"
case FSeccompCompat:
return "compat"
case FDevel:
return "devel"
case FUserns:
return "userns"
case FHostNet:
return "net"
case FHostAbstract:
return "abstract"
case FTty:
return "tty"
case FMapRealUID:
return "mapuid"
case FDevice:
return "device"
case FShareRuntime:
return "runtime"
case FShareTmpdir:
return "tmpdir"
default:
s := make([]string, 0, 1<<4)
for f := Flags(1); f < fMax; f <<= 1 {
if flags&f != 0 {
s = append(s, f.String())
}
}
if len(s) == 0 {
return "none"
}
return strings.Join(s, ", ")
}
}
// ContainerConfig describes the container configuration to be applied to an underlying [container]. // ContainerConfig describes the container configuration to be applied to an underlying [container].
type ContainerConfig struct { type ContainerConfig struct {
// Container UTS namespace hostname. // Container UTS namespace hostname.
@@ -106,7 +149,7 @@ type ContainerConfig struct {
Args []string `json:"args"` Args []string `json:"args"`
// Flags holds boolean options of [ContainerConfig]. // Flags holds boolean options of [ContainerConfig].
Flags uintptr `json:"-"` Flags Flags `json:"-"`
} }
// ContainerConfigF is [ContainerConfig] stripped of its methods. // ContainerConfigF is [ContainerConfig] stripped of its methods.

View File

@@ -3,6 +3,7 @@ package hst_test
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"math"
"reflect" "reflect"
"syscall" "syscall"
"testing" "testing"
@@ -10,6 +11,30 @@ import (
"hakurei.app/hst" "hakurei.app/hst"
) )
func TestFlagsString(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
flags hst.Flags
want string
}{
{"none", 0, "none"},
{"none high", hst.FAll + 1, "none"},
{"all", hst.FAll, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir"},
{"all high", math.MaxUint, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := tc.flags.String(); got != tc.want {
t.Errorf("String(%#b): %q, want %q", tc.flags, got, tc.want)
}
})
}
}
func TestContainerConfig(t *testing.T) { func TestContainerConfig(t *testing.T) {
t.Parallel() t.Parallel()

View File

@@ -4,8 +4,8 @@ import (
"encoding/gob" "encoding/gob"
"strings" "strings"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
) )
@@ -97,16 +97,16 @@ func (b *FSBind) Apply(z *ApplyState) {
} }
var flags int var flags int
if b.Write { if b.Write {
flags |= bits.BindWritable flags |= comp.BindWritable
} }
if b.Device { if b.Device {
flags |= bits.BindDevice | bits.BindWritable flags |= comp.BindDevice | comp.BindWritable
} }
if b.Ensure { if b.Ensure {
flags |= bits.BindEnsure flags |= comp.BindEnsure
} }
if b.Optional { if b.Optional {
flags |= bits.BindOptional flags |= comp.BindOptional
} }
switch { switch {

View File

@@ -4,7 +4,7 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits" "hakurei.app/container/comp"
"hakurei.app/hst" "hakurei.app/hst"
) )
@@ -24,7 +24,7 @@ func TestFSBind(t *testing.T) {
}, true, container.Ops{&container.BindMountOp{ }, true, container.Ops{&container.BindMountOp{
Source: m("/mnt/dev"), Source: m("/mnt/dev"),
Target: m("/dev"), Target: m("/dev"),
Flags: bits.BindWritable | bits.BindDevice | bits.BindOptional, Flags: comp.BindWritable | comp.BindDevice | comp.BindOptional,
}}, m("/dev"), ms("/mnt/dev"), }}, m("/dev"), ms("/mnt/dev"),
"d+/mnt/dev:/dev"}, "d+/mnt/dev:/dev"},
@@ -36,7 +36,7 @@ func TestFSBind(t *testing.T) {
}, true, container.Ops{&container.BindMountOp{ }, true, container.Ops{&container.BindMountOp{
Source: m("/mnt/dev"), Source: m("/mnt/dev"),
Target: m("/dev"), Target: m("/dev"),
Flags: bits.BindWritable | bits.BindDevice | bits.BindEnsure, Flags: comp.BindWritable | comp.BindDevice | comp.BindEnsure,
}}, m("/dev"), ms("/mnt/dev"), }}, m("/dev"), ms("/mnt/dev"),
"d-/mnt/dev:/dev"}, "d-/mnt/dev:/dev"},
@@ -48,7 +48,7 @@ func TestFSBind(t *testing.T) {
}, true, container.Ops{&container.BindMountOp{ }, true, container.Ops{&container.BindMountOp{
Source: m("/mnt/dev"), Source: m("/mnt/dev"),
Target: m("/dev"), Target: m("/dev"),
Flags: bits.BindWritable | bits.BindDevice, Flags: comp.BindWritable | comp.BindDevice,
}}, m("/dev"), ms("/mnt/dev"), }}, m("/dev"), ms("/mnt/dev"),
"d*/mnt/dev:/dev"}, "d*/mnt/dev:/dev"},
@@ -59,7 +59,7 @@ func TestFSBind(t *testing.T) {
}, true, container.Ops{&container.BindMountOp{ }, true, container.Ops{&container.BindMountOp{
Source: m("/mnt/tmp"), Source: m("/mnt/tmp"),
Target: m("/tmp"), Target: m("/tmp"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}}, m("/tmp"), ms("/mnt/tmp"), }}, m("/tmp"), ms("/mnt/tmp"),
"w*/mnt/tmp:/tmp"}, "w*/mnt/tmp:/tmp"},
@@ -98,7 +98,7 @@ func TestFSBind(t *testing.T) {
Special: true, Special: true,
}, true, container.Ops{&container.AutoRootOp{ }, true, container.Ops{&container.AutoRootOp{
Host: m("/"), Host: m("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}}, m("/"), ms("/"), "autoroot:w"}, }}, m("/"), ms("/"), "autoroot:w"},
{"autoroot silly", &hst.FSBind{ {"autoroot silly", &hst.FSBind{
@@ -108,7 +108,7 @@ func TestFSBind(t *testing.T) {
Special: true, Special: true,
}, true, container.Ops{&container.AutoRootOp{ }, true, container.Ops{&container.AutoRootOp{
Host: m("/etc"), Host: m("/etc"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}}, m("/"), ms("/etc"), "autoroot:w:/etc"}, }}, m("/"), ms("/etc"), "autoroot:w:/etc"},
{"autoetc", &hst.FSBind{ {"autoetc", &hst.FSBind{

View File

@@ -18,7 +18,7 @@ func Main(ctx context.Context, msg message.Msg, config *hst.Config) {
log.Fatal(err) log.Fatal(err)
} }
seal := outcome{syscallDispatcher: direct{}} seal := outcome{syscallDispatcher: direct{msg}}
if err := seal.finalise(ctx, msg, &id, config); err != nil { if err := seal.finalise(ctx, msg, &id, config); err != nil {
printMessageError("cannot seal app:", err) printMessageError("cannot seal app:", err)
os.Exit(1) os.Exit(1)

View File

@@ -17,9 +17,10 @@ import (
"time" "time"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/seccomp"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app/state" "hakurei.app/internal/app/state"
"hakurei.app/message" "hakurei.app/message"
@@ -41,9 +42,149 @@ func TestApp(t *testing.T) {
wantSys *system.I wantSys *system.I
wantParams *container.Params wantParams *container.Params
}{ }{
{ {"template", new(stubNixOS), hst.Template(), checkExpectInstanceId, system.New(panicMsgContext{}, message.NewMsg(nil), 1000009).
"nixos permissive defaults no enablements", new(stubNixOS), // spParamsOp
&hst.Config{Container: &hst.ContainerConfig{ Ensure(m("/tmp/hakurei.0"), 0711).
// spRuntimeOp
Ensure(m("/tmp/hakurei.0/runtime"), 0700).
UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute).
Ensure(m("/tmp/hakurei.0/runtime/9"), 0700).
UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/9"), acl.Read, acl.Write, acl.Execute).
// spTmpdirOp
Ensure(m("/tmp/hakurei.0/tmpdir"), 0700).
UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute).
Ensure(m("/tmp/hakurei.0/tmpdir/9"), 01700).
UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/9"), acl.Read, acl.Write, acl.Execute).
// instance
Ephemeral(system.Process, m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 0711).
// spWaylandOp
Wayland(
m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
).
// ensureRuntimeDir
Ensure(m("/run/user/1971/hakurei"), 0700).
UpdatePermType(system.User, m("/run/user/1971/hakurei"), acl.Execute).
Ensure(m("/run/user/1971"), 0700).
UpdatePermType(system.User, m("/run/user/1971"), acl.Execute).
// runtime
Ephemeral(system.Process, m("/run/user/1971/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 0700).
UpdatePerm(m("/run/user/1971/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), acl.Execute).
// spPulseOp
Link(m("/run/user/1971/pulse/native"), m("/run/user/1971/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/pulse")).
// spDBusOp
MustProxyDBus(
hst.Template().SessionBus,
hst.Template().SystemBus, dbus.ProxyPair{
"unix:path=/run/user/1971/bus",
"/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bus",
}, dbus.ProxyPair{
"unix:path=/var/run/dbus/system_bus_socket",
"/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/system_bus_socket",
},
).UpdatePerm(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bus"), acl.Read, acl.Write).
UpdatePerm(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/system_bus_socket"), acl.Read, acl.Write).
// spFilesystemOp
Ensure(m("/var/lib/hakurei/u0"), 0700).
UpdatePermType(system.User, m("/var/lib/hakurei/u0"), acl.Execute).
UpdatePermType(system.User, m("/var/lib/hakurei/u0/org.chromium.Chromium"), acl.Read, acl.Write, acl.Execute), &container.Params{
Dir: m("/data/data/org.chromium.Chromium"),
Env: []string{
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1971/bus",
"DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket",
"GOOGLE_API_KEY=AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
"GOOGLE_DEFAULT_CLIENT_ID=77185425430.apps.googleusercontent.com",
"GOOGLE_DEFAULT_CLIENT_SECRET=OTJgUOQcT7lO7GsGZq2G4IlT",
"HOME=/data/data/org.chromium.Chromium",
"PULSE_COOKIE=/.hakurei/pulse-cookie",
"PULSE_SERVER=unix:/run/user/1971/pulse/native",
"SHELL=/run/current-system/sw/bin/zsh",
"TERM=xterm-256color",
"USER=chronos",
"WAYLAND_DISPLAY=wayland-0",
"XDG_RUNTIME_DIR=/run/user/1971",
"XDG_SESSION_CLASS=user",
"XDG_SESSION_TYPE=wayland",
},
// spParamsOp
Hostname: "localhost",
RetainSession: true,
HostNet: true,
HostAbstract: true,
Path: m("/run/current-system/sw/bin/chromium"),
Args: []string{
"chromium",
"--ignore-gpu-blocklist",
"--disable-smooth-scrolling",
"--enable-features=UseOzonePlatform",
"--ozone-platform=wayland",
},
SeccompFlags: seccomp.AllowMultiarch,
Uid: 1971,
Gid: 100,
Ops: new(container.Ops).
// resolveRoot
Root(m("/var/lib/hakurei/base/org.debian"), comp.BindWritable).
// spParamsOp
Proc(fhs.AbsProc).
Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
Bind(fhs.AbsDev, fhs.AbsDev, comp.BindWritable|comp.BindDevice).
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777).
// spRuntimeOp
Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/tmp/hakurei.0/runtime/9"), m("/run/user/1971"), comp.BindWritable).
// spTmpdirOp
Bind(m("/tmp/hakurei.0/tmpdir/9"), fhs.AbsTmp, comp.BindWritable).
// spAccountOp
Place(m("/etc/passwd"), []byte("chronos:x:1971:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")).
// spWaylandOp
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1971/wayland-0"), 0).
// spPulseOp
Bind(m("/run/user/1971/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/pulse"), m("/run/user/1971/pulse/native"), 0).
Place(m("/.hakurei/pulse-cookie"), bytes.Repeat([]byte{0}, pulseCookieSizeMax)).
// spDBusOp
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bus"), m("/run/user/1971/bus"), 0).
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0).
// spFilesystemOp
Etc(fhs.AbsEtc, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").
Tmpfs(fhs.AbsTmp, 0, 0755).
Overlay(m("/nix/store"),
fhs.AbsVarLib.Append("hakurei/nix/u0/org.chromium.Chromium/rw-store/upper"),
fhs.AbsVarLib.Append("hakurei/nix/u0/org.chromium.Chromium/rw-store/work"),
fhs.AbsVarLib.Append("hakurei/base/org.nixos/ro-store")).
Link(m("/run/current-system"), "/run/current-system", true).
Link(m("/run/opengl-driver"), "/run/opengl-driver", true).
Bind(fhs.AbsVarLib.Append("hakurei/u0/org.chromium.Chromium"),
m("/data/data/org.chromium.Chromium"),
comp.BindWritable|comp.BindEnsure).
Bind(fhs.AbsDev.Append("dri"), fhs.AbsDev.Append("dri"),
comp.BindOptional|comp.BindWritable|comp.BindDevice).
Remount(fhs.AbsRoot, syscall.MS_RDONLY),
}},
{"nixos permissive defaults no enablements", new(stubNixOS), &hst.Config{Container: &hst.ContainerConfig{
Filesystem: []hst.FilesystemConfigJSON{ Filesystem: []hst.FilesystemConfigJSON{
{FilesystemConfig: &hst.FSBind{ {FilesystemConfig: &hst.FSBind{
Target: fhs.AbsRoot, Target: fhs.AbsRoot,
@@ -71,20 +212,22 @@ func TestApp(t *testing.T) {
Args: []string{"/run/current-system/sw/bin/zsh"}, Args: []string{"/run/current-system/sw/bin/zsh"},
Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty | hst.FShareRuntime | hst.FShareTmpdir, Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty | hst.FShareRuntime | hst.FShareTmpdir,
}}, }}, state.ID{
state.ID{
0x4a, 0x45, 0x0b, 0x65, 0x4a, 0x45, 0x0b, 0x65,
0x96, 0xd7, 0xbc, 0x15, 0x96, 0xd7, 0xbc, 0x15,
0xbd, 0x01, 0x78, 0x0e, 0xbd, 0x01, 0x78, 0x0e,
0xb9, 0xa6, 0x07, 0xac, 0xb9, 0xa6, 0x07, 0xac,
}, }, system.New(t.Context(), msg, 1000000).
system.New(t.Context(), msg, 1000000).
Ensure(m("/tmp/hakurei.0"), 0711). Ensure(m("/tmp/hakurei.0"), 0711).
Ensure(m("/tmp/hakurei.0/runtime"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute). Ensure(m("/tmp/hakurei.0/runtime"), 0700).
Ensure(m("/tmp/hakurei.0/runtime/0"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/0"), acl.Read, acl.Write, acl.Execute). UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute).
Ensure(m("/tmp/hakurei.0/tmpdir"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute). Ensure(m("/tmp/hakurei.0/runtime/0"), 0700).
Ensure(m("/tmp/hakurei.0/tmpdir/0"), 01700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/0"), acl.Read, acl.Write, acl.Execute), UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/0"), acl.Read, acl.Write, acl.Execute).
&container.Params{ Ensure(m("/tmp/hakurei.0/tmpdir"), 0700).
UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute).
Ensure(m("/tmp/hakurei.0/tmpdir/0"), 01700).
UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/0"), acl.Read, acl.Write, acl.Execute), &container.Params{
Dir: m("/home/chronos"), Dir: m("/home/chronos"),
Path: m("/run/current-system/sw/bin/zsh"), Path: m("/run/current-system/sw/bin/zsh"),
Args: []string{"/run/current-system/sw/bin/zsh"}, Args: []string{"/run/current-system/sw/bin/zsh"},
@@ -98,33 +241,31 @@ func TestApp(t *testing.T) {
"XDG_SESSION_TYPE=tty", "XDG_SESSION_TYPE=tty",
}, },
Ops: new(container.Ops). Ops: new(container.Ops).
Root(m("/"), bits.BindWritable). Root(m("/"), comp.BindWritable).
Proc(m("/proc/")). Proc(m("/proc/")).
Tmpfs(hst.AbsPrivateTmp, 4096, 0755). Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
DevWritable(m("/dev/"), true). DevWritable(m("/dev/"), true).
Tmpfs(m("/dev/shm"), 0, 01777). Tmpfs(m("/dev/shm"), 0, 01777).
Tmpfs(m("/run/user/"), 4096, 0755). Tmpfs(m("/run/user/"), 4096, 0755).
Bind(m("/tmp/hakurei.0/runtime/0"), m("/run/user/65534"), bits.BindWritable). Bind(m("/tmp/hakurei.0/runtime/0"), m("/run/user/65534"), comp.BindWritable).
Bind(m("/tmp/hakurei.0/tmpdir/0"), m("/tmp/"), bits.BindWritable). Bind(m("/tmp/hakurei.0/tmpdir/0"), m("/tmp/"), comp.BindWritable).
Place(m("/etc/passwd"), []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")). Place(m("/etc/passwd"), []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:65534:\n")). Place(m("/etc/group"), []byte("hakurei:x:65534:\n")).
Bind(m("/dev/kvm"), m("/dev/kvm"), bits.BindWritable|bits.BindDevice|bits.BindOptional). Bind(m("/dev/kvm"), m("/dev/kvm"), comp.BindWritable|comp.BindDevice|comp.BindOptional).
Etc(m("/etc/"), "4a450b6596d7bc15bd01780eb9a607ac"). Etc(m("/etc/"), "4a450b6596d7bc15bd01780eb9a607ac").
Tmpfs(m("/run/user/1971"), 8192, 0755). Tmpfs(m("/run/user/1971"), 8192, 0755).
Tmpfs(m("/run/nscd"), 8192, 0755). Tmpfs(m("/run/nscd"), 8192, 0755).
Tmpfs(m("/run/dbus"), 8192, 0755). Tmpfs(m("/run/dbus"), 8192, 0755).
Remount(m("/dev/"), syscall.MS_RDONLY). Remount(m("/dev/"), syscall.MS_RDONLY).
Remount(m("/"), syscall.MS_RDONLY), Remount(m("/"), syscall.MS_RDONLY),
SeccompPresets: bits.PresetExt | bits.PresetDenyDevel, SeccompPresets: comp.PresetExt | comp.PresetDenyDevel,
HostNet: true, HostNet: true,
HostAbstract: true, HostAbstract: true,
RetainSession: true, RetainSession: true,
ForwardCancel: true, ForwardCancel: true,
}, }},
},
{ {"nixos permissive defaults chromium", new(stubNixOS), &hst.Config{
"nixos permissive defaults chromium", new(stubNixOS),
&hst.Config{
ID: "org.chromium.Chromium", ID: "org.chromium.Chromium",
Identity: 9, Identity: 9,
Groups: []string{"video"}, Groups: []string{"video"},
@@ -195,14 +336,12 @@ func TestApp(t *testing.T) {
Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty | hst.FShareRuntime | hst.FShareTmpdir, Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty | hst.FShareRuntime | hst.FShareTmpdir,
}, },
}, }, state.ID{
state.ID{
0xeb, 0xf0, 0x83, 0xd1, 0xeb, 0xf0, 0x83, 0xd1,
0xb1, 0x75, 0x91, 0x17, 0xb1, 0x75, 0x91, 0x17,
0x82, 0xd4, 0x13, 0x36, 0x82, 0xd4, 0x13, 0x36,
0x9b, 0x64, 0xce, 0x7c, 0x9b, 0x64, 0xce, 0x7c,
}, }, system.New(t.Context(), msg, 1000009).
system.New(t.Context(), msg, 1000009).
Ensure(m("/tmp/hakurei.0"), 0711). Ensure(m("/tmp/hakurei.0"), 0711).
Ensure(m("/tmp/hakurei.0/runtime"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute). Ensure(m("/tmp/hakurei.0/runtime"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute).
Ensure(m("/tmp/hakurei.0/runtime/9"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/9"), acl.Read, acl.Write, acl.Execute). Ensure(m("/tmp/hakurei.0/runtime/9"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/9"), acl.Read, acl.Write, acl.Execute).
@@ -251,8 +390,8 @@ func TestApp(t *testing.T) {
"/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket", "/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket",
}). }).
UpdatePerm(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus"), acl.Read, acl.Write). UpdatePerm(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus"), acl.Read, acl.Write).
UpdatePerm(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), acl.Read, acl.Write), UpdatePerm(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), acl.Read, acl.Write), &container.Params{
&container.Params{
Dir: m("/home/chronos"), Dir: m("/home/chronos"),
Path: m("/run/current-system/sw/bin/zsh"), Path: m("/run/current-system/sw/bin/zsh"),
Args: []string{"zsh", "-c", "exec chromium "}, Args: []string{"zsh", "-c", "exec chromium "},
@@ -271,14 +410,14 @@ func TestApp(t *testing.T) {
"XDG_SESSION_TYPE=wayland", "XDG_SESSION_TYPE=wayland",
}, },
Ops: new(container.Ops). Ops: new(container.Ops).
Root(m("/"), bits.BindWritable). Root(m("/"), comp.BindWritable).
Proc(m("/proc/")). Proc(m("/proc/")).
Tmpfs(hst.AbsPrivateTmp, 4096, 0755). Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
DevWritable(m("/dev/"), true). DevWritable(m("/dev/"), true).
Tmpfs(m("/dev/shm"), 0, 01777). Tmpfs(m("/dev/shm"), 0, 01777).
Tmpfs(m("/run/user/"), 4096, 0755). Tmpfs(m("/run/user/"), 4096, 0755).
Bind(m("/tmp/hakurei.0/runtime/9"), m("/run/user/65534"), bits.BindWritable). Bind(m("/tmp/hakurei.0/runtime/9"), m("/run/user/65534"), comp.BindWritable).
Bind(m("/tmp/hakurei.0/tmpdir/9"), m("/tmp/"), bits.BindWritable). Bind(m("/tmp/hakurei.0/tmpdir/9"), m("/tmp/"), comp.BindWritable).
Place(m("/etc/passwd"), []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")). Place(m("/etc/passwd"), []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:65534:\n")). Place(m("/etc/group"), []byte("hakurei:x:65534:\n")).
Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/65534/wayland-0"), 0). Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/65534/wayland-0"), 0).
@@ -286,25 +425,22 @@ func TestApp(t *testing.T) {
Place(m(hst.PrivateTmp+"/pulse-cookie"), bytes.Repeat([]byte{0}, pulseCookieSizeMax)). Place(m(hst.PrivateTmp+"/pulse-cookie"), bytes.Repeat([]byte{0}, pulseCookieSizeMax)).
Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus"), m("/run/user/65534/bus"), 0). Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus"), m("/run/user/65534/bus"), 0).
Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0). Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0).
Bind(m("/dev/dri"), m("/dev/dri"), bits.BindWritable|bits.BindDevice|bits.BindOptional). Bind(m("/dev/dri"), m("/dev/dri"), comp.BindWritable|comp.BindDevice|comp.BindOptional).
Bind(m("/dev/kvm"), m("/dev/kvm"), bits.BindWritable|bits.BindDevice|bits.BindOptional). Bind(m("/dev/kvm"), m("/dev/kvm"), comp.BindWritable|comp.BindDevice|comp.BindOptional).
Etc(m("/etc/"), "ebf083d1b175911782d413369b64ce7c"). Etc(m("/etc/"), "ebf083d1b175911782d413369b64ce7c").
Tmpfs(m("/run/user/1971"), 8192, 0755). Tmpfs(m("/run/user/1971"), 8192, 0755).
Tmpfs(m("/run/nscd"), 8192, 0755). Tmpfs(m("/run/nscd"), 8192, 0755).
Tmpfs(m("/run/dbus"), 8192, 0755). Tmpfs(m("/run/dbus"), 8192, 0755).
Remount(m("/dev/"), syscall.MS_RDONLY). Remount(m("/dev/"), syscall.MS_RDONLY).
Remount(m("/"), syscall.MS_RDONLY), Remount(m("/"), syscall.MS_RDONLY),
SeccompPresets: bits.PresetExt | bits.PresetDenyDevel, SeccompPresets: comp.PresetExt | comp.PresetDenyDevel,
HostNet: true, HostNet: true,
HostAbstract: true, HostAbstract: true,
RetainSession: true, RetainSession: true,
ForwardCancel: true, ForwardCancel: true,
}, }},
},
{ {"nixos chromium direct wayland", new(stubNixOS), &hst.Config{
"nixos chromium direct wayland", new(stubNixOS),
&hst.Config{
ID: "org.chromium.Chromium", ID: "org.chromium.Chromium",
Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse), Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse),
Container: &hst.ContainerConfig{ Container: &hst.ContainerConfig{
@@ -354,14 +490,12 @@ func TestApp(t *testing.T) {
DirectWayland: true, DirectWayland: true,
Identity: 1, Groups: []string{}, Identity: 1, Groups: []string{},
}, }, state.ID{
state.ID{
0x8e, 0x2c, 0x76, 0xb0, 0x8e, 0x2c, 0x76, 0xb0,
0x66, 0xda, 0xbe, 0x57, 0x66, 0xda, 0xbe, 0x57,
0x4c, 0xf0, 0x73, 0xbd, 0x4c, 0xf0, 0x73, 0xbd,
0xb4, 0x6e, 0xb5, 0xc1, 0xb4, 0x6e, 0xb5, 0xc1,
}, }, system.New(t.Context(), msg, 1000001).
system.New(t.Context(), msg, 1000001).
Ensure(m("/tmp/hakurei.0"), 0711). Ensure(m("/tmp/hakurei.0"), 0711).
Ensure(m("/tmp/hakurei.0/runtime"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute). Ensure(m("/tmp/hakurei.0/runtime"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute).
Ensure(m("/tmp/hakurei.0/runtime/1"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/1"), acl.Read, acl.Write, acl.Execute). Ensure(m("/tmp/hakurei.0/runtime/1"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/1"), acl.Read, acl.Write, acl.Execute).
@@ -401,8 +535,8 @@ func TestApp(t *testing.T) {
"/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", "/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket",
}). }).
UpdatePerm(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), acl.Read, acl.Write). UpdatePerm(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), acl.Read, acl.Write).
UpdatePerm(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket"), acl.Read, acl.Write), UpdatePerm(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket"), acl.Read, acl.Write), &container.Params{
&container.Params{
Uid: 1971, Uid: 1971,
Gid: 100, Gid: 100,
Dir: m("/var/lib/persist/module/hakurei/0/1"), Dir: m("/var/lib/persist/module/hakurei/0/1"),
@@ -428,8 +562,8 @@ func TestApp(t *testing.T) {
DevWritable(m("/dev/"), true). DevWritable(m("/dev/"), true).
Tmpfs(m("/dev/shm"), 0, 01777). Tmpfs(m("/dev/shm"), 0, 01777).
Tmpfs(m("/run/user/"), 4096, 0755). Tmpfs(m("/run/user/"), 4096, 0755).
Bind(m("/tmp/hakurei.0/runtime/1"), m("/run/user/1971"), bits.BindWritable). Bind(m("/tmp/hakurei.0/runtime/1"), m("/run/user/1971"), comp.BindWritable).
Bind(m("/tmp/hakurei.0/tmpdir/1"), m("/tmp/"), bits.BindWritable). Bind(m("/tmp/hakurei.0/tmpdir/1"), m("/tmp/"), comp.BindWritable).
Place(m("/etc/passwd"), []byte("u0_a1:x:1971:100:Hakurei:/var/lib/persist/module/hakurei/0/1:/run/current-system/sw/bin/zsh\n")). Place(m("/etc/passwd"), []byte("u0_a1:x:1971:100:Hakurei:/var/lib/persist/module/hakurei/0/1:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")). Place(m("/etc/group"), []byte("hakurei:x:100:\n")).
Bind(m("/run/user/1971/wayland-0"), m("/run/user/1971/wayland-0"), 0). Bind(m("/run/user/1971/wayland-0"), m("/run/user/1971/wayland-0"), 0).
@@ -441,22 +575,21 @@ func TestApp(t *testing.T) {
Bind(m("/usr/bin/"), m("/usr/bin/"), 0). Bind(m("/usr/bin/"), m("/usr/bin/"), 0).
Bind(m("/nix/store"), m("/nix/store"), 0). Bind(m("/nix/store"), m("/nix/store"), 0).
Bind(m("/run/current-system"), m("/run/current-system"), 0). Bind(m("/run/current-system"), m("/run/current-system"), 0).
Bind(m("/sys/block"), m("/sys/block"), bits.BindOptional). Bind(m("/sys/block"), m("/sys/block"), comp.BindOptional).
Bind(m("/sys/bus"), m("/sys/bus"), bits.BindOptional). Bind(m("/sys/bus"), m("/sys/bus"), comp.BindOptional).
Bind(m("/sys/class"), m("/sys/class"), bits.BindOptional). Bind(m("/sys/class"), m("/sys/class"), comp.BindOptional).
Bind(m("/sys/dev"), m("/sys/dev"), bits.BindOptional). Bind(m("/sys/dev"), m("/sys/dev"), comp.BindOptional).
Bind(m("/sys/devices"), m("/sys/devices"), bits.BindOptional). Bind(m("/sys/devices"), m("/sys/devices"), comp.BindOptional).
Bind(m("/run/opengl-driver"), m("/run/opengl-driver"), 0). Bind(m("/run/opengl-driver"), m("/run/opengl-driver"), 0).
Bind(m("/dev/dri"), m("/dev/dri"), bits.BindDevice|bits.BindWritable|bits.BindOptional). Bind(m("/dev/dri"), m("/dev/dri"), comp.BindDevice|comp.BindWritable|comp.BindOptional).
Etc(m("/etc/"), "8e2c76b066dabe574cf073bdb46eb5c1"). Etc(m("/etc/"), "8e2c76b066dabe574cf073bdb46eb5c1").
Bind(m("/var/lib/persist/module/hakurei/0/1"), m("/var/lib/persist/module/hakurei/0/1"), bits.BindWritable|bits.BindEnsure). Bind(m("/var/lib/persist/module/hakurei/0/1"), m("/var/lib/persist/module/hakurei/0/1"), comp.BindWritable|comp.BindEnsure).
Remount(m("/dev/"), syscall.MS_RDONLY). Remount(m("/dev/"), syscall.MS_RDONLY).
Remount(m("/"), syscall.MS_RDONLY), Remount(m("/"), syscall.MS_RDONLY),
SeccompPresets: bits.PresetExt | bits.PresetDenyTTY | bits.PresetDenyDevel, SeccompPresets: comp.PresetExt | comp.PresetDenyTTY | comp.PresetDenyDevel,
HostNet: true, HostNet: true,
ForwardCancel: true, ForwardCancel: true,
}, }},
},
} }
for _, tc := range testCases { for _, tc := range testCases {
@@ -574,10 +707,9 @@ func (s stubOsFileReadCloser) Stat() (fs.FileInfo, error) { panic("attempting to
type stubNixOS struct { type stubNixOS struct {
usernameErr map[string]error usernameErr map[string]error
panicDispatcher
} }
func (k *stubNixOS) new(func(k syscallDispatcher)) { panic("not implemented") }
func (k *stubNixOS) getpid() int { return 0xdeadbeef } func (k *stubNixOS) getpid() int { return 0xdeadbeef }
func (k *stubNixOS) getuid() int { return 1971 } func (k *stubNixOS) getuid() int { return 1971 }
func (k *stubNixOS) getgid() int { return 100 } func (k *stubNixOS) getgid() int { return 100 }
@@ -659,6 +791,10 @@ func (k *stubNixOS) readdir(name string) ([]fs.DirEntry, error) {
"tmpfiles.d", "udev", "udisks2", "UPower", "vconsole.conf", "X11", "zfs", "zinputrc", "tmpfiles.d", "udev", "udisks2", "UPower", "vconsole.conf", "X11", "zfs", "zinputrc",
"zoneinfo", "zprofile", "zshenv", "zshrc") "zoneinfo", "zprofile", "zshenv", "zshrc")
case "/var/lib/hakurei/base/org.debian":
return stubDirEntries("bin", "dev", "etc", "home", "lib64", "lost+found",
"mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var")
default: default:
panic(fmt.Sprintf("attempted to read unexpected directory %q", name)) panic(fmt.Sprintf("attempted to read unexpected directory %q", name))
} }
@@ -726,6 +862,38 @@ func (k *stubNixOS) evalSymlinks(path string) (string, error) {
return "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-graphics-drivers", nil return "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-graphics-drivers", nil
case "/var/lib/persist/module/hakurei/0/1": case "/var/lib/persist/module/hakurei/0/1":
return "/var/lib/persist/module/hakurei/0/1", nil return "/var/lib/persist/module/hakurei/0/1", nil
case "/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/upper":
return "/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/upper", nil
case "/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/work":
return "/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/work", nil
case "/var/lib/hakurei/base/org.nixos/ro-store":
return "/var/lib/hakurei/base/org.nixos/ro-store", nil
case "/var/lib/hakurei/u0/org.chromium.Chromium":
return "/var/lib/hakurei/u0/org.chromium.Chromium", nil
case "/var/lib/hakurei/base/org.debian/bin":
return "/var/lib/hakurei/base/org.debian/bin", nil
case "/var/lib/hakurei/base/org.debian/home":
return "/var/lib/hakurei/base/org.debian/home", nil
case "/var/lib/hakurei/base/org.debian/lib64":
return "/var/lib/hakurei/base/org.debian/lib64", nil
case "/var/lib/hakurei/base/org.debian/lost+found":
return "/var/lib/hakurei/base/org.debian/lost+found", nil
case "/var/lib/hakurei/base/org.debian/nix":
return "/var/lib/hakurei/base/org.debian/nix", nil
case "/var/lib/hakurei/base/org.debian/root":
return "/var/lib/hakurei/base/org.debian/root", nil
case "/var/lib/hakurei/base/org.debian/run":
return "/var/lib/hakurei/base/org.debian/run", nil
case "/var/lib/hakurei/base/org.debian/srv":
return "/var/lib/hakurei/base/org.debian/srv", nil
case "/var/lib/hakurei/base/org.debian/sys":
return "/var/lib/hakurei/base/org.debian/sys", nil
case "/var/lib/hakurei/base/org.debian/usr":
return "/var/lib/hakurei/base/org.debian/usr", nil
case "/var/lib/hakurei/base/org.debian/var":
return "/var/lib/hakurei/base/org.debian/var", nil
default: default:
panic(fmt.Sprintf("attempted to evaluate unexpected path %q", path)) panic(fmt.Sprintf("attempted to evaluate unexpected path %q", path))
} }

View File

@@ -1,16 +1,18 @@
package app package app
import ( import (
"context"
"io" "io"
"io/fs" "io/fs"
"log"
"os" "os"
"os/exec" "os/exec"
"os/signal"
"os/user" "os/user"
"path/filepath" "path/filepath"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/seccomp"
"hakurei.app/internal" "hakurei.app/internal"
"hakurei.app/message" "hakurei.app/message"
"hakurei.app/system/dbus" "hakurei.app/system/dbus"
@@ -28,7 +30,7 @@ type syscallDispatcher interface {
// new starts a goroutine with a new instance of syscallDispatcher. // new starts a goroutine with a new instance of syscallDispatcher.
// A syscallDispatcher must never be used in any goroutine other than the one owning it, // A syscallDispatcher must never be used in any goroutine other than the one owning it,
// just synchronising access is not enough, as this is for test instrumentation. // just synchronising access is not enough, as this is for test instrumentation.
new(f func(k syscallDispatcher)) new(f func(k syscallDispatcher, msg message.Msg))
// getpid provides [os.Getpid]. // getpid provides [os.Getpid].
getpid() int getpid() int
@@ -38,6 +40,8 @@ type syscallDispatcher interface {
getgid() int getgid() int
// lookupEnv provides [os.LookupEnv]. // lookupEnv provides [os.LookupEnv].
lookupEnv(key string) (string, bool) lookupEnv(key string) (string, bool)
// pipe provides os.Pipe.
pipe() (r, w *os.File, err error)
// stat provides [os.Stat]. // stat provides [os.Stat].
stat(name string) (os.FileInfo, error) stat(name string) (os.FileInfo, error)
// open provides [os.Open]. // open provides [os.Open].
@@ -46,6 +50,8 @@ type syscallDispatcher interface {
readdir(name string) ([]os.DirEntry, error) readdir(name string) ([]os.DirEntry, error)
// tempdir provides [os.TempDir]. // tempdir provides [os.TempDir].
tempdir() string tempdir() string
// exit provides [os.Exit].
exit(code int)
// evalSymlinks provides [filepath.EvalSymlinks]. // evalSymlinks provides [filepath.EvalSymlinks].
evalSymlinks(path string) (string, error) evalSymlinks(path string) (string, error)
@@ -56,10 +62,29 @@ type syscallDispatcher interface {
// cmdOutput provides the Output method of [exec.Cmd]. // cmdOutput provides the Output method of [exec.Cmd].
cmdOutput(cmd *exec.Cmd) ([]byte, error) cmdOutput(cmd *exec.Cmd) ([]byte, error)
// notifyContext provides [signal.NotifyContext].
notifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc)
// prctl provides [container.Prctl].
prctl(op, arg2, arg3 uintptr) error
// overflowUid provides [container.OverflowUid]. // overflowUid provides [container.OverflowUid].
overflowUid(msg message.Msg) int overflowUid(msg message.Msg) int
// overflowGid provides [container.OverflowGid]. // overflowGid provides [container.OverflowGid].
overflowGid(msg message.Msg) int overflowGid(msg message.Msg) int
// setDumpable provides [container.SetDumpable].
setDumpable(dumpable uintptr) error
// receive provides [container.Receive].
receive(key string, e any, fdp *uintptr) (closeFunc func() error, err error)
// containerStart provides the Start method of [container.Container].
containerStart(z *container.Container) error
// containerStart provides the Serve method of [container.Container].
containerServe(z *container.Container) error
// containerStart provides the Wait method of [container.Container].
containerWait(z *container.Container) error
// seccompLoad provides [seccomp.Load].
seccompLoad(rules []seccomp.NativeRule, flags seccomp.ExportFlag) error
// mustHsuPath provides [internal.MustHsuPath]. // mustHsuPath provides [internal.MustHsuPath].
mustHsuPath() *check.Absolute mustHsuPath() *check.Absolute
@@ -67,23 +92,32 @@ type syscallDispatcher interface {
// dbusAddress provides [dbus.Address]. // dbusAddress provides [dbus.Address].
dbusAddress() (session, system string) dbusAddress() (session, system string)
// setupContSignal provides setupContSignal.
setupContSignal(pid int) (io.ReadCloser, func(), error)
// getMsg returns the [message.Msg] held by syscallDispatcher.
getMsg() message.Msg
// fatal provides [log.Fatal].
fatal(v ...any)
// fatalf provides [log.Fatalf]. // fatalf provides [log.Fatalf].
fatalf(format string, v ...any) fatalf(format string, v ...any)
} }
// direct implements syscallDispatcher on the current kernel. // direct implements syscallDispatcher on the current kernel.
type direct struct{} type direct struct{ msg message.Msg }
func (k direct) new(f func(k syscallDispatcher)) { go f(k) } func (k direct) new(f func(k syscallDispatcher, msg message.Msg)) { go f(k, k.msg) }
func (direct) getpid() int { return os.Getpid() } func (direct) getpid() int { return os.Getpid() }
func (direct) getuid() int { return os.Getuid() } func (direct) getuid() int { return os.Getuid() }
func (direct) getgid() int { return os.Getgid() } func (direct) getgid() int { return os.Getgid() }
func (direct) lookupEnv(key string) (string, bool) { return os.LookupEnv(key) } func (direct) lookupEnv(key string) (string, bool) { return os.LookupEnv(key) }
func (direct) pipe() (r, w *os.File, err error) { return os.Pipe() }
func (direct) stat(name string) (os.FileInfo, error) { return os.Stat(name) } func (direct) stat(name string) (os.FileInfo, error) { return os.Stat(name) }
func (direct) open(name string) (osFile, error) { return os.Open(name) } func (direct) open(name string) (osFile, error) { return os.Open(name) }
func (direct) readdir(name string) ([]os.DirEntry, error) { return os.ReadDir(name) } func (direct) readdir(name string) ([]os.DirEntry, error) { return os.ReadDir(name) }
func (direct) tempdir() string { return os.TempDir() } func (direct) tempdir() string { return os.TempDir() }
func (direct) exit(code int) { os.Exit(code) }
func (direct) evalSymlinks(path string) (string, error) { return filepath.EvalSymlinks(path) } func (direct) evalSymlinks(path string) (string, error) { return filepath.EvalSymlinks(path) }
@@ -98,11 +132,32 @@ func (direct) lookupGroupId(name string) (gid string, err error) {
func (direct) cmdOutput(cmd *exec.Cmd) ([]byte, error) { return cmd.Output() } func (direct) cmdOutput(cmd *exec.Cmd) ([]byte, error) { return cmd.Output() }
func (direct) notifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) {
return signal.NotifyContext(parent, signals...)
}
func (direct) prctl(op, arg2, arg3 uintptr) error { return container.Prctl(op, arg2, arg3) }
func (direct) overflowUid(msg message.Msg) int { return container.OverflowUid(msg) } func (direct) overflowUid(msg message.Msg) int { return container.OverflowUid(msg) }
func (direct) overflowGid(msg message.Msg) int { return container.OverflowGid(msg) } func (direct) overflowGid(msg message.Msg) int { return container.OverflowGid(msg) }
func (direct) setDumpable(dumpable uintptr) error { return container.SetDumpable(dumpable) }
func (direct) receive(key string, e any, fdp *uintptr) (func() error, error) {
return container.Receive(key, e, fdp)
}
func (direct) containerStart(z *container.Container) error { return z.Start() }
func (direct) containerServe(z *container.Container) error { return z.Serve() }
func (direct) containerWait(z *container.Container) error { return z.Wait() }
func (direct) seccompLoad(rules []seccomp.NativeRule, flags seccomp.ExportFlag) error {
return seccomp.Load(rules, flags)
}
func (direct) mustHsuPath() *check.Absolute { return internal.MustHsuPath() } func (direct) mustHsuPath() *check.Absolute { return internal.MustHsuPath() }
func (k direct) dbusAddress() (session, system string) { return dbus.Address() } func (direct) dbusAddress() (session, system string) { return dbus.Address() }
func (direct) fatalf(format string, v ...any) { log.Fatalf(format, v...) } func (direct) setupContSignal(pid int) (io.ReadCloser, func(), error) { return setupContSignal(pid) }
func (k direct) getMsg() message.Msg { return k.msg }
func (k direct) fatal(v ...any) { k.msg.GetLogger().Fatal(v...) }
func (k direct) fatalf(format string, v ...any) { k.msg.GetLogger().Fatalf(format, v...) }

View File

@@ -2,6 +2,7 @@ package app
import ( import (
"bytes" "bytes"
"context"
"io" "io"
"io/fs" "io/fs"
"log" "log"
@@ -10,11 +11,14 @@ import (
"os/exec" "os/exec"
"reflect" "reflect"
"slices" "slices"
"sync"
"testing" "testing"
"time" "time"
"unsafe"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/seccomp"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app/state" "hakurei.app/internal/app/state"
@@ -312,6 +316,10 @@ type kstub struct {
*stub.Stub[syscallDispatcher] *stub.Stub[syscallDispatcher]
} }
func (k *kstub) new(f func(k syscallDispatcher, msg message.Msg)) {
k.New(func(k syscallDispatcher) { f(k, k.(*kstub)) })
}
func (k *kstub) getpid() int { k.Helper(); return k.Expects("getpid").Ret.(int) } func (k *kstub) getpid() int { k.Helper(); return k.Expects("getpid").Ret.(int) }
func (k *kstub) getuid() int { k.Helper(); return k.Expects("getuid").Ret.(int) } func (k *kstub) getuid() int { k.Helper(); return k.Expects("getuid").Ret.(int) }
func (k *kstub) getgid() int { k.Helper(); return k.Expects("getgid").Ret.(int) } func (k *kstub) getgid() int { k.Helper(); return k.Expects("getgid").Ret.(int) }
@@ -353,6 +361,61 @@ func (k *kstub) evalSymlinks(path string) (string, error) {
stub.CheckArg(k.Stub, "path", path, 0)) stub.CheckArg(k.Stub, "path", path, 0))
} }
func (k *kstub) prctl(op, arg2, arg3 uintptr) error {
k.Helper()
return k.Expects("prctl").Error(
stub.CheckArg(k.Stub, "op", op, 0),
stub.CheckArg(k.Stub, "arg2", arg2, 1),
stub.CheckArg(k.Stub, "arg3", arg3, 2))
}
func (k *kstub) setDumpable(dumpable uintptr) error {
k.Helper()
return k.Expects("setDumpable").Error(
stub.CheckArg(k.Stub, "dumpable", dumpable, 0))
}
func (k *kstub) receive(key string, e any, fdp *uintptr) (closeFunc func() error, err error) {
k.Helper()
expect := k.Expects("receive")
reflect.ValueOf(e).Elem().Set(reflect.ValueOf(expect.Args[1]))
if expect.Args[2] != nil {
*fdp = expect.Args[2].(uintptr)
}
return func() error { return k.Expects("closeReceive").Err }, expect.Error(
stub.CheckArg(k.Stub, "key", key, 0))
}
func (k *kstub) expectCheckContainer(expect *stub.Call, z *container.Container) error {
k.Helper()
err := expect.Error(
stub.CheckArgReflect(k.Stub, "params", &z.Params, 0))
if err != nil {
k.Errorf("params:\n%s\n%s", mustMarshal(&z.Params), mustMarshal(expect.Args[0]))
}
return err
}
func (k *kstub) containerStart(z *container.Container) error {
k.Helper()
return k.expectCheckContainer(k.Expects("containerStart"), z)
}
func (k *kstub) containerServe(z *container.Container) error {
k.Helper()
return k.expectCheckContainer(k.Expects("containerServe"), z)
}
func (k *kstub) containerWait(z *container.Container) error {
k.Helper()
return k.expectCheckContainer(k.Expects("containerWait"), z)
}
func (k *kstub) seccompLoad(rules []seccomp.NativeRule, flags seccomp.ExportFlag) error {
k.Helper()
return k.Expects("seccompLoad").Error(
stub.CheckArgReflect(k.Stub, "rules", rules, 0),
stub.CheckArg(k.Stub, "flags", flags, 1))
}
func (k *kstub) cmdOutput(cmd *exec.Cmd) ([]byte, error) { func (k *kstub) cmdOutput(cmd *exec.Cmd) ([]byte, error) {
k.Helper() k.Helper()
expect := k.Expects("cmdOutput") expect := k.Expects("cmdOutput")
@@ -363,6 +426,16 @@ func (k *kstub) cmdOutput(cmd *exec.Cmd) ([]byte, error) {
stub.CheckArg(k.Stub, "cmd.Dir", cmd.Dir, 3)) stub.CheckArg(k.Stub, "cmd.Dir", cmd.Dir, 3))
} }
func (k *kstub) notifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) {
k.Helper()
if k.Expects("notifyContext").Error(
stub.CheckArgReflect(k.Stub, "parent", parent, 0),
stub.CheckArgReflect(k.Stub, "signals", signals, 1)) != nil {
k.FailNow()
}
return k.Context(), func() { k.Helper(); k.Expects("notifyContextStop") }
}
func (k *kstub) mustHsuPath() *check.Absolute { func (k *kstub) mustHsuPath() *check.Absolute {
k.Helper() k.Helper()
return k.Expects("mustHsuPath").Ret.(*check.Absolute) return k.Expects("mustHsuPath").Ret.(*check.Absolute)
@@ -374,8 +447,51 @@ func (k *kstub) dbusAddress() (session, system string) {
return ret[0], ret[1] return ret[0], ret[1]
} }
func (k *kstub) GetLogger() *log.Logger { panic("unreachable") } // stubTrackReader embeds kstub but switches the underlying [stub.Stub] index to sub on its first Read.
// The resulting kstub does not share any state with the instance passed to the instrumented goroutine.
// Therefore, any method making use of such must not be called.
type stubTrackReader struct {
sub int
subOnce sync.Once
*kstub
}
func (r *stubTrackReader) Read(p []byte) (n int, err error) {
r.subOnce.Do(func() {
subVal := reflect.ValueOf(r.kstub.Stub).Elem().FieldByName("sub")
r.kstub = &kstub{panicDispatcher{}, reflect.
NewAt(subVal.Type(), unsafe.Pointer(subVal.UnsafeAddr())).Elem().
Interface().([]*stub.Stub[syscallDispatcher])[r.sub]}
})
return r.kstub.Read(p)
}
func (k *kstub) setupContSignal(pid int) (io.ReadCloser, func(), error) {
k.Helper()
expect := k.Expects("setupContSignal")
return &stubTrackReader{sub: expect.Ret.(int), kstub: k}, func() { k.Expects("wKeepAlive") }, expect.Error(
stub.CheckArg(k.Stub, "pid", pid, 0))
}
func (k *kstub) getMsg() message.Msg { k.Helper(); k.Expects("getMsg"); return k }
func (k *kstub) Close() error { k.Helper(); return k.Expects("rcClose").Err }
func (k *kstub) Read(p []byte) (n int, err error) {
k.Helper()
expect := k.Expects("rcRead")
// special case to terminate exit outcomes goroutine
// to proceed with further testing of the entrypoint
if expect.Ret == nil {
panic(stub.PanicExit)
}
return copy(p, expect.Ret.([]byte)), expect.Err
}
func (k *kstub) GetLogger() *log.Logger { k.Helper(); return k.Expects("getLogger").Ret.(*log.Logger) }
func (k *kstub) IsVerbose() bool { k.Helper(); return k.Expects("isVerbose").Ret.(bool) } func (k *kstub) IsVerbose() bool { k.Helper(); return k.Expects("isVerbose").Ret.(bool) }
func (k *kstub) SwapVerbose(verbose bool) bool { func (k *kstub) SwapVerbose(verbose bool) bool {
k.Helper() k.Helper()
@@ -492,20 +608,38 @@ func (panicMsgContext) Value(any) any { panic("unreachable") }
// This type is meant to be embedded in partial syscallDispatcher implementations. // This type is meant to be embedded in partial syscallDispatcher implementations.
type panicDispatcher struct{} type panicDispatcher struct{}
func (panicDispatcher) new(func(k syscallDispatcher)) { panic("unreachable") } func (panicDispatcher) new(func(k syscallDispatcher, msg message.Msg)) { panic("unreachable") }
func (panicDispatcher) getpid() int { panic("unreachable") } func (panicDispatcher) getpid() int { panic("unreachable") }
func (panicDispatcher) getuid() int { panic("unreachable") } func (panicDispatcher) getuid() int { panic("unreachable") }
func (panicDispatcher) getgid() int { panic("unreachable") } func (panicDispatcher) getgid() int { panic("unreachable") }
func (panicDispatcher) lookupEnv(string) (string, bool) { panic("unreachable") } func (panicDispatcher) lookupEnv(string) (string, bool) { panic("unreachable") }
func (panicDispatcher) pipe() (*os.File, *os.File, error) { panic("unreachable") }
func (panicDispatcher) stat(string) (os.FileInfo, error) { panic("unreachable") } func (panicDispatcher) stat(string) (os.FileInfo, error) { panic("unreachable") }
func (panicDispatcher) open(string) (osFile, error) { panic("unreachable") } func (panicDispatcher) open(string) (osFile, error) { panic("unreachable") }
func (panicDispatcher) readdir(string) ([]os.DirEntry, error) { panic("unreachable") } func (panicDispatcher) readdir(string) ([]os.DirEntry, error) { panic("unreachable") }
func (panicDispatcher) tempdir() string { panic("unreachable") } func (panicDispatcher) tempdir() string { panic("unreachable") }
func (panicDispatcher) exit(int) { panic("unreachable") }
func (panicDispatcher) evalSymlinks(string) (string, error) { panic("unreachable") } func (panicDispatcher) evalSymlinks(string) (string, error) { panic("unreachable") }
func (panicDispatcher) prctl(uintptr, uintptr, uintptr) error { panic("unreachable") }
func (panicDispatcher) lookupGroupId(string) (string, error) { panic("unreachable") } func (panicDispatcher) lookupGroupId(string) (string, error) { panic("unreachable") }
func (panicDispatcher) cmdOutput(*exec.Cmd) ([]byte, error) { panic("unreachable") } func (panicDispatcher) cmdOutput(*exec.Cmd) ([]byte, error) { panic("unreachable") }
func (panicDispatcher) overflowUid(message.Msg) int { panic("unreachable") } func (panicDispatcher) overflowUid(message.Msg) int { panic("unreachable") }
func (panicDispatcher) overflowGid(message.Msg) int { panic("unreachable") } func (panicDispatcher) overflowGid(message.Msg) int { panic("unreachable") }
func (panicDispatcher) setDumpable(uintptr) error { panic("unreachable") }
func (panicDispatcher) receive(string, any, *uintptr) (func() error, error) { panic("unreachable") }
func (panicDispatcher) containerStart(*container.Container) error { panic("unreachable") }
func (panicDispatcher) containerServe(*container.Container) error { panic("unreachable") }
func (panicDispatcher) containerWait(*container.Container) error { panic("unreachable") }
func (panicDispatcher) mustHsuPath() *check.Absolute { panic("unreachable") } func (panicDispatcher) mustHsuPath() *check.Absolute { panic("unreachable") }
func (panicDispatcher) dbusAddress() (string, string) { panic("unreachable") } func (panicDispatcher) dbusAddress() (string, string) { panic("unreachable") }
func (panicDispatcher) setupContSignal(int) (io.ReadCloser, func(), error) { panic("unreachable") }
func (panicDispatcher) getMsg() message.Msg { panic("unreachable") }
func (panicDispatcher) fatal(...any) { panic("unreachable") }
func (panicDispatcher) fatalf(string, ...any) { panic("unreachable") } func (panicDispatcher) fatalf(string, ...any) { panic("unreachable") }
func (panicDispatcher) notifyContext(context.Context, ...os.Signal) (context.Context, context.CancelFunc) {
panic("unreachable")
}
func (panicDispatcher) seccompLoad([]seccomp.NativeRule, seccomp.ExportFlag) error {
panic("unreachable")
}

View File

@@ -21,6 +21,8 @@ type Hsu struct {
id int id int
kOnce sync.Once kOnce sync.Once
// msg is not populated
k syscallDispatcher k syscallDispatcher
} }

View File

@@ -7,14 +7,13 @@ import (
"log" "log"
"os" "os"
"os/exec" "os/exec"
"os/signal"
"runtime" "runtime"
"sync/atomic" "sync/atomic"
"syscall" "syscall"
"time" "time"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits" "hakurei.app/container/comp"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/message" "hakurei.app/message"
@@ -23,6 +22,20 @@ import (
//#include "shim-signal.h" //#include "shim-signal.h"
import "C" import "C"
// setupContSignal sets up the SIGCONT signal handler for the cross-uid shim exit hack.
// The signal handler is implemented in C, signals can be processed by reading from the returned reader.
// The returned function must be called after all signal processing concludes.
func setupContSignal(pid int) (io.ReadCloser, func(), error) {
if r, w, err := os.Pipe(); err != nil {
return nil, nil, err
} else if _, err = C.hakurei_shim_setup_cont_signal(C.pid_t(pid), C.int(w.Fd())); err != nil {
_, _ = r.Close(), w.Close()
return nil, nil, err
} else {
return r, func() { runtime.KeepAlive(w) }, nil
}
}
// shimEnv is the name of the environment variable storing decimal representation of // shimEnv is the name of the environment variable storing decimal representation of
// setup pipe fd for [container.Receive]. // setup pipe fd for [container.Receive].
const shimEnv = "HAKUREI_SHIM" const shimEnv = "HAKUREI_SHIM"
@@ -46,76 +59,102 @@ type shimParams struct {
// valid checks shimParams to be safe for use. // valid checks shimParams to be safe for use.
func (p *shimParams) valid() bool { return p != nil && p.PrivPID > 0 } func (p *shimParams) valid() bool { return p != nil && p.PrivPID > 0 }
// ShimMain is the main function of the shim process and runs as the unconstrained target user. // shimName is the prefix used by log.std in the shim process.
func ShimMain() { const shimName = "shim"
log.SetPrefix("shim: ")
log.SetFlags(0)
msg := message.NewMsg(log.Default())
if err := container.SetDumpable(container.SUID_DUMP_DISABLE); err != nil { // Shim is called by the main function of the shim process and runs as the unconstrained target user.
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err) // Shim does not return.
func Shim(msg message.Msg) {
if msg == nil {
msg = message.NewMsg(log.Default())
}
shimEntrypoint(direct{msg})
}
func shimEntrypoint(k syscallDispatcher) {
msg := k.getMsg()
if msg == nil {
panic("attempting to call shimEntrypoint with nil msg")
} else if logger := msg.GetLogger(); logger != nil {
logger.SetPrefix(shimName + ": ")
logger.SetFlags(0)
}
if err := k.setDumpable(container.SUID_DUMP_DISABLE); err != nil {
k.fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
} }
var ( var (
state outcomeState state outcomeState
closeSetup func() error closeSetup func() error
) )
if f, err := container.Receive(shimEnv, &state, nil); err != nil { if f, err := k.receive(shimEnv, &state, nil); err != nil {
if errors.Is(err, syscall.EBADF) { if errors.Is(err, syscall.EBADF) {
log.Fatal("invalid config descriptor") k.fatal("invalid config descriptor")
} }
if errors.Is(err, container.ErrReceiveEnv) { if errors.Is(err, container.ErrReceiveEnv) {
log.Fatal(shimEnv + " not set") k.fatal(shimEnv + " not set")
} }
log.Fatalf("cannot receive shim setup params: %v", err) k.fatalf("cannot receive shim setup params: %v", err)
} else { } else {
msg.SwapVerbose(state.Shim.Verbose) msg.SwapVerbose(state.Shim.Verbose)
closeSetup = f closeSetup = f
if err = state.populateLocal(direct{}, msg); err != nil { if err = state.populateLocal(k, msg); err != nil {
if m, ok := message.GetMessage(err); ok { if m, ok := message.GetMessage(err); ok {
log.Fatal(m) k.fatal(m)
} else { } else {
log.Fatalf("cannot populate local state: %v", err) k.fatalf("cannot populate local state: %v", err)
} }
} }
} }
// the Go runtime does not expose siginfo_t so SIGCONT is handled in C to check si_pid // the Go runtime does not expose siginfo_t so SIGCONT is handled in C to check si_pid
var signalPipe io.ReadCloser var signalPipe io.ReadCloser
if r, w, err := os.Pipe(); err != nil { if r, wKeepAlive, err := k.setupContSignal(state.Shim.PrivPID); err != nil {
log.Fatalf("cannot pipe: %v", err) switch {
} else if _, err = C.hakurei_shim_setup_cont_signal(C.pid_t(state.Shim.PrivPID), C.int(w.Fd())); err != nil { case errors.As(err, new(*os.SyscallError)): // returned by os.Pipe
log.Fatalf("cannot install SIGCONT handler: %v", err) k.fatal(err.Error())
return
case errors.As(err, new(syscall.Errno)): // returned by hakurei_shim_setup_cont_signal
k.fatalf("cannot install SIGCONT handler: %v", err)
return
default: // unreachable
k.fatalf("cannot set up exit request: %v", err)
return
}
} else { } else {
defer runtime.KeepAlive(w) defer wKeepAlive()
signalPipe = r signalPipe = r
} }
// pdeath_signal delivery is checked as if the dying process called kill(2), see kernel/exit.c // pdeath_signal delivery is checked as if the dying process called kill(2), see kernel/exit.c
if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGCONT), 0); errno != 0 { if err := k.prctl(syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGCONT), 0); err != nil {
log.Fatalf("cannot set parent-death signal: %v", errno) k.fatalf("cannot set parent-death signal: %v", err)
} }
stateParams := state.newParams() stateParams := state.newParams()
for _, op := range state.Shim.Ops { for _, op := range state.Shim.Ops {
if err := op.toContainer(stateParams); err != nil { if err := op.toContainer(stateParams); err != nil {
if m, ok := message.GetMessage(err); ok { if m, ok := message.GetMessage(err); ok {
log.Fatal(m) k.fatal(m)
} else { } else {
log.Fatalf("cannot create container state: %v", err) k.fatalf("cannot create container state: %v", err)
} }
} }
} }
// shim exit outcomes // shim exit outcomes
var cancelContainer atomic.Pointer[context.CancelFunc] var cancelContainer atomic.Pointer[context.CancelFunc]
go func() { k.new(func(k syscallDispatcher, msg message.Msg) {
buf := make([]byte, 1) buf := make([]byte, 1)
for { for {
if _, err := signalPipe.Read(buf); err != nil { if _, err := signalPipe.Read(buf); err != nil {
log.Fatalf("cannot read from signal pipe: %v", err) k.fatalf("cannot read from signal pipe: %v", err)
} }
switch buf[0] { switch buf[0] {
@@ -128,37 +167,37 @@ func ShimMain() {
// setup has not completed, terminate immediately // setup has not completed, terminate immediately
msg.Resume() msg.Resume()
os.Exit(hst.ExitRequest) k.exit(hst.ExitRequest)
return return
case 1: // got SIGCONT after adoption: monitor died before delivering signal case 1: // got SIGCONT after adoption: monitor died before delivering signal
msg.BeforeExit() msg.BeforeExit()
os.Exit(hst.ExitOrphan) k.exit(hst.ExitOrphan)
return return
case 2: // unreachable case 2: // unreachable
log.Println("sa_sigaction got invalid siginfo") msg.Verbose("sa_sigaction got invalid siginfo")
case 3: // got SIGCONT from unexpected process: hopefully the terminal driver case 3: // got SIGCONT from unexpected process: hopefully the terminal driver
log.Println("got SIGCONT from unexpected process") msg.Verbose("got SIGCONT from unexpected process")
default: // unreachable default: // unreachable
log.Fatalf("got invalid message %d from signal handler", buf[0]) k.fatalf("got invalid message %d from signal handler", buf[0])
} }
} }
}() })
if stateParams.params.Ops == nil { if stateParams.params.Ops == nil {
log.Fatal("invalid container params") k.fatal("invalid container params")
} }
// close setup socket // close setup socket
if err := closeSetup(); err != nil { if err := closeSetup(); err != nil {
log.Printf("cannot close setup pipe: %v", err) msg.Verbosef("cannot close setup pipe: %v", err)
// not fatal // not fatal
} }
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) ctx, stop := k.notifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
cancelContainer.Store(&stop) cancelContainer.Store(&stop)
z := container.New(ctx, msg) z := container.New(ctx, msg)
z.Params = *stateParams.params z.Params = *stateParams.params
@@ -167,30 +206,30 @@ func ShimMain() {
// bounds and default enforced in finalise.go // bounds and default enforced in finalise.go
z.WaitDelay = state.Shim.WaitDelay z.WaitDelay = state.Shim.WaitDelay
if err := z.Start(); err != nil { if err := k.containerStart(z); err != nil {
printMessageError("cannot start container:", err) printMessageError("cannot start container:", err)
os.Exit(hst.ExitFailure) k.exit(hst.ExitFailure)
} }
if err := z.Serve(); err != nil { if err := k.containerServe(z); err != nil {
printMessageError("cannot configure container:", err) printMessageError("cannot configure container:", err)
} }
if err := seccomp.Load( if err := k.seccompLoad(
seccomp.Preset(bits.PresetStrict, seccomp.AllowMultiarch), seccomp.Preset(comp.PresetStrict, seccomp.AllowMultiarch),
seccomp.AllowMultiarch, seccomp.AllowMultiarch,
); err != nil { ); err != nil {
log.Fatalf("cannot load syscall filter: %v", err) k.fatalf("cannot load syscall filter: %v", err)
} }
if err := z.Wait(); err != nil { if err := k.containerWait(z); err != nil {
var exitError *exec.ExitError var exitError *exec.ExitError
if !errors.As(err, &exitError) { if !errors.As(err, &exitError) {
if errors.Is(err, context.Canceled) { if errors.Is(err, context.Canceled) {
os.Exit(hst.ExitCancel) k.exit(hst.ExitCancel)
} }
log.Printf("wait: %v", err) msg.Verbosef("cannot wait: %v", err)
os.Exit(127) k.exit(127)
} }
os.Exit(exitError.ExitCode()) k.exit(exitError.ExitCode())
} }
} }

155
internal/app/shim_test.go Normal file
View File

@@ -0,0 +1,155 @@
package app
import (
"bytes"
"context"
"log"
"os"
"syscall"
"testing"
"hakurei.app/container"
"hakurei.app/container/comp"
"hakurei.app/container/fhs"
"hakurei.app/container/seccomp"
"hakurei.app/container/stub"
"hakurei.app/hst"
)
func TestShimEntrypoint(t *testing.T) {
t.Parallel()
shimPreset := seccomp.Preset(comp.PresetStrict, seccomp.AllowMultiarch)
templateParams := &container.Params{
Dir: m("/data/data/org.chromium.Chromium"),
Env: []string{
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus",
"DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket",
"GOOGLE_API_KEY=AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
"GOOGLE_DEFAULT_CLIENT_ID=77185425430.apps.googleusercontent.com",
"GOOGLE_DEFAULT_CLIENT_SECRET=OTJgUOQcT7lO7GsGZq2G4IlT",
"HOME=/data/data/org.chromium.Chromium",
"PULSE_COOKIE=/.hakurei/pulse-cookie",
"PULSE_SERVER=unix:/run/user/1000/pulse/native",
"SHELL=/run/current-system/sw/bin/zsh",
"TERM=xterm-256color",
"USER=chronos",
"WAYLAND_DISPLAY=wayland-0",
"XDG_RUNTIME_DIR=/run/user/1000",
"XDG_SESSION_CLASS=user",
"XDG_SESSION_TYPE=wayland",
},
// spParamsOp
Hostname: "localhost",
RetainSession: true,
HostNet: true,
HostAbstract: true,
ForwardCancel: true,
Path: m("/run/current-system/sw/bin/chromium"),
Args: []string{
"chromium",
"--ignore-gpu-blocklist",
"--disable-smooth-scrolling",
"--enable-features=UseOzonePlatform",
"--ozone-platform=wayland",
},
SeccompFlags: seccomp.AllowMultiarch,
Uid: 1000,
Gid: 100,
Ops: new(container.Ops).
// resolveRoot
Root(m("/var/lib/hakurei/base/org.debian"), comp.BindWritable).
// spParamsOp
Proc(fhs.AbsProc).
Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
Bind(fhs.AbsDev, fhs.AbsDev, comp.BindWritable|comp.BindDevice).
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777).
// spRuntimeOp
Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/tmp/hakurei.10/runtime/9999"), m("/run/user/1000"), comp.BindWritable).
// spTmpdirOp
Bind(m("/tmp/hakurei.10/tmpdir/9999"), fhs.AbsTmp, comp.BindWritable).
// spAccountOp
Place(m("/etc/passwd"), []byte("chronos:x:1000:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")).
// spWaylandOp
Bind(m("/tmp/hakurei.10/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1000/wayland-0"), 0).
// spPulseOp
Bind(m("/run/user/1000/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/pulse"), m("/run/user/1000/pulse/native"), 0).
Place(m("/.hakurei/pulse-cookie"), bytes.Repeat([]byte{0}, pulseCookieSizeMax)).
// spDBusOp
Bind(m("/tmp/hakurei.10/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bus"), m("/run/user/1000/bus"), 0).
Bind(m("/tmp/hakurei.10/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0).
// spFilesystemOp
Etc(fhs.AbsEtc, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").
Tmpfs(fhs.AbsTmp, 0, 0755).
Overlay(m("/nix/store"),
fhs.AbsVarLib.Append("hakurei/nix/u0/org.chromium.Chromium/rw-store/upper"),
fhs.AbsVarLib.Append("hakurei/nix/u0/org.chromium.Chromium/rw-store/work"),
fhs.AbsVarLib.Append("hakurei/base/org.nixos/ro-store")).
Link(m("/run/current-system"), "/run/current-system", true).
Link(m("/run/opengl-driver"), "/run/opengl-driver", true).
Bind(fhs.AbsVarLib.Append("hakurei/u0/org.chromium.Chromium"),
m("/data/data/org.chromium.Chromium"),
comp.BindWritable|comp.BindEnsure).
Bind(fhs.AbsDev.Append("dri"), fhs.AbsDev.Append("dri"),
comp.BindOptional|comp.BindWritable|comp.BindDevice).
Remount(fhs.AbsRoot, syscall.MS_RDONLY),
}
checkSimple(t, "shimEntrypoint", []simpleTestCase{
{"success", func(k *kstub) error { shimEntrypoint(k); return nil }, stub.Expect{Calls: []stub.Call{
call("getMsg", stub.ExpectArgs{}, nil, nil),
call("getLogger", stub.ExpectArgs{}, (*log.Logger)(nil), nil),
call("setDumpable", stub.ExpectArgs{uintptr(container.SUID_DUMP_DISABLE)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SHIM", outcomeState{
Shim: &shimParams{PrivPID: 0xbad, WaitDelay: 0xf, Verbose: true, Ops: []outcomeOp{
&spParamsOp{"xterm-256color", true},
&spRuntimeOp{sessionTypeWayland},
spTmpdirOp{},
spAccountOp{},
&spWaylandOp{},
&spPulseOp{(*[256]byte)(bytes.Repeat([]byte{0}, pulseCookieSizeMax)), pulseCookieSizeMax},
&spDBusOp{true},
&spFilesystemOp{},
}},
ID: &checkExpectInstanceId,
Identity: hst.IdentityMax,
UserID: 10,
Container: hst.Template().Container,
Mapuid: 1000,
Mapgid: 100,
EnvPaths: &EnvPaths{TempDir: fhs.AbsTmp, RuntimePath: fhs.AbsRunUser.Append("1000")},
}, nil}, nil, nil),
call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbosef", stub.ExpectArgs{"process share directory at %q, runtime directory at %q", []any{m("/tmp/hakurei.10"), m("/run/user/1000/hakurei")}}, nil, nil),
call("setupContSignal", stub.ExpectArgs{0xbad}, 0, nil),
call("prctl", stub.ExpectArgs{uintptr(syscall.PR_SET_PDEATHSIG), uintptr(syscall.SIGCONT), uintptr(0)}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil),
call("closeReceive", stub.ExpectArgs{}, nil, nil),
call("notifyContext", stub.ExpectArgs{context.Background(), []os.Signal{os.Interrupt, syscall.SIGTERM}}, nil, nil),
call("containerStart", stub.ExpectArgs{templateParams}, nil, nil),
call("containerServe", stub.ExpectArgs{templateParams}, nil, nil),
call("seccompLoad", stub.ExpectArgs{shimPreset, seccomp.AllowMultiarch}, nil, nil),
call("containerWait", stub.ExpectArgs{templateParams}, nil, nil),
// deferred
call("wKeepAlive", stub.ExpectArgs{}, nil, nil),
}, Tracks: []stub.Expect{{Calls: []stub.Call{
call("rcRead", stub.ExpectArgs{}, []byte{2}, nil),
call("verbose", stub.ExpectArgs{[]any{"sa_sigaction got invalid siginfo"}}, nil, nil),
call("rcRead", stub.ExpectArgs{}, []byte{3}, nil),
call("verbose", stub.ExpectArgs{[]any{"got SIGCONT from unexpected process"}}, nil, nil),
call("rcRead", stub.ExpectArgs{}, nil, nil), // stub terminates this goroutine
}}}}, nil},
})
}

View File

@@ -11,8 +11,8 @@ import (
"syscall" "syscall"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/hst" "hakurei.app/hst"
@@ -75,16 +75,16 @@ func (s *spParamsOp) toContainer(state *outcomeStateParams) error {
} }
if state.Container.Flags&hst.FSeccompCompat == 0 { if state.Container.Flags&hst.FSeccompCompat == 0 {
state.params.SeccompPresets |= bits.PresetExt state.params.SeccompPresets |= comp.PresetExt
} }
if state.Container.Flags&hst.FDevel == 0 { if state.Container.Flags&hst.FDevel == 0 {
state.params.SeccompPresets |= bits.PresetDenyDevel state.params.SeccompPresets |= comp.PresetDenyDevel
} }
if state.Container.Flags&hst.FUserns == 0 { if state.Container.Flags&hst.FUserns == 0 {
state.params.SeccompPresets |= bits.PresetDenyNS state.params.SeccompPresets |= comp.PresetDenyNS
} }
if state.Container.Flags&hst.FTty == 0 { if state.Container.Flags&hst.FTty == 0 {
state.params.SeccompPresets |= bits.PresetDenyTTY state.params.SeccompPresets |= comp.PresetDenyTTY
} }
if state.Container.Flags&hst.FMapRealUID != 0 { if state.Container.Flags&hst.FMapRealUID != 0 {
@@ -112,7 +112,7 @@ func (s *spParamsOp) toContainer(state *outcomeStateParams) error {
if state.Container.Flags&hst.FDevice == 0 { if state.Container.Flags&hst.FDevice == 0 {
state.params.DevWritable(fhs.AbsDev, true) state.params.DevWritable(fhs.AbsDev, true)
} else { } else {
state.params.Bind(fhs.AbsDev, fhs.AbsDev, bits.BindWritable|bits.BindDevice) state.params.Bind(fhs.AbsDev, fhs.AbsDev, comp.BindWritable|comp.BindDevice)
} }
// /dev is mounted readonly later on, this prevents /dev/shm from going readonly with it // /dev is mounted readonly later on, this prevents /dev/shm from going readonly with it
state.params.Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777) state.params.Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777)

View File

@@ -8,8 +8,8 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/container/stub" "hakurei.app/container/stub"
@@ -65,11 +65,11 @@ func TestSpParamsOp(t *testing.T) {
HostAbstract: true, HostAbstract: true,
Path: config.Container.Path, Path: config.Container.Path,
Args: []string{config.Container.Path.String()}, Args: []string{config.Container.Path.String()},
SeccompPresets: bits.PresetExt | bits.PresetDenyDevel | bits.PresetDenyNS | bits.PresetDenyTTY, SeccompPresets: comp.PresetExt | comp.PresetDenyDevel | comp.PresetDenyNS | comp.PresetDenyTTY,
Uid: 1000, Uid: 1000,
Gid: 100, Gid: 100,
Ops: new(container.Ops). Ops: new(container.Ops).
Root(m("/var/lib/hakurei/base/org.debian"), bits.BindWritable). Root(m("/var/lib/hakurei/base/org.debian"), comp.BindWritable).
Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755). Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
DevWritable(fhs.AbsDev, true). DevWritable(fhs.AbsDev, true).
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777), Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777),
@@ -107,9 +107,9 @@ func TestSpParamsOp(t *testing.T) {
Uid: 1000, Uid: 1000,
Gid: 100, Gid: 100,
Ops: new(container.Ops). Ops: new(container.Ops).
Root(m("/var/lib/hakurei/base/org.debian"), bits.BindWritable). Root(m("/var/lib/hakurei/base/org.debian"), comp.BindWritable).
Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755). Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
Bind(fhs.AbsDev, fhs.AbsDev, bits.BindWritable|bits.BindDevice). Bind(fhs.AbsDev, fhs.AbsDev, comp.BindWritable|comp.BindDevice).
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777), Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"TERM": "xterm", "TERM": "xterm",
@@ -425,8 +425,8 @@ func TestSpFilesystemOp(t *testing.T) {
Bind( Bind(
fhs.AbsVarLib.Append("hakurei/u0/org.chromium.Chromium"), fhs.AbsVarLib.Append("hakurei/u0/org.chromium.Chromium"),
check.MustAbs("/data/data/org.chromium.Chromium"), check.MustAbs("/data/data/org.chromium.Chromium"),
bits.BindWritable|bits.BindEnsure). comp.BindWritable|comp.BindEnsure).
Bind(fhs.AbsDev.Append("dri"), fhs.AbsDev.Append("dri"), bits.BindDevice|bits.BindWritable|bits.BindOptional). Bind(fhs.AbsDev.Append("dri"), fhs.AbsDev.Append("dri"), comp.BindDevice|comp.BindWritable|comp.BindOptional).
Remount(fhs.AbsRoot, syscall.MS_RDONLY), Remount(fhs.AbsRoot, syscall.MS_RDONLY),
}, nil, nil}, }, nil, nil},
}) })

View File

@@ -3,8 +3,8 @@ package app
import ( import (
"encoding/gob" "encoding/gob"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/system" "hakurei.app/system"
@@ -111,7 +111,7 @@ func (s *spRuntimeOp) toContainer(state *outcomeStateParams) error {
state.params.Tmpfs(fhs.AbsRunUser, 1<<12, 0755) state.params.Tmpfs(fhs.AbsRunUser, 1<<12, 0755)
if state.Container.Flags&hst.FShareRuntime != 0 { if state.Container.Flags&hst.FShareRuntime != 0 {
_, runtimeDirInst := s.commonPaths(state.outcomeState) _, runtimeDirInst := s.commonPaths(state.outcomeState)
state.params.Bind(runtimeDirInst, state.runtimeDir, bits.BindWritable) state.params.Bind(runtimeDirInst, state.runtimeDir, comp.BindWritable)
} else { } else {
state.params.Mkdir(state.runtimeDir, 0700) state.params.Mkdir(state.runtimeDir, 0700)
} }

View File

@@ -4,7 +4,7 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits" "hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
@@ -41,7 +41,7 @@ func TestSpRuntimeOp(t *testing.T) {
}, &container.Params{ }, &container.Params{
Ops: new(container.Ops). Ops: new(container.Ops).
Tmpfs(fhs.AbsRunUser, 1<<12, 0755). Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), bits.BindWritable), Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), comp.BindWritable),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"XDG_RUNTIME_DIR": "/run/user/1000", "XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_CLASS": "user", "XDG_SESSION_CLASS": "user",
@@ -68,7 +68,7 @@ func TestSpRuntimeOp(t *testing.T) {
}, &container.Params{ }, &container.Params{
Ops: new(container.Ops). Ops: new(container.Ops).
Tmpfs(fhs.AbsRunUser, 1<<12, 0755). Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), bits.BindWritable), Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), comp.BindWritable),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"XDG_RUNTIME_DIR": "/run/user/1000", "XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_CLASS": "user", "XDG_SESSION_CLASS": "user",
@@ -95,7 +95,7 @@ func TestSpRuntimeOp(t *testing.T) {
}, &container.Params{ }, &container.Params{
Ops: new(container.Ops). Ops: new(container.Ops).
Tmpfs(fhs.AbsRunUser, 1<<12, 0755). Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), bits.BindWritable), Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), comp.BindWritable),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"XDG_RUNTIME_DIR": "/run/user/1000", "XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_CLASS": "user", "XDG_SESSION_CLASS": "user",
@@ -118,7 +118,7 @@ func TestSpRuntimeOp(t *testing.T) {
}, &container.Params{ }, &container.Params{
Ops: new(container.Ops). Ops: new(container.Ops).
Tmpfs(fhs.AbsRunUser, 1<<12, 0755). Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), bits.BindWritable), Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), comp.BindWritable),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"XDG_RUNTIME_DIR": "/run/user/1000", "XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_CLASS": "user", "XDG_SESSION_CLASS": "user",

View File

@@ -3,8 +3,8 @@ package app
import ( import (
"encoding/gob" "encoding/gob"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/system" "hakurei.app/system"
@@ -30,7 +30,7 @@ func (s spTmpdirOp) toSystem(state *outcomeStateSys) error {
func (s spTmpdirOp) toContainer(state *outcomeStateParams) error { func (s spTmpdirOp) toContainer(state *outcomeStateParams) error {
if state.Container.Flags&hst.FShareTmpdir != 0 { if state.Container.Flags&hst.FShareTmpdir != 0 {
_, tmpdirInst := s.commonPaths(state.outcomeState) _, tmpdirInst := s.commonPaths(state.outcomeState)
state.params.Bind(tmpdirInst, fhs.AbsTmp, bits.BindWritable) state.params.Bind(tmpdirInst, fhs.AbsTmp, comp.BindWritable)
} else { } else {
state.params.Tmpfs(fhs.AbsTmp, 0, 01777) state.params.Tmpfs(fhs.AbsTmp, 0, 01777)
} }

View File

@@ -4,7 +4,7 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits" "hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
@@ -28,7 +28,7 @@ func TestSpTmpdirOp(t *testing.T) {
// this op configures the container state and does not make calls during toContainer // this op configures the container state and does not make calls during toContainer
}, &container.Params{ }, &container.Params{
Ops: new(container.Ops). Ops: new(container.Ops).
Bind(m("/proc/nonexistent/tmp/hakurei.0/tmpdir/9"), fhs.AbsTmp, bits.BindWritable), Bind(m("/proc/nonexistent/tmp/hakurei.0/tmpdir/9"), fhs.AbsTmp, comp.BindWritable),
}, nil, nil}, }, nil, nil},
}) })
} }

View File

@@ -9,8 +9,8 @@ import (
"time" "time"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/message" "hakurei.app/message"
@@ -40,7 +40,7 @@ func Exec(ctx context.Context, msg message.Msg, p string) ([]*Entry, error) {
z := container.NewCommand(c, msg, toolPath, lddName, p) z := container.NewCommand(c, msg, toolPath, lddName, p)
z.Hostname = "hakurei-" + lddName z.Hostname = "hakurei-" + lddName
z.SeccompFlags |= seccomp.AllowMultiarch z.SeccompFlags |= seccomp.AllowMultiarch
z.SeccompPresets |= bits.PresetStrict z.SeccompPresets |= comp.PresetStrict
stdout, stderr := new(bytes.Buffer), new(bytes.Buffer) stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
z.Stdout = stdout z.Stdout = stdout
z.Stderr = stderr z.Stderr = stderr

View File

@@ -9,8 +9,8 @@ import (
"syscall" "syscall"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/helper" "hakurei.app/helper"
"hakurei.app/ldd" "hakurei.app/ldd"
@@ -65,7 +65,7 @@ func (p *Proxy) Start() error {
p.final, true, p.final, true,
argF, func(z *container.Container) { argF, func(z *container.Container) {
z.SeccompFlags |= seccomp.AllowMultiarch z.SeccompFlags |= seccomp.AllowMultiarch
z.SeccompPresets |= bits.PresetStrict z.SeccompPresets |= comp.PresetStrict
z.Hostname = "hakurei-dbus" z.Hostname = "hakurei-dbus"
if p.output != nil { if p.output != nil {
z.Stdout, z.Stderr = p.output, p.output z.Stdout, z.Stderr = p.output, p.output
@@ -114,7 +114,7 @@ func (p *Proxy) Start() error {
check.SortAbs(sockDirPaths) check.SortAbs(sockDirPaths)
sockDirPaths = check.CompactAbs(sockDirPaths) sockDirPaths = check.CompactAbs(sockDirPaths)
for _, name := range sockDirPaths { for _, name := range sockDirPaths {
z.Bind(name, name, bits.BindWritable) z.Bind(name, name, comp.BindWritable)
} }
// xdg-dbus-proxy bin path // xdg-dbus-proxy bin path

View File

@@ -32,6 +32,16 @@ nixosTest {
environment.systemPackages = [ environment.systemPackages = [
# For go tests: # For go tests:
(writeShellScriptBin "hakurei-test" '' (writeShellScriptBin "hakurei-test" ''
# Assert hst CGO_ENABLED=0: ${
with pkgs;
runCommand "hakurei-hst-cgo" { nativeBuildInputs = [ go ]; } ''
cp -r ${options.environment.hakurei.package.default.src} "$out"
chmod -R +w "$out"
cp ${writeText "hst_cgo_test.go" ''package hakurei_test;import("testing";"hakurei.app/hst");func TestTemplate(t *testing.T){hst.Template()}''} "$out/hst_cgo_test.go"
(cd "$out" && HOME="$(mktemp -d)" CGO_ENABLED=0 go test .)
''
}
cd ${self.packages.${system}.hakurei.src} cd ${self.packages.${system}.hakurei.src}
${fhs}/bin/hakurei-fhs -c \ ${fhs}/bin/hakurei-fhs -c \
'go test ${if withRace then "-race" else "-count 16"} ./...' \ 'go test ${if withRace then "-race" else "-count 16"} ./...' \

View File

@@ -22,7 +22,7 @@ in
{ {
name = "funcgraph-retval"; name = "funcgraph-retval";
patch = null; patch = null;
extraStructuredConfig.FUNCTION_GRAPH_RETVAL = lib.kernel.yes; structuredExtraConfig.FUNCTION_GRAPH_RETVAL = lib.kernel.yes;
} }
]; ];
} }