diff --git a/container_linux_test.go b/container_linux_test.go index ece24bf66..9953e7133 100644 --- a/container_linux_test.go +++ b/container_linux_test.go @@ -1432,9 +1432,9 @@ func testUserNamespaces(t *testing.T, readonlyRootFS bool) { }), )} if readonlyRootFS { - opts = append([]NewContainerOpts{WithRemappedSnapshotView(id, image, 1000, 2000)}, opts...) + opts = append([]NewContainerOpts{WithRemappedSnapshotView(id, image, 1000, 2000, remapperChown)}, opts...) } else { - opts = append([]NewContainerOpts{WithRemappedSnapshot(id, image, 1000, 2000)}, opts...) + opts = append([]NewContainerOpts{WithRemappedSnapshot(id, image, 1000, 2000, remapperChown)}, opts...) } container, err := client.NewContainer(ctx, id, opts...) diff --git a/container_opts_unix.go b/container_opts_unix.go index b109a10ec..04be00cd1 100644 --- a/container_opts_unix.go +++ b/container_opts_unix.go @@ -28,22 +28,35 @@ import ( "github.com/containerd/containerd/containers" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/snapshots" "github.com/opencontainers/image-spec/identity" ) +// Remapper specifies the type of remapping to be done +type Remapper string + +const ( + remapperChown Remapper = "chown" + remapperSnapshotter Remapper = "snapshotter" +) + // WithRemappedSnapshot creates a new snapshot and remaps the uid/gid for the // filesystem to be used by a container with user namespaces -func WithRemappedSnapshot(id string, i Image, uid, gid uint32) NewContainerOpts { - return withRemappedSnapshotBase(id, i, uid, gid, false) +func WithRemappedSnapshot(id string, i Image, uid, gid uint32, remapper Remapper) NewContainerOpts { + return withRemappedSnapshotBase(id, i, uid, gid, false, remapper) } // WithRemappedSnapshotView is similar to WithRemappedSnapshot but rootfs is mounted as read-only. -func WithRemappedSnapshotView(id string, i Image, uid, gid uint32) NewContainerOpts { - return withRemappedSnapshotBase(id, i, uid, gid, true) +func WithRemappedSnapshotView(id string, i Image, uid, gid uint32, remapper Remapper) NewContainerOpts { + return withRemappedSnapshotBase(id, i, uid, gid, true, remapper) } -func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool) NewContainerOpts { +func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool, remapper Remapper) NewContainerOpts { return func(ctx context.Context, client *Client, c *containers.Container) error { + if remapper != remapperChown && remapper != remapperSnapshotter { + return fmt.Errorf("remapper must be either '%s' or '%s'", remapperChown, remapperSnapshotter) + } + diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), client.platform) if err != nil { return err @@ -52,6 +65,7 @@ func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool var ( parent = identity.ChainID(diffIDs).String() usernsID = fmt.Sprintf("%s-%d-%d", parent, uid, gid) + opts = []snapshots.Opt{} ) c.Snapshotter, err = client.resolveSnapshotterName(ctx, c.Snapshotter) if err != nil { @@ -61,8 +75,15 @@ func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool if err != nil { return err } + if remapper == remapperSnapshotter { + opts = append(opts, + snapshots.WithLabels(map[string]string{ + "containerd.io/snapshot/uidmapping": fmt.Sprintf("%d:%d:%d", 0, uid, 65535), + "containerd.io/snapshot/gidmapping": fmt.Sprintf("%d:%d:%d", 0, gid, 65535), + })) + } if _, err := snapshotter.Stat(ctx, usernsID); err == nil { - if _, err := snapshotter.Prepare(ctx, id, usernsID); err == nil { + if _, err := snapshotter.Prepare(ctx, id, usernsID, opts...); err == nil { c.SnapshotKey = id c.Image = i.Name() return nil @@ -70,21 +91,23 @@ func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool return err } } - mounts, err := snapshotter.Prepare(ctx, usernsID+"-remap", parent) + mounts, err := snapshotter.Prepare(ctx, usernsID+"-remap", parent, opts...) if err != nil { return err } - if err := remapRootFS(ctx, mounts, uid, gid); err != nil { - snapshotter.Remove(ctx, usernsID) - return err + if remapper == remapperChown { + if err := remapRootFS(ctx, mounts, uid, gid); err != nil { + snapshotter.Remove(ctx, usernsID) + return err + } } - if err := snapshotter.Commit(ctx, usernsID, usernsID+"-remap"); err != nil { + if err := snapshotter.Commit(ctx, usernsID, usernsID+"-remap", opts...); err != nil { return err } if readonly { - _, err = snapshotter.View(ctx, id, usernsID) + _, err = snapshotter.View(ctx, id, usernsID, opts...) } else { - _, err = snapshotter.Prepare(ctx, id, usernsID) + _, err = snapshotter.Prepare(ctx, id, usernsID, opts...) } if err != nil { return err