diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-07-03 02:59:43 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-07-03 02:59:43 +0900 |
| commit | 1b5ecd9eaf3289d164d8ed1bce6013e0e4ef8e86 (patch) | |
| tree | 047fe5fabdfb37d5c0088f7f572cebc8b13853bb /container_test.go | |
| parent | 82561d62b66f17c05604e87f18187bb3a91f00d2 (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.go | 281 |
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") -} |
