aboutsummaryrefslogtreecommitdiffhomepage
path: root/container_test.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-07-03 02:59:43 +0900
committerOphestra <cat@gensokyo.uk>2025-07-03 02:59:43 +0900
commit1b5ecd9eaf3289d164d8ed1bce6013e0e4ef8e86 (patch)
tree047fe5fabdfb37d5c0088f7f572cebc8b13853bb /container_test.go
parent82561d62b66f17c05604e87f18187bb3a91f00d2 (diff)
container: move out of toplevel
This allows slightly easier use of the vanity url. This also provides some disambiguation between low level containers and hakurei app containers. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container_test.go')
-rw-r--r--container_test.go281
1 files changed, 0 insertions, 281 deletions
diff --git a/container_test.go b/container_test.go
deleted file mode 100644
index 4604d025..00000000
--- a/container_test.go
+++ /dev/null
@@ -1,281 +0,0 @@
-package hakurei_test
-
-import (
- "bytes"
- "context"
- "encoding/gob"
- "log"
- "os"
- "os/exec"
- "strings"
- "syscall"
- "testing"
- "time"
-
- "git.gensokyo.uk/security/hakurei"
- "git.gensokyo.uk/security/hakurei/hst"
- "git.gensokyo.uk/security/hakurei/internal"
- "git.gensokyo.uk/security/hakurei/internal/hlog"
- "git.gensokyo.uk/security/hakurei/ldd"
- "git.gensokyo.uk/security/hakurei/seccomp"
- "git.gensokyo.uk/security/hakurei/vfs"
-)
-
-const (
- ignore = "\x00"
- ignoreV = -1
-)
-
-func TestContainer(t *testing.T) {
- {
- oldVerbose := hlog.Load()
- oldOutput := hakurei.GetOutput()
- internal.InstallOutput(true)
- t.Cleanup(func() { hlog.Store(oldVerbose) })
- t.Cleanup(func() { hakurei.SetOutput(oldOutput) })
- }
-
- testCases := []struct {
- name string
- filter bool
- session bool
- net bool
- ops *hakurei.Ops
- mnt []*vfs.MountInfoEntry
- host string
- rules []seccomp.NativeRule
- flags seccomp.ExportFlag
- presets seccomp.FilterPreset
- }{
- {"minimal", true, false, false,
- new(hakurei.Ops), nil, "test-minimal",
- nil, 0, seccomp.PresetStrict},
- {"allow", true, true, true,
- new(hakurei.Ops), nil, "test-minimal",
- nil, 0, seccomp.PresetExt | seccomp.PresetDenyDevel},
- {"no filter", false, true, true,
- new(hakurei.Ops), nil, "test-no-filter",
- nil, 0, seccomp.PresetExt},
- {"custom rules", true, true, true,
- new(hakurei.Ops), nil, "test-no-filter",
- []seccomp.NativeRule{
- {seccomp.ScmpSyscall(syscall.SYS_SETUID), seccomp.ScmpErrno(syscall.EPERM), nil},
- }, 0, seccomp.PresetExt},
- {"tmpfs", true, false, false,
- new(hakurei.Ops).
- Tmpfs(hst.Tmp, 0, 0755),
- []*vfs.MountInfoEntry{
- e("/", hst.Tmp, "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
- }, "test-tmpfs",
- nil, 0, seccomp.PresetStrict},
- {"dev", true, true /* go test output is not a tty */, false,
- new(hakurei.Ops).
- Dev("/dev").
- Mqueue("/dev/mqueue"),
- []*vfs.MountInfoEntry{
- e("/", "/dev", "rw,nosuid,nodev,relatime", "tmpfs", "devtmpfs", ignore),
- e("/null", "/dev/null", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
- e("/zero", "/dev/zero", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
- e("/full", "/dev/full", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
- e("/random", "/dev/random", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
- e("/urandom", "/dev/urandom", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
- e("/tty", "/dev/tty", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
- e("/", "/dev/pts", "rw,nosuid,noexec,relatime", "devpts", "devpts", "rw,mode=620,ptmxmode=666"),
- e("/", "/dev/mqueue", "rw,nosuid,nodev,noexec,relatime", "mqueue", "mqueue", "rw"),
- }, "",
- nil, 0, seccomp.PresetStrict},
- }
-
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
- defer cancel()
-
- container := hakurei.New(ctx, "/usr/bin/sandbox.test", "-test.v",
- "-test.run=TestHelperCheckContainer", "--", "check", tc.host)
- container.Uid = 1000
- container.Gid = 100
- container.Hostname = tc.host
- container.CommandContext = commandContext
- container.Stdout, container.Stderr = os.Stdout, os.Stderr
- container.Ops = tc.ops
- container.SeccompRules = tc.rules
- container.SeccompFlags = tc.flags | seccomp.AllowMultiarch
- container.SeccompPresets = tc.presets
- container.SeccompDisable = !tc.filter
- container.RetainSession = tc.session
- container.HostNet = tc.net
- if container.Args[5] == "" {
- if name, err := os.Hostname(); err != nil {
- t.Fatalf("cannot get hostname: %v", err)
- } else {
- container.Args[5] = name
- }
- }
-
- container.
- Tmpfs("/tmp", 0, 0755).
- Bind(os.Args[0], os.Args[0], 0).
- Mkdir("/usr/bin", 0755).
- Link(os.Args[0], "/usr/bin/sandbox.test").
- Place("/etc/hostname", []byte(container.Args[5]))
- // in case test has cgo enabled
- var libPaths []string
- if entries, err := ldd.ExecFilter(ctx,
- commandContext,
- func(v []byte) []byte {
- return bytes.SplitN(v, []byte("TestHelperInit\n"), 2)[1]
- }, os.Args[0]); err != nil {
- log.Fatalf("ldd: %v", err)
- } else {
- libPaths = ldd.Path(entries)
- }
- for _, name := range libPaths {
- container.Bind(name, name, 0)
- }
- // needs /proc to check mountinfo
- container.Proc("/proc")
-
- mnt := make([]*vfs.MountInfoEntry, 0, 3+len(libPaths))
- mnt = append(mnt, e("/sysroot", "/", "rw,nosuid,nodev,relatime", "tmpfs", "rootfs", ignore))
- mnt = append(mnt, tc.mnt...)
- mnt = append(mnt,
- e("/", "/tmp", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
- e(ignore, os.Args[0], "ro,nosuid,nodev,relatime", ignore, ignore, ignore),
- e(ignore, "/etc/hostname", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", ignore),
- )
- for _, name := range libPaths {
- mnt = append(mnt, e(ignore, name, "ro,nosuid,nodev,relatime", ignore, ignore, ignore))
- }
- mnt = append(mnt, e("/", "/proc", "rw,nosuid,nodev,noexec,relatime", "proc", "proc", "rw"))
- want := new(bytes.Buffer)
- if err := gob.NewEncoder(want).Encode(mnt); err != nil {
- t.Fatalf("cannot serialise expected mount points: %v", err)
- }
- container.Stdin = want
-
- if err := container.Start(); err != nil {
- hlog.PrintBaseError(err, "start:")
- t.Fatalf("cannot start container: %v", err)
- } else if err = container.Serve(); err != nil {
- hlog.PrintBaseError(err, "serve:")
- t.Errorf("cannot serve setup params: %v", err)
- }
- if err := container.Wait(); err != nil {
- hlog.PrintBaseError(err, "wait:")
- t.Fatalf("wait: %v", err)
- }
- })
- }
-}
-
-func e(root, target, vfsOptstr, fsType, source, fsOptstr string) *vfs.MountInfoEntry {
- return &vfs.MountInfoEntry{
- ID: ignoreV,
- Parent: ignoreV,
- Devno: vfs.DevT{ignoreV, ignoreV},
- Root: root,
- Target: target,
- VfsOptstr: vfsOptstr,
- OptFields: []string{ignore},
- FsType: fsType,
- Source: source,
- FsOptstr: fsOptstr,
- }
-}
-
-func TestContainerString(t *testing.T) {
- container := hakurei.New(t.Context(), "ldd", "/usr/bin/env")
- container.SeccompFlags |= seccomp.AllowMultiarch
- container.SeccompRules = seccomp.Preset(
- seccomp.PresetExt|seccomp.PresetDenyNS|seccomp.PresetDenyTTY,
- container.SeccompFlags)
- container.SeccompPresets = seccomp.PresetStrict
- want := `argv: ["ldd" "/usr/bin/env"], filter: true, rules: 65, flags: 0x1, presets: 0xf`
- if got := container.String(); got != want {
- t.Errorf("String: %s, want %s", got, want)
- }
-}
-
-func TestHelperInit(t *testing.T) {
- if len(os.Args) != 5 || os.Args[4] != "init" {
- return
- }
- hakurei.SetOutput(hlog.Output{})
- hakurei.Init(hlog.Prepare, internal.InstallOutput)
-}
-
-func TestHelperCheckContainer(t *testing.T) {
- if len(os.Args) != 6 || os.Args[4] != "check" {
- return
- }
-
- t.Run("user", func(t *testing.T) {
- if uid := syscall.Getuid(); uid != 1000 {
- t.Errorf("Getuid: %d, want 1000", uid)
- }
- if gid := syscall.Getgid(); gid != 100 {
- t.Errorf("Getgid: %d, want 100", gid)
- }
- })
- t.Run("hostname", func(t *testing.T) {
- if name, err := os.Hostname(); err != nil {
- t.Fatalf("cannot get hostname: %v", err)
- } else if name != os.Args[5] {
- t.Errorf("Hostname: %q, want %q", name, os.Args[5])
- }
-
- if p, err := os.ReadFile("/etc/hostname"); err != nil {
- t.Fatalf("%v", err)
- } else if string(p) != os.Args[5] {
- t.Errorf("/etc/hostname: %q, want %q", string(p), os.Args[5])
- }
- })
- t.Run("mount", func(t *testing.T) {
- var mnt []*vfs.MountInfoEntry
- if err := gob.NewDecoder(os.Stdin).Decode(&mnt); err != nil {
- t.Fatalf("cannot receive expected mount points: %v", err)
- }
-
- var d *vfs.MountInfoDecoder
- if f, err := os.Open("/proc/self/mountinfo"); err != nil {
- t.Fatalf("cannot open mountinfo: %v", err)
- } else {
- d = vfs.NewMountInfoDecoder(f)
- }
-
- i := 0
- for cur := range d.Entries() {
- if i == len(mnt) {
- t.Errorf("got more than %d entries", len(mnt))
- break
- }
-
- // ugly hack but should be reliable and is less likely to false negative than comparing by parsed flags
- cur.VfsOptstr = strings.TrimSuffix(cur.VfsOptstr, ",relatime")
- cur.VfsOptstr = strings.TrimSuffix(cur.VfsOptstr, ",noatime")
- mnt[i].VfsOptstr = strings.TrimSuffix(mnt[i].VfsOptstr, ",relatime")
- mnt[i].VfsOptstr = strings.TrimSuffix(mnt[i].VfsOptstr, ",noatime")
-
- if !cur.EqualWithIgnore(mnt[i], "\x00") {
- t.Errorf("[FAIL] %s", cur)
- } else {
- t.Logf("[ OK ] %s", cur)
- }
-
- i++
- }
- if err := d.Err(); err != nil {
- t.Errorf("cannot parse mountinfo: %v", err)
- }
-
- if i != len(mnt) {
- t.Errorf("got %d entries, want %d", i, len(mnt))
- }
- })
-}
-
-func commandContext(ctx context.Context) *exec.Cmd {
- return exec.CommandContext(ctx, os.Args[0], "-test.v",
- "-test.run=TestHelperInit", "--", "init")
-}