From 3bffae8fe7b4898bc72b6c4db48db0a60d9b2f56 Mon Sep 17 00:00:00 2001 From: Justin Cormack Date: Wed, 5 Apr 2017 13:11:45 +0100 Subject: [PATCH] Allow overriding the default mount options This refactors the mount handling, without changing any defaults. Any specification of a mount destination will override the default, so if you want to make `sysfs` read only you can add ``` mounts: - type: sysfs options: ["ro"] ``` Signed-off-by: Justin Cormack --- src/cmd/moby/config.go | 92 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 12 deletions(-) diff --git a/src/cmd/moby/config.go b/src/cmd/moby/config.go index d90860f89..50e5de538 100644 --- a/src/cmd/moby/config.go +++ b/src/cmd/moby/config.go @@ -6,7 +6,10 @@ import ( "encoding/json" "errors" "fmt" + "os" "path" + "path/filepath" + "sort" "strings" log "github.com/Sirupsen/logrus" @@ -92,6 +95,39 @@ func ConfigToOCI(image *MobyImage) ([]byte, error) { return ConfigInspectToOCI(image, inspect) } +func defaultMountpoint(tp string) string { + switch tp { + case "proc": + return "/proc" + case "devpts": + return "/dev/pts" + case "sysfs": + return "/sys" + case "cgroup": + return "/sys/fs/cgroup" + case "mqueue": + return "/dev/mqueue" + default: + return "" + } +} + +// Sort mounts by number of path components so /dev/pts is listed after /dev +type mlist []specs.Mount + +func (m mlist) Len() int { + return len(m) +} +func (m mlist) Less(i, j int) bool { + return m.parts(i) < m.parts(j) +} +func (m mlist) Swap(i, j int) { + m[i], m[j] = m[j], m[i] +} +func (m mlist) parts(i int) int { + return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator)) +} + // ConfigInspectToOCI converts a config and the output of image inspect to an OCI config file func ConfigInspectToOCI(image *MobyImage, inspect types.ImageInspect) ([]byte, error) { oci := specs.Spec{} @@ -116,6 +152,7 @@ func ConfigInspectToOCI(image *MobyImage, inspect types.ImageInspect) ([]byte, e if cwd == "" { cwd = "/" } + // default options match what Docker does procOptions := []string{"nosuid", "nodev", "noexec", "relatime"} devOptions := []string{"nosuid", "strictatime", "mode=755", "size=65536k"} if image.Readonly { @@ -128,15 +165,13 @@ func ConfigInspectToOCI(image *MobyImage, inspect types.ImageInspect) ([]byte, e } cgroupOptions := []string{"nosuid", "noexec", "nodev", "relatime", "ro"} // note omits "standard" /dev/shm and /dev/mqueue - mounts := []specs.Mount{ - {Destination: "/proc", Type: "proc", Source: "proc", Options: procOptions}, - {Destination: "/dev", Type: "tmpfs", Source: "tmpfs", Options: devOptions}, - {Destination: "/dev/pts", Type: "devpts", Source: "devpts", Options: ptsOptions}, - {Destination: "/sys", Type: "sysfs", Source: "sysfs", Options: sysOptions}, - {Destination: "/sys/fs/cgroup", Type: "cgroup", Source: "cgroup", Options: cgroupOptions}, + mounts := map[string]specs.Mount{ + "/proc": {Destination: "/proc", Type: "proc", Source: "proc", Options: procOptions}, + "/dev": {Destination: "/dev", Type: "tmpfs", Source: "tmpfs", Options: devOptions}, + "/dev/pts": {Destination: "/dev/pts", Type: "devpts", Source: "devpts", Options: ptsOptions}, + "/sys": {Destination: "/sys", Type: "sysfs", Source: "sysfs", Options: sysOptions}, + "/sys/fs/cgroup": {Destination: "/sys/fs/cgroup", Type: "cgroup", Source: "cgroup", Options: cgroupOptions}, } - // TODO if any standard mount points supplied, remove from above, so can change options - mounts = append(mounts, image.Mounts...) for _, t := range image.Tmpfs { parts := strings.Split(t, ":") if len(parts) > 2 { @@ -147,7 +182,7 @@ func ConfigInspectToOCI(image *MobyImage, inspect types.ImageInspect) ([]byte, e if len(parts) == 2 { opts = strings.Split(parts[2], ",") } - mounts = append(mounts, specs.Mount{Destination: dest, Type: "tmpfs", Source: "tmpfs", Options: opts}) + mounts[dest] = specs.Mount{Destination: dest, Type: "tmpfs", Source: "tmpfs", Options: opts} } for _, b := range image.Binds { parts := strings.Split(b, ":") @@ -163,9 +198,42 @@ func ConfigInspectToOCI(image *MobyImage, inspect types.ImageInspect) ([]byte, e if len(parts) == 3 { opts = strings.Split(parts[2], ",") } - mounts = append(mounts, specs.Mount{Destination: dest, Type: "bind", Source: src, Options: opts}) + mounts[dest] = specs.Mount{Destination: dest, Type: "bind", Source: src, Options: opts} } - + for _, m := range image.Mounts { + tp := m.Type + src := m.Source + dest := m.Destination + opts := m.Options + if tp == "" { + switch src { + case "mqueue", "devpts", "proc", "sysfs", "cgroup": + tp = src + } + } + if tp == "" && dest == "/dev" { + tp = "tmpfs" + } + if tp == "" { + return []byte{}, fmt.Errorf("Mount for destination %s is missing type", dest) + } + if src == "" { + // usually sane, eg proc, tmpfs etc + src = tp + } + if dest == "" { + dest = defaultMountpoint(tp) + } + if dest == "" { + return []byte{}, fmt.Errorf("Mount type %s is missing destination", tp) + } + mounts[dest] = specs.Mount{Destination: dest, Type: tp, Source: src, Options: opts} + } + mountList := mlist{} + for _, m := range mounts { + mountList = append(mountList, m) + } + sort.Sort(mountList) namespaces := []specs.LinuxNamespace{} if image.Net != "" && image.Net != "host" { return []byte{}, fmt.Errorf("invalid net namespace: %s", image.Net) @@ -232,7 +300,7 @@ func ConfigInspectToOCI(image *MobyImage, inspect types.ImageInspect) ([]byte, e } oci.Hostname = image.Hostname - oci.Mounts = mounts + oci.Mounts = mountList oci.Linux = &specs.Linux{ // UIDMappings