Skip to content

Commit 5db9fb1

Browse files
committed
shim/manager: Retry shim start without userns on clone failure
cloneMntNs sets CLONE_NEWUSER|CLONE_NEWNS on the child, but clone can fail for reasons the proactive AppArmor sysctl check cannot detect — seccomp filters, other LSM policies, or EACCES when inherited socket fds cross the user namespace boundary after exec triggers capability recomputation. Return whether namespace flags were set so the caller can distinguish a namespace-related Start failure from an unrelated one. On failure, rebuild the command without clone flags and retry, degrading gracefully to no mount isolation rather than failing the container start entirely. Signed-off-by: Derek McGowan <derek@mcg.dev>
1 parent 73cc270 commit 5db9fb1

3 files changed

Lines changed: 33 additions & 11 deletions

File tree

internal/shim/manager/manager_unix.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,32 @@ func (manager) Start(ctx context.Context, bparams *bootapi.BootstrapParams) (_ *
186186
cmd.ExtraFiles = append(cmd.ExtraFiles, s.f)
187187
}
188188

189-
cloneMntNs(ctx, cmd)
189+
userns := cloneMntNs(ctx, cmd)
190190

191-
if err := cmd.Start(); err != nil {
192-
return nil, err
191+
if startErr := cmd.Start(); startErr != nil {
192+
if !userns {
193+
return nil, startErr
194+
}
195+
// clone(CLONE_NEWUSER) can fail for reasons not covered by the
196+
// proactive AppArmor check — e.g. seccomp filters, LSM policies,
197+
// or EACCES from the child's capability recomputation when
198+
// inherited socket fds cross the user namespace boundary after
199+
// exec. Retry without namespace isolation rather than failing
200+
// the container start.
201+
//
202+
// Note: we cannot log here — during "start" the logger output
203+
// goes to stderr which containerd captures as part of the
204+
// bootstrap response (CombinedOutput), corrupting the JSON.
205+
cmd, err = newCommand(ctx, id, bparams.ContainerdGrpcAddress, bparams.ContainerdTtrpcAddress, debug)
206+
if err != nil {
207+
return nil, err
208+
}
209+
for _, s := range sockets {
210+
cmd.ExtraFiles = append(cmd.ExtraFiles, s.f)
211+
}
212+
if err := cmd.Start(); err != nil {
213+
return nil, fmt.Errorf("retry without userns failed: %w (original error: %v)", err, startErr)
214+
}
193215
}
194216

195217
defer func() {

internal/shim/manager/mount_linux.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,18 @@ import (
5252
// filesystem setup.
5353
//
5454
// If namespace creation is not possible (e.g. AppArmor restricts
55-
// unprivileged user namespaces), the function logs a warning and the shim
56-
// will run without mount isolation.
57-
func cloneMntNs(_ context.Context, cmd *exec.Cmd) {
55+
// unprivileged user namespaces), the shim runs without mount isolation
56+
// and this function returns false.
57+
// cloneMntNs returns true if user namespace clone flags were set.
58+
func cloneMntNs(_ context.Context, cmd *exec.Cmd) bool {
5859
if restricted, err := apparmorRestrictsUserns(); err != nil {
5960
// Failed to check apparmor userns restriction, skipping mount namespace isolation")
6061
// We can't log anything here as it will break the TTRPC protocol!
61-
// TODO(vvoland): Find a better way to surface this to the user.
62-
return
62+
return false
6363
} else if restricted {
6464
// apparmor_restrict_unprivileged_userns=1 prevents user namespace creation; shim will run without mount namespace isolation
6565
// We can't log anything here as it will break the TTRPC protocol!
66-
// TODO(vvoland): Find a better way to surface this to the user.
67-
return
66+
return false
6867
}
6968

7069
uid := os.Getuid()
@@ -76,6 +75,7 @@ func cloneMntNs(_ context.Context, cmd *exec.Cmd) {
7675
cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{
7776
{ContainerID: gid, HostID: gid, Size: 1},
7877
}
78+
return true
7979
}
8080

8181
// apparmorRestrictsUserns checks if the kernel sysctl

internal/shim/manager/mount_other.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ import (
2323
"os/exec"
2424
)
2525

26-
func cloneMntNs(_ context.Context, _ *exec.Cmd) {}
26+
func cloneMntNs(_ context.Context, _ *exec.Cmd) bool { return false }

0 commit comments

Comments
 (0)