Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions cmd/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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},
} {
Expand Down
43 changes: 41 additions & 2 deletions cmd/userns.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}

Expand All @@ -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:]...)...)

Expand Down
52 changes: 52 additions & 0 deletions cmd/userns_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}
}