From 4c4356692580afb3971094e322aea64abe0e2500 Mon Sep 17 00:00:00 2001 From: Dan Walsh Date: Tue, 18 Mar 2014 16:49:16 -0400 Subject: [PATCH] This patch adds SELinux labeling support. docker will run the process(es) within the container with an SELinux label and will label all of the content within the container with mount label. Any temporary file systems created within the container need to be mounted with the same mount label. The user can override the process label by specifying -Z With a string of space separated options. -Z "user=unconfined_u role=unconfined_r type=unconfined_t level=s0" Would cause the process label to run with unconfined_u:unconfined_r:unconfined_t:s0" By default the processes will run execute within the container as svirt_lxc_net_t. All of the content in the container as svirt_sandbox_file_t. The process mcs level is based of the PID of the docker process that is creating the container. If you run the container in --priv mode, the labeling will be disabled. Docker-DCO-1.1-Signed-off-by: Dan Walsh (github: rhatdan) --- Dockerfile | 2 +- graph/graph.go | 2 +- hack/PACKAGERS.md | 7 + pkg/label/label.go | 23 ++ pkg/label/label_selinux.go | 69 ++++ pkg/libcontainer/nsinit/execin.go | 11 +- pkg/libcontainer/nsinit/init.go | 8 +- pkg/libcontainer/nsinit/mount.go | 22 +- pkg/selinux/selinux.go | 387 ++++++++++++++++++ pkg/selinux/selinux_test.go | 64 +++ runconfig/config.go | 11 + runconfig/parse.go | 20 + runtime/container.go | 1 + runtime/execdriver/driver.go | 5 + runtime/execdriver/lxc/lxc_template.go | 20 +- runtime/execdriver/native/default_template.go | 2 + runtime/graphdriver/aufs/aufs.go | 2 +- runtime/graphdriver/aufs/aufs_test.go | 52 +-- runtime/graphdriver/aufs/migrate.go | 6 +- runtime/graphdriver/btrfs/btrfs.go | 6 +- runtime/graphdriver/devmapper/deviceset.go | 9 +- runtime/graphdriver/devmapper/driver.go | 15 +- runtime/graphdriver/devmapper/driver_test.go | 20 +- runtime/graphdriver/driver.go | 2 +- runtime/graphdriver/vfs/driver.go | 2 +- runtime/runtime.go | 4 +- 26 files changed, 700 insertions(+), 72 deletions(-) create mode 100644 pkg/label/label.go create mode 100644 pkg/label/label_selinux.go create mode 100644 pkg/selinux/selinux.go create mode 100644 pkg/selinux/selinux_test.go diff --git a/Dockerfile b/Dockerfile index 42438e3946..2de5b34171 100644 --- a/Dockerfile +++ b/Dockerfile @@ -87,7 +87,7 @@ RUN git config --global user.email 'docker-dummy@example.com' VOLUME /var/lib/docker WORKDIR /go/src/github.com/dotcloud/docker -ENV DOCKER_BUILDTAGS apparmor +ENV DOCKER_BUILDTAGS apparmor selinux # Wrap all commands in the "docker-in-docker" script to allow nested containers ENTRYPOINT ["hack/dind"] diff --git a/graph/graph.go b/graph/graph.go index 4349cac129..33aca486c6 100644 --- a/graph/graph.go +++ b/graph/graph.go @@ -189,7 +189,7 @@ func (graph *Graph) Register(jsonData []byte, layerData archive.ArchiveReader, i } // Create root filesystem in the driver - if err := graph.driver.Create(img.ID, img.Parent); err != nil { + if err := graph.driver.Create(img.ID, img.Parent, ""); err != nil { return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err) } // Mount the root filesystem so we can apply the diff/layer diff --git a/hack/PACKAGERS.md b/hack/PACKAGERS.md index 47e8413bf3..dc255c57ad 100644 --- a/hack/PACKAGERS.md +++ b/hack/PACKAGERS.md @@ -177,6 +177,13 @@ export DOCKER_BUILDTAGS='exclude_graphdriver_aufs' NOTE: if you need to set more than one build tag, space separate them. +If you're building a binary that may need to be used on platforms that include +SELinux, you will need to set `DOCKER_BUILDTAGS` as follows: + +```bash +export DOCKER_BUILDTAGS='selinux' +``` + ### Static Daemon If it is feasible within the constraints of your distribution, you should diff --git a/pkg/label/label.go b/pkg/label/label.go new file mode 100644 index 0000000000..ba1e9f48ea --- /dev/null +++ b/pkg/label/label.go @@ -0,0 +1,23 @@ +// +build !selinux !linux + +package label + +func GenLabels(options string) (string, string, error) { + return "", "", nil +} + +func FormatMountLabel(src string, MountLabel string) string { + return src +} + +func SetProcessLabel(processLabel string) error { + return nil +} + +func SetFileLabel(path string, fileLabel string) error { + return nil +} + +func GetPidCon(pid int) (string, error) { + return "", nil +} diff --git a/pkg/label/label_selinux.go b/pkg/label/label_selinux.go new file mode 100644 index 0000000000..300a8b6d14 --- /dev/null +++ b/pkg/label/label_selinux.go @@ -0,0 +1,69 @@ +// +build selinux,linux + +package label + +import ( + "fmt" + "github.com/dotcloud/docker/pkg/selinux" + "strings" +) + +func GenLabels(options string) (string, string, error) { + processLabel, mountLabel := selinux.GetLxcContexts() + var err error + if processLabel == "" { // SELinux is disabled + return "", "", err + } + s := strings.Fields(options) + l := len(s) + if l > 0 { + pcon := selinux.NewContext(processLabel) + for i := 0; i < l; i++ { + o := strings.Split(s[i], "=") + pcon[o[0]] = o[1] + } + processLabel = pcon.Get() + mountLabel, err = selinux.CopyLevel(processLabel, mountLabel) + } + return processLabel, mountLabel, err +} + +func FormatMountLabel(src string, MountLabel string) string { + var mountLabel string + if src != "" { + mountLabel = src + if MountLabel != "" { + mountLabel = fmt.Sprintf("%s,context=\"%s\"", mountLabel, MountLabel) + } + } else { + if MountLabel != "" { + mountLabel = fmt.Sprintf("context=\"%s\"", MountLabel) + } + } + return mountLabel +} + +func SetProcessLabel(processLabel string) error { + if selinux.SelinuxEnabled() { + return selinux.Setexeccon(processLabel) + } + return nil +} + +func GetProcessLabel() (string, error) { + if selinux.SelinuxEnabled() { + return selinux.Getexeccon() + } + return "", nil +} + +func SetFileLabel(path string, fileLabel string) error { + if selinux.SelinuxEnabled() && fileLabel != "" { + return selinux.Setfilecon(path, fileLabel) + } + return nil +} + +func GetPidCon(pid int) (string, error) { + return selinux.Getpidcon(pid) +} diff --git a/pkg/libcontainer/nsinit/execin.go b/pkg/libcontainer/nsinit/execin.go index f8b8931390..9017af06e9 100644 --- a/pkg/libcontainer/nsinit/execin.go +++ b/pkg/libcontainer/nsinit/execin.go @@ -4,6 +4,7 @@ package nsinit import ( "fmt" + "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/pkg/system" "os" @@ -32,7 +33,11 @@ func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []s closeFds() return -1, err } - + processLabel, err := label.GetPidCon(nspid) + if err != nil { + closeFds() + return -1, err + } // foreach namespace fd, use setns to join an existing container's namespaces for _, fd := range fds { if fd > 0 { @@ -80,6 +85,10 @@ dropAndExec: if err := finalizeNamespace(container); err != nil { return -1, err } + err = label.SetProcessLabel(processLabel) + if err != nil { + return -1, err + } if err := system.Execv(args[0], args[0:], container.Env); err != nil { return -1, err } diff --git a/pkg/libcontainer/nsinit/init.go b/pkg/libcontainer/nsinit/init.go index 117ae875ed..5aa5f9f5b5 100644 --- a/pkg/libcontainer/nsinit/init.go +++ b/pkg/libcontainer/nsinit/init.go @@ -4,6 +4,7 @@ package nsinit import ( "fmt" + "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/pkg/libcontainer/apparmor" "github.com/dotcloud/docker/pkg/libcontainer/capabilities" @@ -12,6 +13,7 @@ import ( "github.com/dotcloud/docker/pkg/system" "github.com/dotcloud/docker/pkg/user" "os" + "runtime" "syscall" ) @@ -57,7 +59,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol return fmt.Errorf("parent death signal %s", err) } ns.logger.Println("setup mount namespace") - if err := setupNewMountNamespace(rootfs, container.Mounts, console, container.ReadonlyFs, container.NoPivotRoot); err != nil { + if err := setupNewMountNamespace(rootfs, container.Mounts, console, container.ReadonlyFs, container.NoPivotRoot, container.Context["mount_label"]); err != nil { return fmt.Errorf("setup mount namespace %s", err) } if err := setupNetwork(container, context); err != nil { @@ -76,6 +78,10 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol return err } } + runtime.LockOSThread() + if err := label.SetProcessLabel(container.Context["process_label"]); err != nil { + return fmt.Errorf("SetProcessLabel label %s", err) + } ns.logger.Printf("execing %s\n", args[0]) return system.Execv(args[0], args[0:], container.Env) } diff --git a/pkg/libcontainer/nsinit/mount.go b/pkg/libcontainer/nsinit/mount.go index 61a90125e0..796143c68e 100644 --- a/pkg/libcontainer/nsinit/mount.go +++ b/pkg/libcontainer/nsinit/mount.go @@ -4,6 +4,7 @@ package nsinit import ( "fmt" + "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/pkg/system" "io/ioutil" @@ -20,7 +21,7 @@ const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NOD // // There is no need to unmount the new mounts because as soon as the mount namespace // is no longer in use, the mounts will be removed automatically -func setupNewMountNamespace(rootfs string, bindMounts []libcontainer.Mount, console string, readonly, noPivotRoot bool) error { +func setupNewMountNamespace(rootfs string, bindMounts []libcontainer.Mount, console string, readonly, noPivotRoot bool, mountLabel string) error { flag := syscall.MS_PRIVATE if noPivotRoot { flag = syscall.MS_SLAVE @@ -36,7 +37,7 @@ func setupNewMountNamespace(rootfs string, bindMounts []libcontainer.Mount, cons return fmt.Errorf("mounting %s as readonly %s", rootfs, err) } } - if err := mountSystem(rootfs); err != nil { + if err := mountSystem(rootfs, mountLabel); err != nil { return fmt.Errorf("mount system %s", err) } @@ -64,7 +65,7 @@ func setupNewMountNamespace(rootfs string, bindMounts []libcontainer.Mount, cons if err := setupDev(rootfs); err != nil { return err } - if err := setupPtmx(rootfs, console); err != nil { + if err := setupPtmx(rootfs, console, mountLabel); err != nil { return err } if err := system.Chdir(rootfs); err != nil { @@ -196,7 +197,7 @@ func setupDev(rootfs string) error { } // setupConsole ensures that the container has a proper /dev/console setup -func setupConsole(rootfs, console string) error { +func setupConsole(rootfs, console string, mountLabel string) error { oldMask := system.Umask(0000) defer system.Umask(oldMask) @@ -220,6 +221,9 @@ func setupConsole(rootfs, console string) error { if err := system.Mknod(dest, (st.Mode&^07777)|0600, int(st.Rdev)); err != nil { return fmt.Errorf("mknod %s %s", dest, err) } + if err := label.SetFileLabel(console, mountLabel); err != nil { + return fmt.Errorf("SetFileLabel Failed %s %s", dest, err) + } if err := system.Mount(console, dest, "bind", syscall.MS_BIND, ""); err != nil { return fmt.Errorf("bind %s to %s %s", console, dest, err) } @@ -228,7 +232,7 @@ func setupConsole(rootfs, console string) error { // mountSystem sets up linux specific system mounts like sys, proc, shm, and devpts // inside the mount namespace -func mountSystem(rootfs string) error { +func mountSystem(rootfs string, mountLabel string) error { for _, m := range []struct { source string path string @@ -238,8 +242,8 @@ func mountSystem(rootfs string) error { }{ {source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags}, {source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: defaultMountFlags}, - {source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaultMountFlags, data: "mode=1777,size=65536k"}, - {source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: "newinstance,ptmxmode=0666,mode=620,gid=5"}, + {source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaultMountFlags, data: label.FormatMountLabel("mode=1755,size=65536k", mountLabel)}, + {source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: label.FormatMountLabel("newinstance,ptmxmode=0666,mode=620,gid=5", mountLabel)}, } { if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) { return fmt.Errorf("mkdirall %s %s", m.path, err) @@ -253,7 +257,7 @@ func mountSystem(rootfs string) error { // setupPtmx adds a symlink to pts/ptmx for /dev/ptmx and // finishes setting up /dev/console -func setupPtmx(rootfs, console string) error { +func setupPtmx(rootfs, console string, mountLabel string) error { ptmx := filepath.Join(rootfs, "dev/ptmx") if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) { return err @@ -262,7 +266,7 @@ func setupPtmx(rootfs, console string) error { return fmt.Errorf("symlink dev ptmx %s", err) } if console != "" { - if err := setupConsole(rootfs, console); err != nil { + if err := setupConsole(rootfs, console, mountLabel); err != nil { return err } } diff --git a/pkg/selinux/selinux.go b/pkg/selinux/selinux.go new file mode 100644 index 0000000000..5236d3fb87 --- /dev/null +++ b/pkg/selinux/selinux.go @@ -0,0 +1,387 @@ +package selinux + +import ( + "bufio" + "crypto/rand" + "encoding/binary" + "fmt" + "github.com/dotcloud/docker/pkg/mount" + "github.com/dotcloud/docker/pkg/system" + "io" + "os" + "regexp" + "strconv" + "strings" + "syscall" +) + +const ( + Enforcing = 1 + Permissive = 0 + Disabled = -1 + selinuxDir = "/etc/selinux/" + selinuxConfig = selinuxDir + "config" + selinuxTypeTag = "SELINUXTYPE" + selinuxTag = "SELINUX" + selinuxPath = "/sys/fs/selinux" + xattrNameSelinux = "security.selinux" + stRdOnly = 0x01 +) + +var ( + assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`) + spaceRegex = regexp.MustCompile(`^([^=]+) (.*)$`) + mcsList = make(map[string]bool) + selinuxfs = "unknown" + selinuxEnabled = false + selinuxEnabledChecked = false +) + +type SELinuxContext map[string]string + +func GetSelinuxMountPoint() string { + if selinuxfs != "unknown" { + return selinuxfs + } + selinuxfs = "" + + mounts, err := mount.GetMounts() + if err != nil { + return selinuxfs + } + for _, mount := range mounts { + if mount.Fstype == "selinuxfs" { + selinuxfs = mount.Mountpoint + break + } + } + if selinuxfs != "" { + var buf syscall.Statfs_t + syscall.Statfs(selinuxfs, &buf) + if (buf.Flags & stRdOnly) == 1 { + selinuxfs = "" + } + } + return selinuxfs +} + +func SelinuxEnabled() bool { + if selinuxEnabledChecked { + return selinuxEnabled + } + selinuxEnabledChecked = true + if fs := GetSelinuxMountPoint(); fs != "" { + if con, _ := Getcon(); con != "kernel" { + selinuxEnabled = true + } + } + return selinuxEnabled +} + +func ReadConfig(target string) (value string) { + var ( + val, key string + bufin *bufio.Reader + ) + + in, err := os.Open(selinuxConfig) + if err != nil { + return "" + } + defer in.Close() + + bufin = bufio.NewReader(in) + + for done := false; !done; { + var line string + if line, err = bufin.ReadString('\n'); err != nil { + if err != io.EOF { + return "" + } + done = true + } + line = strings.TrimSpace(line) + if len(line) == 0 { + // Skip blank lines + continue + } + if line[0] == ';' || line[0] == '#' { + // Skip comments + continue + } + if groups := assignRegex.FindStringSubmatch(line); groups != nil { + key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) + if key == target { + return strings.Trim(val, "\"") + } + } + } + return "" +} + +func GetSELinuxPolicyRoot() string { + return selinuxDir + ReadConfig(selinuxTypeTag) +} + +func readCon(name string) (string, error) { + var val string + + in, err := os.Open(name) + if err != nil { + return "", err + } + defer in.Close() + + _, err = fmt.Fscanf(in, "%s", &val) + return val, err +} + +func Setfilecon(path string, scon string) error { + return system.Lsetxattr(path, xattrNameSelinux, []byte(scon), 0) +} + +func Getfilecon(path string) (string, error) { + var scon []byte + + cnt, err := syscall.Getxattr(path, xattrNameSelinux, scon) + scon = make([]byte, cnt) + cnt, err = syscall.Getxattr(path, xattrNameSelinux, scon) + return string(scon), err +} + +func Setfscreatecon(scon string) error { + return writeCon("/proc/self/attr/fscreate", scon) +} + +func Getfscreatecon() (string, error) { + return readCon("/proc/self/attr/fscreate") +} + +func Getcon() (string, error) { + return readCon("/proc/self/attr/current") +} + +func Getpidcon(pid int) (string, error) { + return readCon(fmt.Sprintf("/proc/%d/attr/current", pid)) +} + +func Getexeccon() (string, error) { + return readCon("/proc/self/attr/exec") +} + +func writeCon(name string, val string) error { + if !SelinuxEnabled() { + return nil + } + out, err := os.OpenFile(name, os.O_WRONLY, 0) + if err != nil { + return err + } + defer out.Close() + + if val != "" { + _, err = out.Write([]byte(val)) + } else { + _, err = out.Write(nil) + } + return err +} + +func Setexeccon(scon string) error { + return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), scon) +} + +func (c SELinuxContext) Get() string { + return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"]) +} + +func NewContext(scon string) SELinuxContext { + c := make(SELinuxContext) + + if len(scon) != 0 { + con := strings.SplitN(scon, ":", 4) + c["user"] = con[0] + c["role"] = con[1] + c["type"] = con[2] + c["level"] = con[3] + } + return c +} + +func SelinuxGetEnforce() int { + var enforce int + + enforceS, err := readCon(fmt.Sprintf("%s/enforce", selinuxPath)) + if err != nil { + return -1 + } + + enforce, err = strconv.Atoi(string(enforceS)) + if err != nil { + return -1 + } + return enforce +} + +func SelinuxGetEnforceMode() int { + switch ReadConfig(selinuxTag) { + case "enforcing": + return Enforcing + case "permissive": + return Permissive + } + return Disabled +} + +func mcsAdd(mcs string) { + mcsList[mcs] = true +} + +func mcsDelete(mcs string) { + mcsList[mcs] = false +} + +func mcsExists(mcs string) bool { + return mcsList[mcs] +} + +func IntToMcs(id int, catRange uint32) string { + var ( + SETSIZE = int(catRange) + TIER = SETSIZE + ORD = id + ) + + if id < 1 || id > 523776 { + return "" + } + + for ORD > TIER { + ORD = ORD - TIER + TIER -= 1 + } + TIER = SETSIZE - TIER + ORD = ORD + TIER + return fmt.Sprintf("s0:c%d,c%d", TIER, ORD) +} + +func uniqMcs(catRange uint32) string { + var ( + n uint32 + c1, c2 uint32 + mcs string + ) + + for { + binary.Read(rand.Reader, binary.LittleEndian, &n) + c1 = n % catRange + binary.Read(rand.Reader, binary.LittleEndian, &n) + c2 = n % catRange + if c1 == c2 { + continue + } else { + if c1 > c2 { + t := c1 + c1 = c2 + c2 = t + } + } + mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2) + if mcsExists(mcs) { + continue + } + mcsAdd(mcs) + break + } + return mcs +} + +func FreeContext(con string) { + if con != "" { + scon := NewContext(con) + mcsDelete(scon["level"]) + } +} + +func GetLxcContexts() (processLabel string, fileLabel string) { + var ( + val, key string + bufin *bufio.Reader + ) + + if !SelinuxEnabled() { + return "", "" + } + lxcPath := fmt.Sprintf("%s/content/lxc_contexts", GetSELinuxPolicyRoot()) + fileLabel = "system_u:object_r:svirt_sandbox_file_t:s0" + processLabel = "system_u:system_r:svirt_lxc_net_t:s0" + + in, err := os.Open(lxcPath) + if err != nil { + goto exit + } + defer in.Close() + + bufin = bufio.NewReader(in) + + for done := false; !done; { + var line string + if line, err = bufin.ReadString('\n'); err != nil { + if err == io.EOF { + done = true + } else { + goto exit + } + } + line = strings.TrimSpace(line) + if len(line) == 0 { + // Skip blank lines + continue + } + if line[0] == ';' || line[0] == '#' { + // Skip comments + continue + } + if groups := assignRegex.FindStringSubmatch(line); groups != nil { + key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) + if key == "process" { + processLabel = strings.Trim(val, "\"") + } + if key == "file" { + fileLabel = strings.Trim(val, "\"") + } + } + } +exit: + mcs := IntToMcs(os.Getpid(), 1024) + scon := NewContext(processLabel) + scon["level"] = mcs + processLabel = scon.Get() + scon = NewContext(fileLabel) + scon["level"] = mcs + fileLabel = scon.Get() + return processLabel, fileLabel +} + +func SecurityCheckContext(val string) error { + return writeCon(fmt.Sprintf("%s.context", selinuxPath), val) +} + +func CopyLevel(src, dest string) (string, error) { + if !SelinuxEnabled() { + return "", nil + } + if src == "" { + return "", nil + } + if err := SecurityCheckContext(src); err != nil { + return "", err + } + if err := SecurityCheckContext(dest); err != nil { + return "", err + } + scon := NewContext(src) + tcon := NewContext(dest) + tcon["level"] = scon["level"] + return tcon.Get(), nil +} diff --git a/pkg/selinux/selinux_test.go b/pkg/selinux/selinux_test.go new file mode 100644 index 0000000000..6b59c1db11 --- /dev/null +++ b/pkg/selinux/selinux_test.go @@ -0,0 +1,64 @@ +package selinux_test + +import ( + "github.com/dotcloud/docker/pkg/selinux" + "os" + "testing" +) + +func testSetfilecon(t *testing.T) { + if selinux.SelinuxEnabled() { + tmp := "selinux_test" + out, _ := os.OpenFile(tmp, os.O_WRONLY, 0) + out.Close() + err := selinux.Setfilecon(tmp, "system_u:object_r:bin_t:s0") + if err == nil { + t.Log(selinux.Getfilecon(tmp)) + } else { + t.Log("Setfilecon failed") + t.Fatal(err) + } + os.Remove(tmp) + } +} + +func TestSELinux(t *testing.T) { + var ( + err error + plabel, flabel string + ) + + if selinux.SelinuxEnabled() { + t.Log("Enabled") + plabel, flabel = selinux.GetLxcContexts() + t.Log(plabel) + t.Log(flabel) + plabel, flabel = selinux.GetLxcContexts() + t.Log(plabel) + t.Log(flabel) + t.Log("getenforce ", selinux.SelinuxGetEnforce()) + t.Log("getenforcemode ", selinux.SelinuxGetEnforceMode()) + pid := os.Getpid() + t.Log("PID:%d MCS:%s\n", pid, selinux.IntToMcs(pid, 1023)) + t.Log(selinux.Getcon()) + t.Log(selinux.Getfilecon("/etc/passwd")) + err = selinux.Setfscreatecon("unconfined_u:unconfined_r:unconfined_t:s0") + if err == nil { + t.Log(selinux.Getfscreatecon()) + } else { + t.Log("setfscreatecon failed", err) + t.Fatal(err) + } + err = selinux.Setfscreatecon("") + if err == nil { + t.Log(selinux.Getfscreatecon()) + } else { + t.Log("setfscreatecon failed", err) + t.Fatal(err) + } + t.Log(selinux.Getpidcon(1)) + t.Log(selinux.GetSelinuxMountPoint()) + } else { + t.Log("Disabled") + } +} diff --git a/runconfig/config.go b/runconfig/config.go index e961d659d7..c3ade575c5 100644 --- a/runconfig/config.go +++ b/runconfig/config.go @@ -1,8 +1,10 @@ package runconfig import ( + "encoding/json" "github.com/dotcloud/docker/engine" "github.com/dotcloud/docker/nat" + "github.com/dotcloud/docker/runtime/execdriver" ) // Note: the Config structure should hold only portable information about the container. @@ -34,9 +36,17 @@ type Config struct { Entrypoint []string NetworkDisabled bool OnBuild []string + Context execdriver.Context } func ContainerConfigFromJob(job *engine.Job) *Config { + var context execdriver.Context + val := job.Getenv("Context") + if val != "" { + if err := json.Unmarshal([]byte(val), &context); err != nil { + panic(err) + } + } config := &Config{ Hostname: job.Getenv("Hostname"), Domainname: job.Getenv("Domainname"), @@ -54,6 +64,7 @@ func ContainerConfigFromJob(job *engine.Job) *Config { VolumesFrom: job.Getenv("VolumesFrom"), WorkingDir: job.Getenv("WorkingDir"), NetworkDisabled: job.GetenvBool("NetworkDisabled"), + Context: context, } job.GetenvJson("ExposedPorts", &config.ExposedPorts) job.GetenvJson("Volumes", &config.Volumes) diff --git a/runconfig/parse.go b/runconfig/parse.go index c2591722d5..23c66cd611 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -4,8 +4,10 @@ import ( "fmt" "github.com/dotcloud/docker/nat" "github.com/dotcloud/docker/opts" + "github.com/dotcloud/docker/pkg/label" flag "github.com/dotcloud/docker/pkg/mflag" "github.com/dotcloud/docker/pkg/sysinfo" + "github.com/dotcloud/docker/runtime/execdriver" "github.com/dotcloud/docker/utils" "io/ioutil" "path" @@ -32,6 +34,10 @@ func ParseSubcommand(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) } func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) { + var ( + processLabel string + mountLabel string + ) var ( // FIXME: use utils.ListOpts for attach and volumes? flAttach = opts.NewListOpts(opts.ValidateAttach) @@ -60,6 +66,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID") flWorkingDir = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container") flCpuShares = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)") + flLabelOptions = cmd.String([]string{"Z", "-label"}, "", "Options to pass to underlying labeling system") // For documentation purpose _ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)") @@ -150,6 +157,15 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf entrypoint = []string{*flEntrypoint} } + if !*flPrivileged { + pLabel, mLabel, e := label.GenLabels(*flLabelOptions) + if e != nil { + return nil, nil, cmd, fmt.Errorf("Invalid security labels : %s", e) + } + processLabel = pLabel + mountLabel = mLabel + } + lxcConf, err := parseLxcConfOpts(flLxcOpts) if err != nil { return nil, nil, cmd, err @@ -204,6 +220,10 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf VolumesFrom: strings.Join(flVolumesFrom.GetAll(), ","), Entrypoint: entrypoint, WorkingDir: *flWorkingDir, + Context: execdriver.Context{ + "mount_label": mountLabel, + "process_label": processLabel, + }, } hostConfig := &HostConfig{ diff --git a/runtime/container.go b/runtime/container.go index bff9aea968..53d0aa666e 100644 --- a/runtime/container.go +++ b/runtime/container.go @@ -402,6 +402,7 @@ func populateCommand(c *Container) { User: c.Config.User, Config: driverConfig, Resources: resources, + Context: c.Config.Context, } c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true} } diff --git a/runtime/execdriver/driver.go b/runtime/execdriver/driver.go index 23e31ee8d9..dca889a82d 100644 --- a/runtime/execdriver/driver.go +++ b/runtime/execdriver/driver.go @@ -7,6 +7,10 @@ import ( "os/exec" ) +// Context is a generic key value pair that allows +// arbatrary data to be sent +type Context map[string]string + var ( ErrNotRunning = errors.New("Process could not be started") ErrWaitTimeoutReached = errors.New("Wait timeout reached") @@ -121,6 +125,7 @@ type Command struct { Arguments []string `json:"arguments"` WorkingDir string `json:"working_dir"` ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver + Context Context `json:"context"` // generic context for specific options (apparmor, selinux) Tty bool `json:"tty"` Network *Network `json:"network"` Config []string `json:"config"` // generic values that specific drivers can consume diff --git a/runtime/execdriver/lxc/lxc_template.go b/runtime/execdriver/lxc/lxc_template.go index ce9d90469f..608fb22436 100644 --- a/runtime/execdriver/lxc/lxc_template.go +++ b/runtime/execdriver/lxc/lxc_template.go @@ -1,6 +1,7 @@ package lxc import ( + "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/runtime/execdriver" "strings" "text/template" @@ -29,6 +30,10 @@ lxc.pts = 1024 # disable the main console lxc.console = none +{{if getProcessLabel .Context}} +lxc.se_context = {{ getProcessLabel .Context}} +{{$MOUNTLABEL := getMountLabel .Context}} +{{end}} # no controlling tty at all lxc.tty = 1 @@ -85,8 +90,8 @@ lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noe lxc.mount.entry = {{.Console}} {{escapeFstabSpaces $ROOTFS}}/dev/console none bind,rw 0 0 {{end}} -lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0 -lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0 +lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts {{formatMountLabel "newinstance,ptmxmode=0666,nosuid,noexec" "$MOUNTLABEL"}} 0 0 +lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs {{formatMountLabel "size=65536k,nosuid,nodev,noexec" "$MOUNTLABEL"}} 0 0 {{range $value := .Mounts}} {{if $value.Writable}} @@ -142,11 +147,22 @@ func getMemorySwap(v *execdriver.Resources) int64 { return v.Memory * 2 } +func getProcessLabel(c execdriver.Context) string { + return c["process_label"] +} + +func getMountLabel(c execdriver.Context) string { + return c["mount_label"] +} + func init() { var err error funcMap := template.FuncMap{ "getMemorySwap": getMemorySwap, + "getProcessLabel": getProcessLabel, + "getMountLabel": getMountLabel, "escapeFstabSpaces": escapeFstabSpaces, + "formatMountLabel": label.FormatMountLabel, } LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate) if err != nil { diff --git a/runtime/execdriver/native/default_template.go b/runtime/execdriver/native/default_template.go index d744ab382f..7e1e9ed86e 100644 --- a/runtime/execdriver/native/default_template.go +++ b/runtime/execdriver/native/default_template.go @@ -18,6 +18,8 @@ func createContainer(c *execdriver.Command) *libcontainer.Container { container.User = c.User container.WorkingDir = c.WorkingDir container.Env = c.Env + container.Context["mount_label"] = c.Context["mount_label"] + container.Context["process_label"] = c.Context["process_label"] loopbackNetwork := libcontainer.Network{ Mtu: c.Network.Mtu, diff --git a/runtime/graphdriver/aufs/aufs.go b/runtime/graphdriver/aufs/aufs.go index 6f05ddd025..401bbd8c86 100644 --- a/runtime/graphdriver/aufs/aufs.go +++ b/runtime/graphdriver/aufs/aufs.go @@ -134,7 +134,7 @@ func (a Driver) Exists(id string) bool { // Three folders are created for each id // mnt, layers, and diff -func (a *Driver) Create(id, parent string) error { +func (a *Driver) Create(id, parent string, mountLabel string) error { if err := a.createDirsFor(id); err != nil { return err } diff --git a/runtime/graphdriver/aufs/aufs_test.go b/runtime/graphdriver/aufs/aufs_test.go index cb417c3b26..9cfdebd160 100644 --- a/runtime/graphdriver/aufs/aufs_test.go +++ b/runtime/graphdriver/aufs/aufs_test.go @@ -90,7 +90,7 @@ func TestCreateNewDir(t *testing.T) { d := newDriver(t) defer os.RemoveAll(tmp) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } } @@ -99,7 +99,7 @@ func TestCreateNewDirStructure(t *testing.T) { d := newDriver(t) defer os.RemoveAll(tmp) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -120,7 +120,7 @@ func TestRemoveImage(t *testing.T) { d := newDriver(t) defer os.RemoveAll(tmp) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -145,7 +145,7 @@ func TestGetWithoutParent(t *testing.T) { d := newDriver(t) defer os.RemoveAll(tmp) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -172,7 +172,7 @@ func TestCleanupWithDir(t *testing.T) { d := newDriver(t) defer os.RemoveAll(tmp) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -185,7 +185,7 @@ func TestMountedFalseResponse(t *testing.T) { d := newDriver(t) defer os.RemoveAll(tmp) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -204,10 +204,10 @@ func TestMountedTrueReponse(t *testing.T) { defer os.RemoveAll(tmp) defer d.Cleanup() - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } - if err := d.Create("2", "1"); err != nil { + if err := d.Create("2", "1", ""); err != nil { t.Fatal(err) } @@ -230,10 +230,10 @@ func TestMountWithParent(t *testing.T) { d := newDriver(t) defer os.RemoveAll(tmp) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } - if err := d.Create("2", "1"); err != nil { + if err := d.Create("2", "1", ""); err != nil { t.Fatal(err) } @@ -261,10 +261,10 @@ func TestRemoveMountedDir(t *testing.T) { d := newDriver(t) defer os.RemoveAll(tmp) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } - if err := d.Create("2", "1"); err != nil { + if err := d.Create("2", "1", ""); err != nil { t.Fatal(err) } @@ -300,7 +300,7 @@ func TestCreateWithInvalidParent(t *testing.T) { d := newDriver(t) defer os.RemoveAll(tmp) - if err := d.Create("1", "docker"); err == nil { + if err := d.Create("1", "docker", ""); err == nil { t.Fatalf("Error should not be nil with parent does not exist") } } @@ -309,7 +309,7 @@ func TestGetDiff(t *testing.T) { d := newDriver(t) defer os.RemoveAll(tmp) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -343,10 +343,10 @@ func TestChanges(t *testing.T) { d := newDriver(t) defer os.RemoveAll(tmp) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } - if err := d.Create("2", "1"); err != nil { + if err := d.Create("2", "1", ""); err != nil { t.Fatal(err) } @@ -392,7 +392,7 @@ func TestChanges(t *testing.T) { t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind) } - if err := d.Create("3", "2"); err != nil { + if err := d.Create("3", "2", ""); err != nil { t.Fatal(err) } mntPoint, err = d.Get("3") @@ -437,7 +437,7 @@ func TestDiffSize(t *testing.T) { d := newDriver(t) defer os.RemoveAll(tmp) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -479,7 +479,7 @@ func TestChildDiffSize(t *testing.T) { defer os.RemoveAll(tmp) defer d.Cleanup() - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -515,7 +515,7 @@ func TestChildDiffSize(t *testing.T) { t.Fatalf("Expected size to be %d got %d", size, diffSize) } - if err := d.Create("2", "1"); err != nil { + if err := d.Create("2", "1", ""); err != nil { t.Fatal(err) } @@ -534,7 +534,7 @@ func TestExists(t *testing.T) { defer os.RemoveAll(tmp) defer d.Cleanup() - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -552,7 +552,7 @@ func TestStatus(t *testing.T) { defer os.RemoveAll(tmp) defer d.Cleanup() - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -581,7 +581,7 @@ func TestApplyDiff(t *testing.T) { defer os.RemoveAll(tmp) defer d.Cleanup() - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -607,10 +607,10 @@ func TestApplyDiff(t *testing.T) { t.Fatal(err) } - if err := d.Create("2", ""); err != nil { + if err := d.Create("2", "", ""); err != nil { t.Fatal(err) } - if err := d.Create("3", "2"); err != nil { + if err := d.Create("3", "2", ""); err != nil { t.Fatal(err) } @@ -656,7 +656,7 @@ func TestMountMoreThan42Layers(t *testing.T) { } current = hash(current) - if err := d.Create(current, parent); err != nil { + if err := d.Create(current, parent, ""); err != nil { t.Logf("Current layer %d", i) t.Fatal(err) } diff --git a/runtime/graphdriver/aufs/migrate.go b/runtime/graphdriver/aufs/migrate.go index 6018342d6c..400e260797 100644 --- a/runtime/graphdriver/aufs/migrate.go +++ b/runtime/graphdriver/aufs/migrate.go @@ -77,7 +77,7 @@ func (a *Driver) migrateContainers(pth string, setupInit func(p string) error) e } initID := fmt.Sprintf("%s-init", id) - if err := a.Create(initID, metadata.Image); err != nil { + if err := a.Create(initID, metadata.Image, ""); err != nil { return err } @@ -90,7 +90,7 @@ func (a *Driver) migrateContainers(pth string, setupInit func(p string) error) e return err } - if err := a.Create(id, initID); err != nil { + if err := a.Create(id, initID, ""); err != nil { return err } } @@ -144,7 +144,7 @@ func (a *Driver) migrateImage(m *metadata, pth string, migrated map[string]bool) return err } if !a.Exists(m.ID) { - if err := a.Create(m.ID, m.ParentID); err != nil { + if err := a.Create(m.ID, m.ParentID, ""); err != nil { return err } } diff --git a/runtime/graphdriver/btrfs/btrfs.go b/runtime/graphdriver/btrfs/btrfs.go index b0530be92b..2a94a4089f 100644 --- a/runtime/graphdriver/btrfs/btrfs.go +++ b/runtime/graphdriver/btrfs/btrfs.go @@ -80,7 +80,7 @@ func getDirFd(dir *C.DIR) uintptr { return uintptr(C.dirfd(dir)) } -func subvolCreate(path, name string) error { +func subvolCreate(path, name string, mountLabel string) error { dir, err := openDir(path) if err != nil { return err @@ -155,13 +155,13 @@ func (d *Driver) subvolumesDirId(id string) string { return path.Join(d.subvolumesDir(), id) } -func (d *Driver) Create(id string, parent string) error { +func (d *Driver) Create(id string, parent string, mountLabel string) error { subvolumes := path.Join(d.home, "subvolumes") if err := os.MkdirAll(subvolumes, 0700); err != nil { return err } if parent == "" { - if err := subvolCreate(subvolumes, id); err != nil { + if err := subvolCreate(subvolumes, id, mountLabel); err != nil { return err } } else { diff --git a/runtime/graphdriver/devmapper/deviceset.go b/runtime/graphdriver/devmapper/deviceset.go index dfdb180bb2..762e982208 100644 --- a/runtime/graphdriver/devmapper/deviceset.go +++ b/runtime/graphdriver/devmapper/deviceset.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/utils" "io" "io/ioutil" @@ -827,7 +828,7 @@ func (devices *DeviceSet) Shutdown() error { return nil } -func (devices *DeviceSet) MountDevice(hash, path string) error { +func (devices *DeviceSet) MountDevice(hash, path string, mountLabel string) error { devices.Lock() defer devices.Unlock() @@ -859,9 +860,11 @@ func (devices *DeviceSet) MountDevice(hash, path string) error { var flags uintptr = sysMsMgcVal - err := sysMount(info.DevName(), path, "ext4", flags, "discard") + mountOptions := label.FormatMountLabel("discard", mountLabel) + err := sysMount(info.DevName(), path, "ext4", flags, mountOptions) if err != nil && err == sysEInval { - err = sysMount(info.DevName(), path, "ext4", flags, "") + mountOptions = label.FormatMountLabel(mountLabel, "") + err = sysMount(info.DevName(), path, "ext4", flags, mountOptions) } if err != nil { return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err) diff --git a/runtime/graphdriver/devmapper/driver.go b/runtime/graphdriver/devmapper/driver.go index 33c7a0f483..1324ddab81 100644 --- a/runtime/graphdriver/devmapper/driver.go +++ b/runtime/graphdriver/devmapper/driver.go @@ -22,7 +22,8 @@ func init() { type Driver struct { *DeviceSet - home string + home string + MountLabel string } var Init = func(home string) (graphdriver.Driver, error) { @@ -60,13 +61,13 @@ func (d *Driver) Cleanup() error { return d.DeviceSet.Shutdown() } -func (d *Driver) Create(id, parent string) error { +func (d *Driver) Create(id, parent string, mountLabel string) error { + d.MountLabel = mountLabel if err := d.DeviceSet.AddDevice(id, parent); err != nil { return err } - mp := path.Join(d.home, "mnt", id) - if err := d.mount(id, mp); err != nil { + if err := d.mount(id, mp, d.MountLabel); err != nil { return err } @@ -116,7 +117,7 @@ func (d *Driver) Remove(id string) error { func (d *Driver) Get(id string) (string, error) { mp := path.Join(d.home, "mnt", id) - if err := d.mount(id, mp); err != nil { + if err := d.mount(id, mp, d.MountLabel); err != nil { return "", err } @@ -129,13 +130,13 @@ func (d *Driver) Put(id string) { } } -func (d *Driver) mount(id, mountPoint string) error { +func (d *Driver) mount(id, mountPoint string, mountLabel string) error { // Create the target directories if they don't exist if err := osMkdirAll(mountPoint, 0755); err != nil && !osIsExist(err) { return err } // Mount the device - return d.DeviceSet.MountDevice(id, mountPoint) + return d.DeviceSet.MountDevice(id, mountPoint, mountLabel) } func (d *Driver) Exists(id string) bool { diff --git a/runtime/graphdriver/devmapper/driver_test.go b/runtime/graphdriver/devmapper/driver_test.go index 9af71a00b3..4ca72db0ca 100644 --- a/runtime/graphdriver/devmapper/driver_test.go +++ b/runtime/graphdriver/devmapper/driver_test.go @@ -494,7 +494,7 @@ func TestDriverCreate(t *testing.T) { "?ioctl.loopctlgetfree", ) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } calls.Assert(t, @@ -612,7 +612,7 @@ func TestDriverRemove(t *testing.T) { "?ioctl.loopctlgetfree", ) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -668,7 +668,7 @@ func TestCleanup(t *testing.T) { mountPoints := make([]string, 2) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } // Mount the id @@ -678,7 +678,7 @@ func TestCleanup(t *testing.T) { } mountPoints[0] = p - if err := d.Create("2", "1"); err != nil { + if err := d.Create("2", "1", ""); err != nil { t.Fatal(err) } @@ -731,7 +731,7 @@ func TestNotMounted(t *testing.T) { d := newDriver(t) defer cleanup(d) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -749,7 +749,7 @@ func TestMounted(t *testing.T) { d := newDriver(t) defer cleanup(d) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } if _, err := d.Get("1"); err != nil { @@ -769,7 +769,7 @@ func TestInitCleanedDriver(t *testing.T) { t.Skip("FIXME: not a unit test") d := newDriver(t) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } if _, err := d.Get("1"); err != nil { @@ -797,7 +797,7 @@ func TestMountMountedDriver(t *testing.T) { d := newDriver(t) defer cleanup(d) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -816,7 +816,7 @@ func TestGetReturnsValidDevice(t *testing.T) { d := newDriver(t) defer cleanup(d) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } @@ -844,7 +844,7 @@ func TestDriverGetSize(t *testing.T) { d := newDriver(t) defer cleanup(d) - if err := d.Create("1", ""); err != nil { + if err := d.Create("1", "", ""); err != nil { t.Fatal(err) } diff --git a/runtime/graphdriver/driver.go b/runtime/graphdriver/driver.go index 89fd03a624..7bea704682 100644 --- a/runtime/graphdriver/driver.go +++ b/runtime/graphdriver/driver.go @@ -13,7 +13,7 @@ type InitFunc func(root string) (Driver, error) type Driver interface { String() string - Create(id, parent string) error + Create(id, parent string, mountLabel string) error Remove(id string) error Get(id string) (dir string, err error) diff --git a/runtime/graphdriver/vfs/driver.go b/runtime/graphdriver/vfs/driver.go index 10a7b223a4..fe09560f24 100644 --- a/runtime/graphdriver/vfs/driver.go +++ b/runtime/graphdriver/vfs/driver.go @@ -42,7 +42,7 @@ func copyDir(src, dst string) error { return nil } -func (d *Driver) Create(id string, parent string) error { +func (d *Driver) Create(id string, parent string, mountLabel string) error { dir := d.dir(id) if err := os.MkdirAll(path.Dir(dir), 0700); err != nil { return err diff --git a/runtime/runtime.go b/runtime/runtime.go index 0d3468e350..35bcad9781 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -467,7 +467,7 @@ func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Containe } initID := fmt.Sprintf("%s-init", container.ID) - if err := runtime.driver.Create(initID, img.ID); err != nil { + if err := runtime.driver.Create(initID, img.ID, config.Context["mount_label"]); err != nil { return nil, nil, err } initPath, err := runtime.driver.Get(initID) @@ -480,7 +480,7 @@ func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Containe return nil, nil, err } - if err := runtime.driver.Create(container.ID, initID); err != nil { + if err := runtime.driver.Create(container.ID, initID, config.Context["mount_label"]); err != nil { return nil, nil, err } resolvConf, err := utils.GetResolvConf()