From 597815af7e7255082392e65abe5c802e5de57d9a Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 23 Mar 2017 15:40:09 -0700 Subject: [PATCH 1/2] Generate spec based on image config Signed-off-by: Michael Crosby --- cmd/ctr/run.go | 148 +++++++++++++++++++++++++++++++++-------------- linux/runtime.go | 2 +- 2 files changed, 107 insertions(+), 43 deletions(-) diff --git a/cmd/ctr/run.go b/cmd/ctr/run.go index 6ebce25..a25b206 100644 --- a/cmd/ctr/run.go +++ b/cmd/ctr/run.go @@ -3,30 +3,89 @@ package main import ( gocontext "context" "encoding/json" - "io/ioutil" + "fmt" "path/filepath" "runtime" + "strconv" + "strings" "google.golang.org/grpc" "google.golang.org/grpc/codes" - "github.com/Sirupsen/logrus" "github.com/crosbymichael/console" "github.com/docker/containerd/api/services/execution" rootfsapi "github.com/docker/containerd/api/services/rootfs" "github.com/docker/containerd/images" protobuf "github.com/gogo/protobuf/types" "github.com/opencontainers/image-spec/identity" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/urfave/cli" ) -var rwm = "rwm" +const ( + rwm = "rwm" + rootfsPath = "rootfs" +) -const rootfsPath = "rootfs" +var capabilities = []string{ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE", +} -func spec(id string, args []string, tty bool) *specs.Spec { +func spec(id string, config *ocispec.ImageConfig, context *cli.Context) (*specs.Spec, error) { + env := []string{ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + } + env = append(env, config.Env...) + var ( + args = append(config.Entrypoint, config.Cmd...) + tty = context.Bool("tty") + uid, gid uint32 + ) + if config.User != "" { + parts := strings.Split(config.User, ":") + switch len(parts) { + case 1: + v, err := strconv.ParseUint(parts[0], 0, 10) + if err != nil { + return nil, err + } + uid, gid = uint32(v), uint32(v) + case 2: + v, err := strconv.ParseUint(parts[0], 0, 10) + if err != nil { + return nil, err + } + uid = uint32(v) + if v, err = strconv.ParseUint(parts[1], 0, 10); err != nil { + return nil, err + } + gid = uint32(v) + default: + return nil, fmt.Errorf("invalid USER value %s", config.User) + } + } + if tty { + env = append(env, "TERM=xterm") + } + cwd := config.WorkingDir + if cwd == "" { + cwd = "/" + } return &specs.Spec{ Version: specs.Version, Platform: specs.Platform{ @@ -35,16 +94,25 @@ func spec(id string, args []string, tty bool) *specs.Spec { }, Root: specs.Root{ Path: rootfsPath, - Readonly: true, + Readonly: context.Bool("readonly"), }, Process: specs.Process{ - Args: args, - Env: []string{ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - }, + Args: args, + Env: env, Terminal: tty, - Cwd: "/", + Cwd: cwd, NoNewPrivileges: true, + User: specs.User{ + UID: uid, + GID: gid, + }, + Capabilities: &specs.LinuxCapabilities{ + Bounding: capabilities, + Permitted: capabilities, + Inheritable: capabilities, + Effective: capabilities, + Ambient: capabilities, + }, }, Mounts: []specs.Mount{ { @@ -80,7 +148,7 @@ func spec(id string, args []string, tty bool) *specs.Spec { Destination: "/sys", Type: "sysfs", Source: "sysfs", - Options: []string{"nosuid", "noexec", "nodev"}, + Options: []string{"nosuid", "noexec", "nodev", "ro"}, }, { Destination: "/run", @@ -135,23 +203,7 @@ func spec(id string, args []string, tty bool) *specs.Spec { }, }, }, - } -} - -func customSpec(configPath string) (*specs.Spec, error) { - b, err := ioutil.ReadFile(configPath) - if err != nil { - return nil, err - } - var s specs.Spec - if err := json.Unmarshal(b, &s); err != nil { - return nil, err - } - if s.Root.Path != rootfsPath { - logrus.Warnf("ignoring Root.Path %q, setting %q forcibly", s.Root.Path, rootfsPath) - s.Root.Path = rootfsPath - } - return &s, nil + }, nil } var runCommand = cli.Command{ @@ -170,15 +222,15 @@ var runCommand = cli.Command{ Name: "rootfs,r", Usage: "path to the container's root filesystem", }, - cli.StringFlag{ - Name: "runtime-config", - Usage: "custom runtime config (config.json)", - }, cli.StringFlag{ Name: "runtime", Usage: "runtime name (linux, windows, vmware-linux)", Value: "linux", }, + cli.BoolFlag{ + Name: "readonly", + Usage: "set the containers filesystem as readonly", + }, }, Action: func(context *cli.Context) error { ctx := gocontext.Background() @@ -253,16 +305,30 @@ var runCommand = cli.Command{ return err } - rootfs := resp.Mounts - - var s *specs.Spec - if config := context.String("runtime-config"); config == "" { - s = spec(id, []string(context.Args().Tail()), context.Bool("tty")) - } else { - s, err = customSpec(config) + ic, err := image.Config(ctx, provider) + if err != nil { + return err + } + var imageConfig ocispec.Image + switch ic.MediaType { + case ocispec.MediaTypeImageConfig, "application/vnd.docker.container.image.v1+json": + r, err := provider.Reader(ctx, ic.Digest) if err != nil { return err } + if err := json.NewDecoder(r).Decode(&imageConfig); err != nil { + r.Close() + return err + } + r.Close() + default: + return fmt.Errorf("unknown image config media type %s", ic.MediaType) + } + rootfs := resp.Mounts + // generate the spec based on the image config + s, err := spec(id, &imageConfig.Config, context) + if err != nil { + return err } data, err := json.Marshal(s) if err != nil { @@ -292,12 +358,10 @@ var runCommand = cli.Command{ if err != nil { return err } - response, err := containers.Create(gocontext.Background(), create) if err != nil { return err } - if _, err := containers.Start(gocontext.Background(), &execution.StartRequest{ ID: response.ID, }); err != nil { diff --git a/linux/runtime.go b/linux/runtime.go index ff9c396..c239e3c 100644 --- a/linux/runtime.go +++ b/linux/runtime.go @@ -41,7 +41,7 @@ type Config struct { } func New(ic *plugin.InitContext) (interface{}, error) { - path := filepath.Join(ic.State, runtimeName) + path := filepath.Join(ic.Root, runtimeName) if err := os.MkdirAll(path, 0700); err != nil { return nil, err } From 71e8d765df946ffa3232618a427949f9ee0c463d Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 24 Mar 2017 12:47:52 -0700 Subject: [PATCH 2/2] set +x on overlay fs dirs We need to set +x on the overlay dirs or after dropping from root to a non-root user an eperm will happen on exec or other file access Signed-off-by: Michael Crosby --- cmd/ctr/run.go | 9 ++++++++- linux/runtime.go | 2 +- snapshot/overlay/overlay.go | 3 +-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/cmd/ctr/run.go b/cmd/ctr/run.go index a25b206..ce2a959 100644 --- a/cmd/ctr/run.go +++ b/cmd/ctr/run.go @@ -113,6 +113,13 @@ func spec(id string, config *ocispec.ImageConfig, context *cli.Context) (*specs. Effective: capabilities, Ambient: capabilities, }, + Rlimits: []specs.LinuxRlimit{ + { + Type: "RLIMIT_NOFILE", + Hard: uint64(1024), + Soft: uint64(1024), + }, + }, }, Mounts: []specs.Mount{ { @@ -311,7 +318,7 @@ var runCommand = cli.Command{ } var imageConfig ocispec.Image switch ic.MediaType { - case ocispec.MediaTypeImageConfig, "application/vnd.docker.container.image.v1+json": + case ocispec.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config: r, err := provider.Reader(ctx, ic.Digest) if err != nil { return err diff --git a/linux/runtime.go b/linux/runtime.go index c239e3c..ff9c396 100644 --- a/linux/runtime.go +++ b/linux/runtime.go @@ -41,7 +41,7 @@ type Config struct { } func New(ic *plugin.InitContext) (interface{}, error) { - path := filepath.Join(ic.Root, runtimeName) + path := filepath.Join(ic.State, runtimeName) if err := os.MkdirAll(path, 0700); err != nil { return nil, err } diff --git a/snapshot/overlay/overlay.go b/snapshot/overlay/overlay.go index 37c54fe..456b5e8 100644 --- a/snapshot/overlay/overlay.go +++ b/snapshot/overlay/overlay.go @@ -47,7 +47,6 @@ func NewSnapshotter(root string, ms storage.MetaStore) (snapshot.Snapshotter, er if err := os.MkdirAll(root, 0700); err != nil { return nil, err } - if err := os.MkdirAll(filepath.Join(root, "snapshots"), 0700); err != nil { return nil, err } @@ -189,7 +188,7 @@ func (o *Snapshotter) createActive(ctx context.Context, key, parent string, read } }() - if err = os.MkdirAll(filepath.Join(td, "fs"), 0700); err != nil { + if err = os.MkdirAll(filepath.Join(td, "fs"), 0711); err != nil { return nil, err } if !readonly {