support user remapping in ctr
* --uidmap support for one remapping * --gidmap support for one remapping * create IoUid and IoGid options for getNewTaskOpts Signed-off-by: Jie Hao Liao <liaojh1998@gmail.com>
This commit is contained in:
parent
f01665aa02
commit
9862cb8f85
@ -20,7 +20,9 @@ package run
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
gocontext "context"
|
gocontext "context"
|
||||||
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
@ -44,6 +46,14 @@ var platformRunFlags = []cli.Flag{
|
|||||||
Name: "runc-systemd-cgroup",
|
Name: "runc-systemd-cgroup",
|
||||||
Usage: "start runc with systemd cgroup manager",
|
Usage: "start runc with systemd cgroup manager",
|
||||||
},
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "uidmap",
|
||||||
|
Usage: "run inside a user namespace with the specified UID mapping range; specified with the format `container-uid:host-uid:length`",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "gidmap",
|
||||||
|
Usage: "run inside a user namespace with the specified GID mapping range; specified with the format `container-gid:host-gid:length`",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContainer creates a new container
|
// NewContainer creates a new container
|
||||||
@ -115,12 +125,30 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
|
|||||||
opts = append(opts, oci.WithImageConfig(image))
|
opts = append(opts, oci.WithImageConfig(image))
|
||||||
cOpts = append(cOpts,
|
cOpts = append(cOpts,
|
||||||
containerd.WithImage(image),
|
containerd.WithImage(image),
|
||||||
containerd.WithSnapshotter(snapshotter),
|
containerd.WithSnapshotter(snapshotter))
|
||||||
|
if uidmap, gidmap := context.String("uidmap"), context.String("gidmap"); uidmap != "" && gidmap != "" {
|
||||||
|
uidMap, err := parseIDMapping(uidmap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
gidMap, err := parseIDMapping(gidmap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts = append(opts,
|
||||||
|
oci.WithUserNamespace([]specs.LinuxIDMapping{uidMap}, []specs.LinuxIDMapping{gidMap}))
|
||||||
|
if context.Bool("read-only") {
|
||||||
|
cOpts = append(cOpts, containerd.WithRemappedSnapshotView(id, image, uidMap.HostID, gidMap.HostID))
|
||||||
|
} else {
|
||||||
|
cOpts = append(cOpts, containerd.WithRemappedSnapshot(id, image, uidMap.HostID, gidMap.HostID))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Even when "read-only" is set, we don't use KindView snapshot here. (#1495)
|
// Even when "read-only" is set, we don't use KindView snapshot here. (#1495)
|
||||||
// We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only,
|
// We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only,
|
||||||
// after creating some mount points on demand.
|
// after creating some mount points on demand.
|
||||||
containerd.WithNewSnapshot(id, image),
|
cOpts = append(cOpts, containerd.WithNewSnapshot(id, image))
|
||||||
containerd.WithImageStopSignal(image, "SIGTERM"))
|
}
|
||||||
|
cOpts = append(cOpts, containerd.WithImageStopSignal(image, "SIGTERM"))
|
||||||
}
|
}
|
||||||
if context.Bool("read-only") {
|
if context.Bool("read-only") {
|
||||||
opts = append(opts, oci.WithRootFSReadonly())
|
opts = append(opts, oci.WithRootFSReadonly())
|
||||||
@ -210,10 +238,51 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts {
|
func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts {
|
||||||
|
var (
|
||||||
|
tOpts []containerd.NewTaskOpts
|
||||||
|
)
|
||||||
if context.Bool("no-pivot") {
|
if context.Bool("no-pivot") {
|
||||||
return []containerd.NewTaskOpts{containerd.WithNoPivotRoot}
|
tOpts = append(tOpts, containerd.WithNoPivotRoot)
|
||||||
}
|
}
|
||||||
return nil
|
if uidmap := context.String("uidmap"); uidmap != "" {
|
||||||
|
uidMap, err := parseIDMapping(uidmap)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("warning: expected to parse uidmap: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
tOpts = append(tOpts, containerd.WithUIDOwner(uidMap.HostID))
|
||||||
|
}
|
||||||
|
if gidmap := context.String("gidmap"); gidmap != "" {
|
||||||
|
gidMap, err := parseIDMapping(gidmap)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("warning: expected to parse uidmap: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
tOpts = append(tOpts, containerd.WithGIDOwner(gidMap.HostID))
|
||||||
|
}
|
||||||
|
return tOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseIDMapping(mapping string) (specs.LinuxIDMapping, error) {
|
||||||
|
parts := strings.Split(mapping, ":")
|
||||||
|
if len(parts) != 3 {
|
||||||
|
return specs.LinuxIDMapping{}, errors.New("user namespace mappings require the format `container-id:host-id:size`")
|
||||||
|
}
|
||||||
|
cID, err := strconv.ParseUint(parts[0], 0, 16)
|
||||||
|
if err != nil {
|
||||||
|
return specs.LinuxIDMapping{}, errors.Wrapf(err, "invalid container id for user namespace remapping")
|
||||||
|
}
|
||||||
|
hID, err := strconv.ParseUint(parts[1], 0, 16)
|
||||||
|
if err != nil {
|
||||||
|
return specs.LinuxIDMapping{}, errors.Wrapf(err, "invalid host id for user namespace remapping")
|
||||||
|
}
|
||||||
|
size, err := strconv.ParseUint(parts[2], 0, 16)
|
||||||
|
if err != nil {
|
||||||
|
return specs.LinuxIDMapping{}, errors.Wrapf(err, "invalid size for user namespace remapping")
|
||||||
|
}
|
||||||
|
return specs.LinuxIDMapping{
|
||||||
|
ContainerID: uint32(cID),
|
||||||
|
HostID: uint32(hID),
|
||||||
|
Size: uint32(size),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validNamespace(ns string) bool {
|
func validNamespace(ns string) bool {
|
||||||
|
@ -103,3 +103,55 @@ func WithShimCgroup(path string) NewTaskOpts {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithUIDOwner allows console I/O to work with the remapped UID in user namespace
|
||||||
|
func WithUIDOwner(uid uint32) NewTaskOpts {
|
||||||
|
return func(ctx context.Context, c *Client, ti *TaskInfo) error {
|
||||||
|
if CheckRuntime(ti.Runtime(), "io.containerd.runc") {
|
||||||
|
if ti.Options == nil {
|
||||||
|
ti.Options = &options.Options{}
|
||||||
|
}
|
||||||
|
opts, ok := ti.Options.(*options.Options)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid v2 shim create options format")
|
||||||
|
}
|
||||||
|
opts.IoUid = uid
|
||||||
|
} else {
|
||||||
|
if ti.Options == nil {
|
||||||
|
ti.Options = &runctypes.CreateOptions{}
|
||||||
|
}
|
||||||
|
opts, ok := ti.Options.(*runctypes.CreateOptions)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("could not cast TaskInfo Options to CreateOptions")
|
||||||
|
}
|
||||||
|
opts.IoUid = uid
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithGIDOwner allows console I/O to work with the remapped GID in user namespace
|
||||||
|
func WithGIDOwner(gid uint32) NewTaskOpts {
|
||||||
|
return func(ctx context.Context, c *Client, ti *TaskInfo) error {
|
||||||
|
if CheckRuntime(ti.Runtime(), "io.containerd.runc") {
|
||||||
|
if ti.Options == nil {
|
||||||
|
ti.Options = &options.Options{}
|
||||||
|
}
|
||||||
|
opts, ok := ti.Options.(*options.Options)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid v2 shim create options format")
|
||||||
|
}
|
||||||
|
opts.IoGid = gid
|
||||||
|
} else {
|
||||||
|
if ti.Options == nil {
|
||||||
|
ti.Options = &runctypes.CreateOptions{}
|
||||||
|
}
|
||||||
|
opts, ok := ti.Options.(*runctypes.CreateOptions)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("could not cast TaskInfo Options to CreateOptions")
|
||||||
|
}
|
||||||
|
opts.IoGid = gid
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user