aboutsummaryrefslogtreecommitdiffhomepage
path: root/container/container_test.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-05-07 15:15:28 +0900
committerOphestra <cat@gensokyo.uk>2026-05-07 15:15:47 +0900
commitd4144fcf7f9217d2a2dcec21202421d5fa9d4928 (patch)
tree42625452fcde08e61370725ae99bc9915d88ccb3 /container/container_test.go
parentbad66facbc1b555d8883534c27ada586fc15df9c (diff)
container: optionally map uid/gid 0 as init
Unfortunately required to work around flawed APIs like binfmt_misc. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container/container_test.go')
-rw-r--r--container/container_test.go66
1 files changed, 57 insertions, 9 deletions
diff --git a/container/container_test.go b/container/container_test.go
index 48b6e8a4..787720a8 100644
--- a/container/container_test.go
+++ b/container/container_test.go
@@ -409,8 +409,11 @@ var containerTestCases = []struct {
func TestContainer(t *testing.T) {
t.Parallel()
+ var suffix string
+runTests:
for i, tc := range containerTestCases {
- t.Run(tc.name, func(t *testing.T) {
+ _suffix := suffix
+ t.Run(tc.name+_suffix, func(t *testing.T) {
t.Parallel()
wantOps, wantOpsCtx := tc.ops(t)
@@ -434,6 +437,8 @@ func TestContainer(t *testing.T) {
c.SeccompDisable = !tc.filter
c.RetainSession = tc.session
c.HostNet = tc.net
+ c.InitAsRoot = _suffix != ""
+ c.Env = append(c.Env, "HAKUREI_TEST_SUFFIX="+_suffix)
if info.CanDegrade {
if _, err := landlock.GetABI(); err != nil {
if !errors.Is(err, syscall.ENOSYS) {
@@ -443,6 +448,9 @@ func TestContainer(t *testing.T) {
t.Log("Landlock LSM is unavailable, enabling HostAbstract")
}
}
+ if c.InitAsRoot {
+ c.SeccompPresets &= ^std.PresetDenyNS
+ }
c.
Readonly(check.MustAbs(pathReadonly), 0755).
@@ -511,6 +519,11 @@ func TestContainer(t *testing.T) {
}
})
}
+
+ if suffix == "" {
+ suffix = " as root"
+ goto runTests
+ }
}
func ent(root, target, vfsOptstr, fsType, source, fsOptstr string) *vfs.MountInfoEntry {
@@ -589,9 +602,9 @@ func testContainerCancel(
}
func TestForward(t *testing.T) {
- testContainerCancel(t, func(c *container.Container) {
- c.ForwardCancel = true
- }, func(ps *os.ProcessState, waitErr error) {
+ t.Parallel()
+
+ f := func(ps *os.ProcessState, waitErr error) {
var exitError *exec.ExitError
if !errors.As(waitErr, &exitError) {
if m, ok := container.InternalMessageFromError(waitErr); ok {
@@ -602,11 +615,26 @@ func TestForward(t *testing.T) {
if code := exitError.ExitCode(); code != blockExitCodeInterrupt {
t.Errorf("ExitCode: %d, want %d", code, blockExitCodeInterrupt)
}
+ }
+ t.Run("direct", func(t *testing.T) {
+ t.Parallel()
+ testContainerCancel(t, func(c *container.Container) {
+ c.ForwardCancel = true
+ }, f)
+ })
+ t.Run("as root", func(t *testing.T) {
+ testContainerCancel(t, func(c *container.Container) {
+ c.ForwardCancel = true
+ c.InitAsRoot = true
+ c.Proc(fhs.AbsProc)
+ }, f)
})
}
func TestCancel(t *testing.T) {
- testContainerCancel(t, nil, func(ps *os.ProcessState, waitErr error) {
+ t.Parallel()
+
+ f := func(ps *os.ProcessState, waitErr error) {
wantErr := context.Canceled
if !reflect.DeepEqual(waitErr, wantErr) {
if m, ok := container.InternalMessageFromError(waitErr); ok {
@@ -619,6 +647,16 @@ func TestCancel(t *testing.T) {
} else if code := ps.ExitCode(); code != 0 {
t.Errorf("ExitCode: %d, want %d", code, 0)
}
+ }
+ t.Run("direct", func(t *testing.T) {
+ t.Parallel()
+ testContainerCancel(t, nil, f)
+ })
+ t.Run("as root", func(t *testing.T) {
+ testContainerCancel(t, func(c *container.Container) {
+ c.InitAsRoot = true
+ c.Proc(fhs.AbsProc)
+ }, f)
})
}
@@ -655,6 +693,8 @@ func init() {
})
c.Command("container", command.UsageInternal, func(args []string) error {
+ asRoot := os.Getenv("HAKUREI_TEST_SUFFIX") == " as root"
+
if len(args) != 1 {
return syscall.EINVAL
}
@@ -672,11 +712,19 @@ func init() {
return fmt.Errorf("gid: %d, want %d", gid, tc.gid)
}
+ // no attack surface increase during as root due to no_new_privs
+ var wantBounding uintptr = 1
+ asRootNot := " not"
+ if !asRoot {
+ wantBounding = 0
+ asRootNot = ""
+ }
+
const (
PR_CAP_AMBIENT = 0x2f
PR_CAP_AMBIENT_IS_SET = 0x1
)
- for i := range container.LastCap(nil) {
+ for i := range container.LastCap(nil) + 1 {
r, _, errno := syscall.Syscall(
syscall.SYS_PRCTL,
PR_CAP_AMBIENT,
@@ -687,7 +735,7 @@ func init() {
return os.NewSyscallError("prctl", errno)
}
if r != 0 {
- return fmt.Errorf("capability %d is set", i)
+ return fmt.Errorf("capability %d in ambient set", i)
}
r, _, errno = syscall.Syscall(
@@ -699,8 +747,8 @@ func init() {
if errno != 0 {
return os.NewSyscallError("prctl", errno)
}
- if r != 0 {
- return fmt.Errorf("capability %d in set", i)
+ if r != wantBounding {
+ return fmt.Errorf("capability %d%s in bounding set", i, asRootNot)
}
}