Skip to content

Commit 31a6449

Browse files
dglrata
andcommitted
Add capability for snapshotters to declare support for UID remapping
This allows user namespace support to progress, either by allowing snapshotters to deal with ownership, or falling back to containerd doing a recursive chown. In the future, when snapshotters implement idmap mounts, they should report the "remap-ids" capability. Co-authored-by: Rodrigo Campos <[email protected]> Signed-off-by: Rodrigo Campos <[email protected]> Signed-off-by: David Leadbeater <[email protected]>
1 parent 36f520d commit 31a6449

14 files changed

Lines changed: 270 additions & 12 deletions

client.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,3 +866,21 @@ func toPlatforms(pt []*apitypes.Platform) []ocispec.Platform {
866866
}
867867
return platforms
868868
}
869+
870+
// GetSnapshotterCapabilities returns the capabilities of a snapshotter.
871+
func (c *Client) GetSnapshotterCapabilities(ctx context.Context, snapshotterName string) ([]string, error) {
872+
filters := []string{fmt.Sprintf("type==%s, id==%s", plugin.SnapshotPlugin, snapshotterName)}
873+
in := c.IntrospectionService()
874+
875+
resp, err := in.Plugins(ctx, filters)
876+
if err != nil {
877+
return nil, err
878+
}
879+
880+
if len(resp.Plugins) <= 0 {
881+
return nil, fmt.Errorf("inspection service could not find snapshotter %s plugin", snapshotterName)
882+
}
883+
884+
sn := resp.Plugins[0]
885+
return sn.Capabilities, nil
886+
}

container_opts.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,11 @@ func WithNewSnapshot(id string, i Image, opts ...snapshots.Opt) NewContainerOpts
224224
if err != nil {
225225
return err
226226
}
227+
228+
parent, err = resolveSnapshotOptions(ctx, client, c.Snapshotter, s, parent, opts...)
229+
if err != nil {
230+
return err
231+
}
227232
if _, err := s.Prepare(ctx, id, parent, opts...); err != nil {
228233
return err
229234
}
@@ -268,6 +273,11 @@ func WithNewSnapshotView(id string, i Image, opts ...snapshots.Opt) NewContainer
268273
if err != nil {
269274
return err
270275
}
276+
277+
parent, err = resolveSnapshotOptions(ctx, client, c.Snapshotter, s, parent, opts...)
278+
if err != nil {
279+
return err
280+
}
271281
if _, err := s.View(ctx, id, parent, opts...); err != nil {
272282
return err
273283
}

pkg/cri/server/container_create.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,10 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
184184
log.G(ctx).Debugf("Container %q spec: %#+v", id, spew.NewFormatter(spec))
185185

186186
// Grab any platform specific snapshotter opts.
187-
sOpts := snapshotterOpts(c.config.ContainerdConfig.Snapshotter, config)
187+
sOpts, err := snapshotterOpts(c.config.ContainerdConfig.Snapshotter, config)
188+
if err != nil {
189+
return nil, err
190+
}
188191

