Skip to content

Commit 01a9334

Browse files
authored
Merge pull request #150 from dmcgowan/retry-on-userns-failure
Retry shim start without userns on clone failure
2 parents 576f82a + 5db9fb1 commit 01a9334

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)