update ctr run to support multiple uid/gid mappings
Signed-off-by: Henry Wang <henwang@amazon.com>
This commit is contained in:
		@@ -40,6 +40,7 @@ import (
 | 
			
		||||
	. "github.com/containerd/containerd/v2/client"
 | 
			
		||||
	"github.com/containerd/containerd/v2/core/containers"
 | 
			
		||||
	"github.com/containerd/containerd/v2/integration/failpoint"
 | 
			
		||||
	"github.com/containerd/containerd/v2/integration/images"
 | 
			
		||||
	"github.com/containerd/containerd/v2/pkg/cio"
 | 
			
		||||
	"github.com/containerd/containerd/v2/pkg/fifosync"
 | 
			
		||||
	"github.com/containerd/containerd/v2/pkg/oci"
 | 
			
		||||
@@ -52,7 +53,8 @@ import (
 | 
			
		||||
	"golang.org/x/sys/unix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const testUserNSImage = "ghcr.io/containerd/alpine:3.14.0"
 | 
			
		||||
// We use this image for user ns tests because it has files with setuid bits
 | 
			
		||||
var testUserNSImage = images.Get(images.VolumeOwnership)
 | 
			
		||||
 | 
			
		||||
func TestTaskUpdate(t *testing.T) {
 | 
			
		||||
	t.Parallel()
 | 
			
		||||
@@ -1095,9 +1097,61 @@ func TestContainerKillInitPidHost(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUserNamespaces(t *testing.T) {
 | 
			
		||||
	t.Run("WritableRootFS", func(t *testing.T) { testUserNamespaces(t, false) })
 | 
			
		||||
	// see #1373 and runc#1572
 | 
			
		||||
	t.Run("ReadonlyRootFS", func(t *testing.T) { testUserNamespaces(t, true) })
 | 
			
		||||
	for name, test := range map[string]struct {
 | 
			
		||||
		testCmd  oci.SpecOpts
 | 
			
		||||
		roRootFS bool
 | 
			
		||||
		exitCode uint32 // testUserNamespaces validates the exit code of the test container against this value
 | 
			
		||||
		uidmaps  []specs.LinuxIDMapping
 | 
			
		||||
		gidmaps  []specs.LinuxIDMapping
 | 
			
		||||
	}{
 | 
			
		||||
		"WritableRootFS": {
 | 
			
		||||
			testCmd:  withExitStatus(7),
 | 
			
		||||
			roRootFS: false,
 | 
			
		||||
			exitCode: 7,
 | 
			
		||||
			uidmaps:  []specs.LinuxIDMapping{{ContainerID: 0, HostID: 1000, Size: 65535}},
 | 
			
		||||
			gidmaps:  []specs.LinuxIDMapping{{ContainerID: 0, HostID: 2000, Size: 65535}},
 | 
			
		||||
		},
 | 
			
		||||
		// see #1373 and runc#1572
 | 
			
		||||
		"ReadonlyRootFS": {
 | 
			
		||||
			testCmd:  withExitStatus(7),
 | 
			
		||||
			roRootFS: true,
 | 
			
		||||
			exitCode: 7,
 | 
			
		||||
			uidmaps:  []specs.LinuxIDMapping{{ContainerID: 0, HostID: 1000, Size: 65535}},
 | 
			
		||||
			gidmaps:  []specs.LinuxIDMapping{{ContainerID: 0, HostID: 2000, Size: 65535}},
 | 
			
		||||
		},
 | 
			
		||||
		"CheckSetUidBit": {
 | 
			
		||||
			testCmd:  withProcessArgs("bash", "-c", "[ -u /usr/bin/passwd ] && exit 7"),
 | 
			
		||||
			roRootFS: false,
 | 
			
		||||
			exitCode: 7,
 | 
			
		||||
			uidmaps:  []specs.LinuxIDMapping{{ContainerID: 0, HostID: 1000, Size: 65535}},
 | 
			
		||||
			gidmaps:  []specs.LinuxIDMapping{{ContainerID: 0, HostID: 2000, Size: 65535}},
 | 
			
		||||
		},
 | 
			
		||||
		"WritableRootFSMultipleMap": {
 | 
			
		||||
			testCmd:  withExitStatus(7),
 | 
			
		||||
			roRootFS: false,
 | 
			
		||||
			exitCode: 7,
 | 
			
		||||
			uidmaps:  []specs.LinuxIDMapping{{ContainerID: 0, HostID: 0, Size: 10}, {ContainerID: 10, HostID: 1000, Size: 65535}},
 | 
			
		||||
			gidmaps:  []specs.LinuxIDMapping{{ContainerID: 0, HostID: 0, Size: 20}, {ContainerID: 20, HostID: 2000, Size: 65535}},
 | 
			
		||||
		},
 | 
			
		||||
		"ReadonlyRootFSMultipleMap": {
 | 
			
		||||
			testCmd:  withExitStatus(7),
 | 
			
		||||
			roRootFS: true,
 | 
			
		||||
			exitCode: 7,
 | 
			
		||||
			uidmaps:  []specs.LinuxIDMapping{{ContainerID: 0, HostID: 0, Size: 20}, {ContainerID: 20, HostID: 2000, Size: 65535}},
 | 
			
		||||
			gidmaps:  []specs.LinuxIDMapping{{ContainerID: 0, HostID: 0, Size: 20}, {ContainerID: 20, HostID: 2000, Size: 65535}},
 | 
			
		||||
		},
 | 
			
		||||
		"CheckSetUidBitMultipleMap": {
 | 
			
		||||
			testCmd:  withProcessArgs("bash", "-c", "[ -u /usr/bin/passwd ] && exit 7"),
 | 
			
		||||
			roRootFS: false,
 | 
			
		||||
			exitCode: 7,
 | 
			
		||||
			uidmaps:  []specs.LinuxIDMapping{{ContainerID: 0, HostID: 0, Size: 20}, {ContainerID: 20, HostID: 2000, Size: 65535}},
 | 
			
		||||
			gidmaps:  []specs.LinuxIDMapping{{ContainerID: 0, HostID: 0, Size: 20}, {ContainerID: 20, HostID: 2000, Size: 65535}},
 | 
			
		||||
		},
 | 
			
		||||
	} {
 | 
			
		||||
		t.Run(name, func(t *testing.T) {
 | 
			
		||||
			testUserNamespaces(t, test.uidmaps, test.gidmaps, test.testCmd, test.roRootFS, test.exitCode)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkUserNS(t *testing.T) {
 | 
			
		||||
@@ -1111,7 +1165,7 @@ func checkUserNS(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testUserNamespaces(t *testing.T, readonlyRootFS bool) {
 | 
			
		||||
func testUserNamespaces(t *testing.T, uidmaps, gidmaps []specs.LinuxIDMapping, cmdOpt oci.SpecOpts, readonlyRootFS bool, expected uint32) {
 | 
			
		||||
	checkUserNS(t)
 | 
			
		||||
 | 
			
		||||
	client, err := newClient(t, address)
 | 
			
		||||
@@ -1133,25 +1187,23 @@ func testUserNamespaces(t *testing.T, readonlyRootFS bool) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	opts := []NewContainerOpts{WithNewSpec(oci.WithImageConfig(image),
 | 
			
		||||
		withExitStatus(7),
 | 
			
		||||
		oci.WithUserNamespace([]specs.LinuxIDMapping{
 | 
			
		||||
			{
 | 
			
		||||
				ContainerID: 0,
 | 
			
		||||
				HostID:      1000,
 | 
			
		||||
				Size:        10000,
 | 
			
		||||
			},
 | 
			
		||||
		}, []specs.LinuxIDMapping{
 | 
			
		||||
			{
 | 
			
		||||
				ContainerID: 0,
 | 
			
		||||
				HostID:      2000,
 | 
			
		||||
				Size:        10000,
 | 
			
		||||
			},
 | 
			
		||||
		}),
 | 
			
		||||
		cmdOpt,
 | 
			
		||||
		oci.WithUserID(34), // run task as the "backup" user
 | 
			
		||||
		oci.WithUserNamespace(uidmaps, gidmaps),
 | 
			
		||||
	)}
 | 
			
		||||
 | 
			
		||||
	if readonlyRootFS {
 | 
			
		||||
		opts = append([]NewContainerOpts{WithRemappedSnapshotView(id, image, 1000, 2000)}, opts...)
 | 
			
		||||
		if len(uidmaps) > 1 {
 | 
			
		||||
			opts = append([]NewContainerOpts{WithUserNSRemappedSnapshotView(id, image, uidmaps, gidmaps)}, opts...)
 | 
			
		||||
		} else {
 | 
			
		||||
			opts = append([]NewContainerOpts{WithRemappedSnapshotView(id, image, 1000, 2000)}, opts...)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		opts = append([]NewContainerOpts{WithRemappedSnapshot(id, image, 1000, 2000)}, opts...)
 | 
			
		||||
		if len(uidmaps) > 1 {
 | 
			
		||||
			opts = append([]NewContainerOpts{WithUserNSRemappedSnapshot(id, image, uidmaps, gidmaps)}, opts...)
 | 
			
		||||
		} else {
 | 
			
		||||
			opts = append([]NewContainerOpts{WithRemappedSnapshot(id, image, 1000, 2000)}, opts...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	container, err := client.NewContainer(ctx, id, opts...)
 | 
			
		||||
@@ -1192,15 +1244,15 @@ func testUserNamespaces(t *testing.T, readonlyRootFS bool) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	if code != 7 {
 | 
			
		||||
		t.Errorf("expected status 7 from wait but received %d", code)
 | 
			
		||||
	if code != expected {
 | 
			
		||||
		t.Errorf("expected status %d from wait but received %d", expected, code)
 | 
			
		||||
	}
 | 
			
		||||
	deleteStatus, err := task.Delete(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	if ec := deleteStatus.ExitCode(); ec != 7 {
 | 
			
		||||
		t.Errorf("expected status 7 from delete but received %d", ec)
 | 
			
		||||
	if ec := deleteStatus.ExitCode(); ec != expected {
 | 
			
		||||
		t.Errorf("expected status %d from delete but received %d", expected, ec)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user