diff --git a/cmd/defaults.go b/cmd/defaults.go index bb23b48d25..911fb53820 100644 --- a/cmd/defaults.go +++ b/cmd/defaults.go @@ -12,7 +12,7 @@ import ( // Return "chroot" if we know we're not actually root, "oci" otherwise. func builderDefaultIsolation() (string, error) { - if inUserNamespace() { + if inOurUserNamespace() { // We probably don't have enough privileges to use a proper // runtime. // Lean on the container that we're in being itself @@ -50,7 +50,6 @@ func builderDefaultStorage() (string, string, error) { }{ {"overlay", `["mountopt=metacopy=on"]`, nil}, {"overlay", ``, nil}, - {"overlay", `["mount_program=/usr/bin/fuse-overlayfs","mountopt=metacopy=on"]`, builderCanUseOverlayFUSE}, {"overlay", `["mount_program=/usr/bin/fuse-overlayfs"]`, builderCanUseOverlayFUSE}, {"vfs", "", nil}, } { diff --git a/cmd/userns.go b/cmd/userns.go index f70a9aab19..d87518d9bd 100644 --- a/cmd/userns.go +++ b/cmd/userns.go @@ -64,6 +64,13 @@ func parseIDMappings(uidmap, gidmap string) ([]specs.LinuxIDMapping, []specs.Lin if err != nil { klog.Fatalf("Error reading ID mappings for %s: %v\n", uid, err) } + } else { + for i := range UIDs { + UIDs[i].HostID = UIDs[i].ContainerID + } + for i := range GIDs { + GIDs[i].HostID = GIDs[i].ContainerID + } } if uidMappings := parseMapping("uidmap", uidmap); len(uidMappings) != 0 { UIDs = uidMappings @@ -74,13 +81,21 @@ func parseIDMappings(uidmap, gidmap string) ([]specs.LinuxIDMapping, []specs.Lin return UIDs, GIDs } -func inUserNamespace() bool { +// inOurUserNamespace returns true if we've already set up a user namespace of +// our own +func inOurUserNamespace() bool { return os.Getenv(usernsMarkerVariable) != "" } +// isNodeDefaultMapping returns true if the current ID map is the default node +// ID map: one range starting at 0, with length 2^32-1, mapped to itself +func isNodeDefaultMapping(m []specs.LinuxIDMapping) bool { + return len(m) == 1 && m[0].ContainerID == 0 && m[0].HostID == 0 && m[0].Size == 0xffffffff +} + func maybeReexecUsingUserNamespace(uidmap string, useNewuidmap bool, gidmap string, useNewgidmap bool) { // If we've already done all of this, there's no need to do it again. - if inUserNamespace() { + if inOurUserNamespace() { return } @@ -91,6 +106,30 @@ func maybeReexecUsingUserNamespace(uidmap string, useNewuidmap bool, gidmap stri } } + // Log the ID maps we were started with. + UIDs, GIDs, err := unshare.GetHostIDMappings("") + if err != nil { + klog.Fatalf("Error reading current ID mappings: %v\n", err) + } + if !isNodeDefaultMapping(UIDs) || !isNodeDefaultMapping(GIDs) { + uidMap, gidMap := "[", "[" + for i, entry := range UIDs { + if i > 0 { + uidMap += ", " + } + uidMap += fmt.Sprintf("(%d:%d:%d)", entry.ContainerID, entry.HostID, entry.Size) + } + for i, entry := range GIDs { + if i > 0 { + gidMap += ", " + } + gidMap += fmt.Sprintf("(%d:%d:%d)", entry.ContainerID, entry.HostID, entry.Size) + } + uidMap += "]" + gidMap += "]" + klog.V(2).Infof("Started in kernel user namespace with UID map %s and GID map %s.", uidMap, gidMap) + } + // Parse our --uidmap and --gidmap flags into ID mappings and re-exec ourselves. cmd := unshare.Command(append([]string{fmt.Sprintf("%s-in-a-user-namespace", os.Args[0])}, os.Args[1:]...)...) diff --git a/cmd/userns_test.go b/cmd/userns_test.go new file mode 100644 index 0000000000..9ab4e8485e --- /dev/null +++ b/cmd/userns_test.go @@ -0,0 +1,52 @@ +package main + +import ( + "testing" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" +) + +func TestIsNodeDefaultMapping(t *testing.T) { + cases := []struct { + description string + mappings []specs.LinuxIDMapping + expected bool + }{ + { + "node defaults", + []specs.LinuxIDMapping{ + {ContainerID: 0, HostID: 0, Size: 0xffffffff}, + }, + true, + }, + { + "typical isolated namespace", + []specs.LinuxIDMapping{ + {ContainerID: 0, HostID: 100100, Size: 65536}, + }, + false, + }, + { + "user with subuid", + []specs.LinuxIDMapping{ + {ContainerID: 0, HostID: 1001, Size: 1}, + {ContainerID: 1, HostID: 100100, Size: 65536}, + }, + false, + }, + { + "multiple ranges", + []specs.LinuxIDMapping{ + {ContainerID: 0, HostID: 1001, Size: 1024}, + {ContainerID: 1024, HostID: 100100, Size: 65536}, + }, + false, + }, + } + for i := range cases { + t.Run(cases[i].description, func(t *testing.T) { + assert.Equalf(t, cases[i].expected, isNodeDefaultMapping(cases[i].mappings), "isNodeDefaultMapping(%v)", cases[i].mappings) + }) + } +}