Skip to content

Commit

Permalink
support user remapping in ctr
Browse files Browse the repository at this point in the history
* --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>
  • Loading branch information
liaojh1998 committed Dec 12, 2019
1 parent f01665a commit 9862cb8
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 5 deletions.
79 changes: 74 additions & 5 deletions cmd/ctr/commands/run/run_unix.go
Expand Up @@ -20,7 +20,9 @@ package run

import (
gocontext "context"
"fmt"
"path/filepath"
"strconv"
"strings"

"github.com/containerd/containerd"
Expand All @@ -44,6 +46,14 @@ var platformRunFlags = []cli.Flag{
Name: "runc-systemd-cgroup",
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
Expand Down Expand Up @@ -115,12 +125,30 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
opts = append(opts, oci.WithImageConfig(image))
cOpts = append(cOpts,
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)
// We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only,
// after creating some mount points on demand.
containerd.WithNewSnapshot(id, image),
containerd.WithImageStopSignal(image, "SIGTERM"))
cOpts = append(cOpts, containerd.WithNewSnapshot(id, image))
}
cOpts = append(cOpts, containerd.WithImageStopSignal(image, "SIGTERM"))
}
if context.Bool("read-only") {
opts = append(opts, oci.WithRootFSReadonly())
Expand Down Expand Up @@ -210,10 +238,51 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
}

func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts {
var (
tOpts []containerd.NewTaskOpts
)
if context.Bool("no-pivot") {
return []containerd.NewTaskOpts{containerd.WithNoPivotRoot}
tOpts = append(tOpts, containerd.WithNoPivotRoot)
}
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 nil
return specs.LinuxIDMapping{
ContainerID: uint32(cID),
HostID: uint32(hID),
Size: uint32(size),
}, nil
}

func validNamespace(ns string) bool {
Expand Down
52 changes: 52 additions & 0 deletions task_opts_unix.go
Expand Up @@ -103,3 +103,55 @@ func WithShimCgroup(path string) NewTaskOpts {
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
}
}

0 comments on commit 9862cb8

Please sign in to comment.