189192
// Set snapshotter before any other options.
190193
opts := []containerd.NewContainerOpts{

pkg/cri/server/container_create_linux.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,7 @@ func generateUserString(username string, uid, gid *runtime.Int64Value) (string,
601601
}
602602

603603
// snapshotterOpts returns any Linux specific snapshotter options for the rootfs snapshot
604-
func snapshotterOpts(snapshotterName string, config *runtime.ContainerConfig) []snapshots.Opt {
605-
return []snapshots.Opt{}
604+
func snapshotterOpts(snapshotterName string, config *runtime.ContainerConfig) ([]snapshots.Opt, error) {
605+
nsOpts := config.GetLinux().GetSecurityContext().GetNamespaceOptions()
606+
return snapshotterRemapOpts(nsOpts)
606607
}

pkg/cri/server/container_create_other.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,6 @@ func (c *criService) containerSpecOpts(config *runtime.ContainerConfig, imageCon
5555
}
5656

5757
// snapshotterOpts returns snapshotter options for the rootfs snapshot
58-
func snapshotterOpts(snapshotterName string, config *runtime.ContainerConfig) []snapshots.Opt {
59-
return []snapshots.Opt{}
58+
func snapshotterOpts(snapshotterName string, config *runtime.ContainerConfig) ([]snapshots.Opt, error) {
59+
return []snapshots.Opt{}, nil
6060
}

pkg/cri/server/container_create_windows.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func (c *criService) containerSpecOpts(config *runtime.ContainerConfig, imageCon
145145
}
146146

147147
// snapshotterOpts returns any Windows specific snapshotter options for the r/w layer
148-
func snapshotterOpts(snapshotterName string, config *runtime.ContainerConfig) []snapshots.Opt {
148+
func snapshotterOpts(snapshotterName string, config *runtime.ContainerConfig) ([]snapshots.Opt, error) {
149149
var opts []snapshots.Opt
150150

151151
switch snapshotterName {
@@ -160,5 +160,5 @@ func snapshotterOpts(snapshotterName string, config *runtime.ContainerConfig) []
160160
}
161161
}
162162

163-
return opts
163+
return opts, nil
164164
}

pkg/cri/server/helpers_linux.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ import (
2828
"syscall"
2929
"time"
3030

31+
"github.com/containerd/containerd"
3132
"github.com/containerd/containerd/log"
3233
"github.com/containerd/containerd/mount"
3334
"github.com/containerd/containerd/pkg/apparmor"
3435
"github.com/containerd/containerd/pkg/seccomp"
3536
"github.com/containerd/containerd/pkg/seutil"
37+
"github.com/containerd/containerd/snapshots"
3638
"github.com/moby/sys/mountinfo"
3739
"github.com/opencontainers/runtime-spec/specs-go"
3840
"github.com/opencontainers/selinux/go-selinux/label"
@@ -275,3 +277,92 @@ func modifyProcessLabel(runtimeType string, spec *specs.Spec) error {
275277
spec.Process.SelinuxLabel = l
276278
return nil
277279
}
280+
281+
func parseUsernsIDMap(runtimeIDMap []*runtime.IDMapping) ([]specs.LinuxIDMapping, error) {
282+
var m []specs.LinuxIDMapping
283+
284+
if len(runtimeIDMap) == 0 {
285+
return m, nil
286+
}
287+
288+
if len(runtimeIDMap) > 1 {
289+
// We only accept 1 line, because containerd.WithRemappedSnapshot() only supports that.
290+
return m, fmt.Errorf("only one mapping line supported, got %v mapping lines", len(runtimeIDMap))
291+
}
292+
293+
// We know len is 1 now.
294+
if runtimeIDMap[0] == nil {
295+
return m, nil
296+
}
297+
uidMap := *runtimeIDMap[0]
298+
299+
if uidMap.Length < 1 {
300+
return m, fmt.Errorf("invalid mapping length: %v", uidMap.Length)
301+
}
302+
303+
m = []specs.LinuxIDMapping{
304+
{
305+
ContainerID: uidMap.ContainerId,
306+
HostID: uidMap.HostId,
307+
Size: uidMap.Length,
308+
},
309+
}
310+
311+
return m, nil
312+
}
313+
314+
func parseUsernsIDs(userns *runtime.UserNamespace) (uids, gids []specs.LinuxIDMapping, retErr error) {
315+
if userns == nil {
316+
// If userns is not set, the kubelet doesn't support this option
317+
// and we should just fallback to no userns. This is completely
318+
// valid.
319+
return nil, nil, nil
320+
}
321+
322+
uidRuntimeMap := userns.GetUids()
323+
gidRuntimeMap := userns.GetGids()
324+
325+
uids, err := parseUsernsIDMap(uidRuntimeMap)
326+
if err != nil {
327+
return nil, nil, fmt.Errorf("UID mapping: %w", err)
328+
}
329+
330+
gids, err = parseUsernsIDMap(gidRuntimeMap)
331+
if err != nil {
332+
return nil, nil, fmt.Errorf("GID mapping: %w", err)
333+
}
334+
335+
switch mode := userns.GetMode(); mode {
336+
case runtime.NamespaceMode_NODE:
337+
if len(uids) != 0 || len(gids) != 0 {
338+
return nil, nil, fmt.Errorf("can't use user namespace mode %q with mappings. Got %v UID mappings and %v GID mappings", mode, len(uids), len(gids))
339+
}
340+
case runtime.NamespaceMode_POD:
341+
// This is valid, we will handle it in WithPodNamespaces().
342+
if len(uids) == 0 || len(gids) == 0 {
343+
return nil, nil, fmt.Errorf("can't use user namespace mode %q without UID and GID mappings", mode)
344+
}
345+
default:
346+
return nil, nil, fmt.Errorf("unsupported user namespace mode: %q", mode)
347+
}
348+
349+
return uids, gids, nil
350+
}
351+
352+
func snapshotterRemapOpts(nsOpts *runtime.NamespaceOption) ([]snapshots.Opt, error) {
353+
snapshotOpt := []snapshots.Opt{}
354+
usernsOpts := nsOpts.GetUsernsOptions()
355+
if usernsOpts == nil {
356+
return snapshotOpt, nil
357+
}
358+
359+
uids, gids, err := parseUsernsIDs(usernsOpts)
360+
if err != nil {
361+
return nil, fmt.Errorf("user namespace configuration: %w", err)
362+
}
363+
364+
if usernsOpts.GetMode() == runtime.NamespaceMode_POD {
365+
snapshotOpt = append(snapshotOpt, containerd.WithRemapperLabels(0, uids[0].HostID, 0, gids[0].HostID, uids[0].Size))
366+
}
367+
return snapshotOpt, nil
368+
}

pkg/cri/server/sandbox_run.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,17 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
158158
if err != nil {
159159
return nil, fmt.Errorf("failed to generate runtime options: %w", err)
160160
}
161-
snapshotterOpt := snapshots.WithLabels(snapshots.FilterInheritedLabels(config.Annotations))
161+
162+
sOpts := []snapshots.Opt{snapshots.WithLabels(snapshots.FilterInheritedLabels(config.Annotations))}
163+
extraSOpts, err := sandboxSnapshotterOpts(config)
164+
if err != nil {
165+
return nil, err
166+
}
167+
sOpts = append(sOpts, extraSOpts...)
168+
162169
opts := []containerd.NewContainerOpts{
163170
containerd.WithSnapshotter(c.runtimeSnapshotter(ctx, ociRuntime)),
164-
customopts.WithNewSnapshot(id, containerdImage, snapshotterOpt),
171+
customopts.WithNewSnapshot(id, containerdImage, sOpts...),
165172
containerd.WithSpec(spec, specOpts...),
166173
containerd.WithContainerLabels(sandboxLabels),
167174
containerd.WithContainerExtension(sandboxMetadataExtension, &sandbox.Metadata),

pkg/cri/server/sandbox_run_linux.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/containerd/containerd"
2626
"github.com/containerd/containerd/oci"
2727
"github.com/containerd/containerd/plugin"
28+
"github.com/containerd/containerd/snapshots"
2829
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
2930
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
3031
selinux "github.com/opencontainers/selinux/go-selinux"
@@ -358,3 +359,10 @@ func (c *criService) updateNetNamespacePath(spec *runtimespec.Spec, nsPath strin
358359
}
359360
}
360361
}
362+
363+
// sandboxSnapshotterOpts generates any platform specific snapshotter options
364+
// for a sandbox container.
365+
func sandboxSnapshotterOpts(config *runtime.PodSandboxConfig) ([]snapshots.Opt, error) {
366+
nsOpts := config.GetLinux().GetSecurityContext().GetNamespaceOptions()
367+
return snapshotterRemapOpts(nsOpts)
368+
}

pkg/cri/server/sandbox_run_other.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ package server
2121
import (
2222
"github.com/containerd/containerd"
2323
"github.com/containerd/containerd/oci"
24+
"github.com/containerd/containerd/snapshots"
2425
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
2526
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
2627
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
@@ -56,3 +57,9 @@ func (c *criService) taskOpts(runtimeType string) []containerd.NewTaskOpts {
5657

5758
func (c *criService) updateNetNamespacePath(spec *runtimespec.Spec, nsPath string) {
5859
}
60+
61+
// sandboxSnapshotterOpts generates any platform specific snapshotter options
62+
// for a sandbox container.
63+
func sandboxSnapshotterOpts(config *runtime.PodSandboxConfig) ([]snapshots.Opt, error) {
64+
return []snapshots.Opt{}, nil
65+
}

0 commit comments

Comments
 (0)