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 <rodrigoca@microsoft.com> Signed-off-by: Rodrigo Campos <rodrigoca@microsoft.com> Signed-off-by: David Leadbeater <dgl@dgl.cx>
This commit is contained in:
		 David Leadbeater
					David Leadbeater
				
			
				
					committed by
					
						 Rodrigo Campos
						Rodrigo Campos
					
				
			
			
				
	
			
			
			 Rodrigo Campos
						Rodrigo Campos
					
				
			
						parent
						
							36f520dc04
						
					
				
				
					commit
					31a6449734
				
			| @@ -28,11 +28,13 @@ import ( | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/log" | ||||
| 	"github.com/containerd/containerd/mount" | ||||
| 	"github.com/containerd/containerd/pkg/apparmor" | ||||
| 	"github.com/containerd/containerd/pkg/seccomp" | ||||
| 	"github.com/containerd/containerd/pkg/seutil" | ||||
| 	"github.com/containerd/containerd/snapshots" | ||||
| 	"github.com/moby/sys/mountinfo" | ||||
| 	"github.com/opencontainers/runtime-spec/specs-go" | ||||
| 	"github.com/opencontainers/selinux/go-selinux/label" | ||||
| @@ -275,3 +277,92 @@ func modifyProcessLabel(runtimeType string, spec *specs.Spec) error { | ||||
| 	spec.Process.SelinuxLabel = l | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func parseUsernsIDMap(runtimeIDMap []*runtime.IDMapping) ([]specs.LinuxIDMapping, error) { | ||||
| 	var m []specs.LinuxIDMapping | ||||
|  | ||||
| 	if len(runtimeIDMap) == 0 { | ||||
| 		return m, nil | ||||
| 	} | ||||
|  | ||||
| 	if len(runtimeIDMap) > 1 { | ||||
| 		// We only accept 1 line, because containerd.WithRemappedSnapshot() only supports that. | ||||
| 		return m, fmt.Errorf("only one mapping line supported, got %v mapping lines", len(runtimeIDMap)) | ||||
| 	} | ||||
|  | ||||
| 	// We know len is 1 now. | ||||
| 	if runtimeIDMap[0] == nil { | ||||
| 		return m, nil | ||||
| 	} | ||||
| 	uidMap := *runtimeIDMap[0] | ||||
|  | ||||
| 	if uidMap.Length < 1 { | ||||
| 		return m, fmt.Errorf("invalid mapping length: %v", uidMap.Length) | ||||
| 	} | ||||
|  | ||||
| 	m = []specs.LinuxIDMapping{ | ||||
| 		{ | ||||
| 			ContainerID: uidMap.ContainerId, | ||||
| 			HostID:      uidMap.HostId, | ||||
| 			Size:        uidMap.Length, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	return m, nil | ||||
| } | ||||
|  | ||||
| func parseUsernsIDs(userns *runtime.UserNamespace) (uids, gids []specs.LinuxIDMapping, retErr error) { | ||||
| 	if userns == nil { | ||||
| 		// If userns is not set, the kubelet doesn't support this option | ||||
| 		// and we should just fallback to no userns. This is completely | ||||
| 		// valid. | ||||
| 		return nil, nil, nil | ||||
| 	} | ||||
|  | ||||
| 	uidRuntimeMap := userns.GetUids() | ||||
| 	gidRuntimeMap := userns.GetGids() | ||||
|  | ||||
| 	uids, err := parseUsernsIDMap(uidRuntimeMap) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, fmt.Errorf("UID mapping: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	gids, err = parseUsernsIDMap(gidRuntimeMap) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, fmt.Errorf("GID mapping: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	switch mode := userns.GetMode(); mode { | ||||
| 	case runtime.NamespaceMode_NODE: | ||||
| 		if len(uids) != 0 || len(gids) != 0 { | ||||
| 			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)) | ||||
| 		} | ||||
| 	case runtime.NamespaceMode_POD: | ||||
| 		// This is valid, we will handle it in WithPodNamespaces(). | ||||
| 		if len(uids) == 0 || len(gids) == 0 { | ||||
| 			return nil, nil, fmt.Errorf("can't use user namespace mode %q without UID and GID mappings", mode) | ||||
| 		} | ||||
| 	default: | ||||
| 		return nil, nil, fmt.Errorf("unsupported user namespace mode: %q", mode) | ||||
| 	} | ||||
|  | ||||
| 	return uids, gids, nil | ||||
| } | ||||
|  | ||||
| func snapshotterRemapOpts(nsOpts *runtime.NamespaceOption) ([]snapshots.Opt, error) { | ||||
| 	snapshotOpt := []snapshots.Opt{} | ||||
| 	usernsOpts := nsOpts.GetUsernsOptions() | ||||
| 	if usernsOpts == nil { | ||||
| 		return snapshotOpt, nil | ||||
| 	} | ||||
|  | ||||
| 	uids, gids, err := parseUsernsIDs(usernsOpts) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("user namespace configuration: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	if usernsOpts.GetMode() == runtime.NamespaceMode_POD { | ||||
| 		snapshotOpt = append(snapshotOpt, containerd.WithRemapperLabels(0, uids[0].HostID, 0, gids[0].HostID, uids[0].Size)) | ||||
| 	} | ||||
| 	return snapshotOpt, nil | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user