Browse Source

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 <dwalsh@redhat.com> (github: rhatdan)
Dan Walsh 11 years ago
parent
commit
4c43566925

+ 1 - 1
Dockerfile

@@ -87,7 +87,7 @@ RUN	git config --global user.email 'docker-dummy@example.com'
 
 
 VOLUME	/var/lib/docker
 VOLUME	/var/lib/docker
 WORKDIR	/go/src/github.com/dotcloud/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
 # Wrap all commands in the "docker-in-docker" script to allow nested containers
 ENTRYPOINT	["hack/dind"]
 ENTRYPOINT	["hack/dind"]

+ 1 - 1
graph/graph.go

@@ -189,7 +189,7 @@ func (graph *Graph) Register(jsonData []byte, layerData archive.ArchiveReader, i
 	}
 	}
 
 
 	// Create root filesystem in the driver
 	// 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)
 		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
 	// Mount the root filesystem so we can apply the diff/layer

+ 7 - 0
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.
 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
 ### Static Daemon
 
 
 If it is feasible within the constraints of your distribution, you should
 If it is feasible within the constraints of your distribution, you should

+ 23 - 0
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
+}

+ 69 - 0
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)
+}

+ 10 - 1
pkg/libcontainer/nsinit/execin.go

@@ -4,6 +4,7 @@ package nsinit
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"github.com/dotcloud/docker/pkg/label"
 	"github.com/dotcloud/docker/pkg/libcontainer"
 	"github.com/dotcloud/docker/pkg/libcontainer"
 	"github.com/dotcloud/docker/pkg/system"
 	"github.com/dotcloud/docker/pkg/system"
 	"os"
 	"os"
@@ -32,7 +33,11 @@ func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []s
 		closeFds()
 		closeFds()
 		return -1, err
 		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
 	// foreach namespace fd, use setns to join an existing container's namespaces
 	for _, fd := range fds {
 	for _, fd := range fds {
 		if fd > 0 {
 		if fd > 0 {
@@ -80,6 +85,10 @@ dropAndExec:
 	if err := finalizeNamespace(container); err != nil {
 	if err := finalizeNamespace(container); err != nil {
 		return -1, err
 		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 {
 	if err := system.Execv(args[0], args[0:], container.Env); err != nil {
 		return -1, err
 		return -1, err
 	}
 	}

+ 7 - 1
pkg/libcontainer/nsinit/init.go

@@ -4,6 +4,7 @@ package nsinit
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"github.com/dotcloud/docker/pkg/label"
 	"github.com/dotcloud/docker/pkg/libcontainer"
 	"github.com/dotcloud/docker/pkg/libcontainer"
 	"github.com/dotcloud/docker/pkg/libcontainer/apparmor"
 	"github.com/dotcloud/docker/pkg/libcontainer/apparmor"
 	"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
 	"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
@@ -12,6 +13,7 @@ import (
 	"github.com/dotcloud/docker/pkg/system"
 	"github.com/dotcloud/docker/pkg/system"
 	"github.com/dotcloud/docker/pkg/user"
 	"github.com/dotcloud/docker/pkg/user"
 	"os"
 	"os"
+	"runtime"
 	"syscall"
 	"syscall"
 )
 )
 
 
@@ -57,7 +59,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
 		return fmt.Errorf("parent death signal %s", err)
 		return fmt.Errorf("parent death signal %s", err)
 	}
 	}
 	ns.logger.Println("setup mount namespace")
 	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)
 		return fmt.Errorf("setup mount namespace %s", err)
 	}
 	}
 	if err := setupNetwork(container, context); err != nil {
 	if err := setupNetwork(container, context); err != nil {
@@ -76,6 +78,10 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
 			return err
 			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])
 	ns.logger.Printf("execing %s\n", args[0])
 	return system.Execv(args[0], args[0:], container.Env)
 	return system.Execv(args[0], args[0:], container.Env)
 }
 }

+ 13 - 9
pkg/libcontainer/nsinit/mount.go

@@ -4,6 +4,7 @@ package nsinit
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"github.com/dotcloud/docker/pkg/label"
 	"github.com/dotcloud/docker/pkg/libcontainer"
 	"github.com/dotcloud/docker/pkg/libcontainer"
 	"github.com/dotcloud/docker/pkg/system"
 	"github.com/dotcloud/docker/pkg/system"
 	"io/ioutil"
 	"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
 // 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
 // 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
 	flag := syscall.MS_PRIVATE
 	if noPivotRoot {
 	if noPivotRoot {
 		flag = syscall.MS_SLAVE
 		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)
 			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)
 		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 {
 	if err := setupDev(rootfs); err != nil {
 		return err
 		return err
 	}
 	}
-	if err := setupPtmx(rootfs, console); err != nil {
+	if err := setupPtmx(rootfs, console, mountLabel); err != nil {
 		return err
 		return err
 	}
 	}
 	if err := system.Chdir(rootfs); err != nil {
 	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
 // 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)
 	oldMask := system.Umask(0000)
 	defer system.Umask(oldMask)
 	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 {
 	if err := system.Mknod(dest, (st.Mode&^07777)|0600, int(st.Rdev)); err != nil {
 		return fmt.Errorf("mknod %s %s", dest, err)
 		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 {
 	if err := system.Mount(console, dest, "bind", syscall.MS_BIND, ""); err != nil {
 		return fmt.Errorf("bind %s to %s %s", console, dest, err)
 		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
 // mountSystem sets up linux specific system mounts like sys, proc, shm, and devpts
 // inside the mount namespace
 // inside the mount namespace
-func mountSystem(rootfs string) error {
+func mountSystem(rootfs string, mountLabel string) error {
 	for _, m := range []struct {
 	for _, m := range []struct {
 		source string
 		source string
 		path   string
 		path   string
@@ -238,8 +242,8 @@ func mountSystem(rootfs string) error {
 	}{
 	}{
 		{source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags},
 		{source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags},
 		{source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", 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) {
 		if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) {
 			return fmt.Errorf("mkdirall %s %s", m.path, 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
 // setupPtmx adds a symlink to pts/ptmx for /dev/ptmx and
 // finishes setting up /dev/console
 // 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")
 	ptmx := filepath.Join(rootfs, "dev/ptmx")
 	if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
 	if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
 		return err
 		return err
@@ -262,7 +266,7 @@ func setupPtmx(rootfs, console string) error {
 		return fmt.Errorf("symlink dev ptmx %s", err)
 		return fmt.Errorf("symlink dev ptmx %s", err)
 	}
 	}
 	if console != "" {
 	if console != "" {
-		if err := setupConsole(rootfs, console); err != nil {
+		if err := setupConsole(rootfs, console, mountLabel); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}

+ 387 - 0
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
+}

+ 64 - 0
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")
+	}
+}

+ 11 - 0
runconfig/config.go

@@ -1,8 +1,10 @@
 package runconfig
 package runconfig
 
 
 import (
 import (
+	"encoding/json"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/nat"
+	"github.com/dotcloud/docker/runtime/execdriver"
 )
 )
 
 
 // Note: the Config structure should hold only portable information about the container.
 // Note: the Config structure should hold only portable information about the container.
@@ -34,9 +36,17 @@ type Config struct {
 	Entrypoint      []string
 	Entrypoint      []string
 	NetworkDisabled bool
 	NetworkDisabled bool
 	OnBuild         []string
 	OnBuild         []string
+	Context         execdriver.Context
 }
 }
 
 
 func ContainerConfigFromJob(job *engine.Job) *Config {
 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{
 	config := &Config{
 		Hostname:        job.Getenv("Hostname"),
 		Hostname:        job.Getenv("Hostname"),
 		Domainname:      job.Getenv("Domainname"),
 		Domainname:      job.Getenv("Domainname"),
@@ -54,6 +64,7 @@ func ContainerConfigFromJob(job *engine.Job) *Config {
 		VolumesFrom:     job.Getenv("VolumesFrom"),
 		VolumesFrom:     job.Getenv("VolumesFrom"),
 		WorkingDir:      job.Getenv("WorkingDir"),
 		WorkingDir:      job.Getenv("WorkingDir"),
 		NetworkDisabled: job.GetenvBool("NetworkDisabled"),
 		NetworkDisabled: job.GetenvBool("NetworkDisabled"),
+		Context:         context,
 	}
 	}
 	job.GetenvJson("ExposedPorts", &config.ExposedPorts)
 	job.GetenvJson("ExposedPorts", &config.ExposedPorts)
 	job.GetenvJson("Volumes", &config.Volumes)
 	job.GetenvJson("Volumes", &config.Volumes)

+ 20 - 0
runconfig/parse.go

@@ -4,8 +4,10 @@ import (
 	"fmt"
 	"fmt"
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/opts"
 	"github.com/dotcloud/docker/opts"
+	"github.com/dotcloud/docker/pkg/label"
 	flag "github.com/dotcloud/docker/pkg/mflag"
 	flag "github.com/dotcloud/docker/pkg/mflag"
 	"github.com/dotcloud/docker/pkg/sysinfo"
 	"github.com/dotcloud/docker/pkg/sysinfo"
+	"github.com/dotcloud/docker/runtime/execdriver"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io/ioutil"
 	"io/ioutil"
 	"path"
 	"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) {
 func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
+	var (
+		processLabel string
+		mountLabel   string
+	)
 	var (
 	var (
 		// FIXME: use utils.ListOpts for attach and volumes?
 		// FIXME: use utils.ListOpts for attach and volumes?
 		flAttach  = opts.NewListOpts(opts.ValidateAttach)
 		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")
 		flUser            = cmd.String([]string{"u", "-user"}, "", "Username or UID")
 		flWorkingDir      = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
 		flWorkingDir      = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
 		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
 		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
 		// For documentation purpose
 		_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
 		_ = 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}
 		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)
 	lxcConf, err := parseLxcConfOpts(flLxcOpts)
 	if err != nil {
 	if err != nil {
 		return nil, nil, cmd, err
 		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(), ","),
 		VolumesFrom:     strings.Join(flVolumesFrom.GetAll(), ","),
 		Entrypoint:      entrypoint,
 		Entrypoint:      entrypoint,
 		WorkingDir:      *flWorkingDir,
 		WorkingDir:      *flWorkingDir,
+		Context: execdriver.Context{
+			"mount_label":   mountLabel,
+			"process_label": processLabel,
+		},
 	}
 	}
 
 
 	hostConfig := &HostConfig{
 	hostConfig := &HostConfig{

+ 1 - 0
runtime/container.go

@@ -402,6 +402,7 @@ func populateCommand(c *Container) {
 		User:       c.Config.User,
 		User:       c.Config.User,
 		Config:     driverConfig,
 		Config:     driverConfig,
 		Resources:  resources,
 		Resources:  resources,
+		Context:    c.Config.Context,
 	}
 	}
 	c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
 	c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
 }
 }

+ 5 - 0
runtime/execdriver/driver.go

@@ -7,6 +7,10 @@ import (
 	"os/exec"
 	"os/exec"
 )
 )
 
 
+// Context is a generic key value pair that allows
+// arbatrary data to be sent
+type Context map[string]string
+
 var (
 var (
 	ErrNotRunning              = errors.New("Process could not be started")
 	ErrNotRunning              = errors.New("Process could not be started")
 	ErrWaitTimeoutReached      = errors.New("Wait timeout reached")
 	ErrWaitTimeoutReached      = errors.New("Wait timeout reached")
@@ -121,6 +125,7 @@ type Command struct {
 	Arguments  []string   `json:"arguments"`
 	Arguments  []string   `json:"arguments"`
 	WorkingDir string     `json:"working_dir"`
 	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
 	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"`
 	Tty        bool       `json:"tty"`
 	Network    *Network   `json:"network"`
 	Network    *Network   `json:"network"`
 	Config     []string   `json:"config"` //  generic values that specific drivers can consume
 	Config     []string   `json:"config"` //  generic values that specific drivers can consume

+ 18 - 2
runtime/execdriver/lxc/lxc_template.go

@@ -1,6 +1,7 @@
 package lxc
 package lxc
 
 
 import (
 import (
+	"github.com/dotcloud/docker/pkg/label"
 	"github.com/dotcloud/docker/runtime/execdriver"
 	"github.com/dotcloud/docker/runtime/execdriver"
 	"strings"
 	"strings"
 	"text/template"
 	"text/template"
@@ -29,6 +30,10 @@ lxc.pts = 1024
 
 
 # disable the main console
 # disable the main console
 lxc.console = none
 lxc.console = none
+{{if getProcessLabel .Context}}
+lxc.se_context = {{ getProcessLabel .Context}}
+{{$MOUNTLABEL := getMountLabel .Context}}
+{{end}}
 
 
 # no controlling tty at all
 # no controlling tty at all
 lxc.tty = 1
 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
 lxc.mount.entry = {{.Console}} {{escapeFstabSpaces $ROOTFS}}/dev/console none bind,rw 0 0
 {{end}}
 {{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}}
 {{range $value := .Mounts}}
 {{if $value.Writable}}
 {{if $value.Writable}}
@@ -142,11 +147,22 @@ func getMemorySwap(v *execdriver.Resources) int64 {
 	return v.Memory * 2
 	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() {
 func init() {
 	var err error
 	var err error
 	funcMap := template.FuncMap{
 	funcMap := template.FuncMap{
 		"getMemorySwap":     getMemorySwap,
 		"getMemorySwap":     getMemorySwap,
+		"getProcessLabel":   getProcessLabel,
+		"getMountLabel":     getMountLabel,
 		"escapeFstabSpaces": escapeFstabSpaces,
 		"escapeFstabSpaces": escapeFstabSpaces,
+		"formatMountLabel":  label.FormatMountLabel,
 	}
 	}
 	LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
 	LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
 	if err != nil {
 	if err != nil {

+ 2 - 0
runtime/execdriver/native/default_template.go

@@ -18,6 +18,8 @@ func createContainer(c *execdriver.Command) *libcontainer.Container {
 	container.User = c.User
 	container.User = c.User
 	container.WorkingDir = c.WorkingDir
 	container.WorkingDir = c.WorkingDir
 	container.Env = c.Env
 	container.Env = c.Env
+	container.Context["mount_label"] = c.Context["mount_label"]
+	container.Context["process_label"] = c.Context["process_label"]
 
 
 	loopbackNetwork := libcontainer.Network{
 	loopbackNetwork := libcontainer.Network{
 		Mtu:     c.Network.Mtu,
 		Mtu:     c.Network.Mtu,

+ 1 - 1
runtime/graphdriver/aufs/aufs.go

@@ -134,7 +134,7 @@ func (a Driver) Exists(id string) bool {
 
 
 // Three folders are created for each id
 // Three folders are created for each id
 // mnt, layers, and diff
 // 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 {
 	if err := a.createDirsFor(id); err != nil {
 		return err
 		return err
 	}
 	}

+ 26 - 26
runtime/graphdriver/aufs/aufs_test.go

@@ -90,7 +90,7 @@ func TestCreateNewDir(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 }
 }
@@ -99,7 +99,7 @@ func TestCreateNewDirStructure(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -120,7 +120,7 @@ func TestRemoveImage(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -145,7 +145,7 @@ func TestGetWithoutParent(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -172,7 +172,7 @@ func TestCleanupWithDir(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -185,7 +185,7 @@ func TestMountedFalseResponse(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -204,10 +204,10 @@ func TestMountedTrueReponse(t *testing.T) {
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 	defer d.Cleanup()
 	defer d.Cleanup()
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if err := d.Create("2", "1"); err != nil {
+	if err := d.Create("2", "1", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -230,10 +230,10 @@ func TestMountWithParent(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if err := d.Create("2", "1"); err != nil {
+	if err := d.Create("2", "1", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -261,10 +261,10 @@ func TestRemoveMountedDir(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if err := d.Create("2", "1"); err != nil {
+	if err := d.Create("2", "1", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -300,7 +300,7 @@ func TestCreateWithInvalidParent(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 	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")
 		t.Fatalf("Error should not be nil with parent does not exist")
 	}
 	}
 }
 }
@@ -309,7 +309,7 @@ func TestGetDiff(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -343,10 +343,10 @@ func TestChanges(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if err := d.Create("2", "1"); err != nil {
+	if err := d.Create("2", "1", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -392,7 +392,7 @@ func TestChanges(t *testing.T) {
 		t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
 		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)
 		t.Fatal(err)
 	}
 	}
 	mntPoint, err = d.Get("3")
 	mntPoint, err = d.Get("3")
@@ -437,7 +437,7 @@ func TestDiffSize(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -479,7 +479,7 @@ func TestChildDiffSize(t *testing.T) {
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 	defer d.Cleanup()
 	defer d.Cleanup()
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -515,7 +515,7 @@ func TestChildDiffSize(t *testing.T) {
 		t.Fatalf("Expected size to be %d got %d", size, diffSize)
 		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)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -534,7 +534,7 @@ func TestExists(t *testing.T) {
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 	defer d.Cleanup()
 	defer d.Cleanup()
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -552,7 +552,7 @@ func TestStatus(t *testing.T) {
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 	defer d.Cleanup()
 	defer d.Cleanup()
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -581,7 +581,7 @@ func TestApplyDiff(t *testing.T) {
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 	defer d.Cleanup()
 	defer d.Cleanup()
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -607,10 +607,10 @@ func TestApplyDiff(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if err := d.Create("2", ""); err != nil {
+	if err := d.Create("2", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if err := d.Create("3", "2"); err != nil {
+	if err := d.Create("3", "2", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -656,7 +656,7 @@ func TestMountMoreThan42Layers(t *testing.T) {
 		}
 		}
 		current = hash(current)
 		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.Logf("Current layer %d", i)
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}

+ 3 - 3
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)
 				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
 					return err
 				}
 				}
 
 
@@ -90,7 +90,7 @@ func (a *Driver) migrateContainers(pth string, setupInit func(p string) error) e
 					return err
 					return err
 				}
 				}
 
 
-				if err := a.Create(id, initID); err != nil {
+				if err := a.Create(id, initID, ""); err != nil {
 					return err
 					return err
 				}
 				}
 			}
 			}
@@ -144,7 +144,7 @@ func (a *Driver) migrateImage(m *metadata, pth string, migrated map[string]bool)
 			return err
 			return err
 		}
 		}
 		if !a.Exists(m.ID) {
 		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
 				return err
 			}
 			}
 		}
 		}

+ 3 - 3
runtime/graphdriver/btrfs/btrfs.go

@@ -80,7 +80,7 @@ func getDirFd(dir *C.DIR) uintptr {
 	return uintptr(C.dirfd(dir))
 	return uintptr(C.dirfd(dir))
 }
 }
 
 
-func subvolCreate(path, name string) error {
+func subvolCreate(path, name string, mountLabel string) error {
 	dir, err := openDir(path)
 	dir, err := openDir(path)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -155,13 +155,13 @@ func (d *Driver) subvolumesDirId(id string) string {
 	return path.Join(d.subvolumesDir(), id)
 	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")
 	subvolumes := path.Join(d.home, "subvolumes")
 	if err := os.MkdirAll(subvolumes, 0700); err != nil {
 	if err := os.MkdirAll(subvolumes, 0700); err != nil {
 		return err
 		return err
 	}
 	}
 	if parent == "" {
 	if parent == "" {
-		if err := subvolCreate(subvolumes, id); err != nil {
+		if err := subvolCreate(subvolumes, id, mountLabel); err != nil {
 			return err
 			return err
 		}
 		}
 	} else {
 	} else {

+ 6 - 3
runtime/graphdriver/devmapper/deviceset.go

@@ -6,6 +6,7 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"github.com/dotcloud/docker/pkg/label"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
@@ -827,7 +828,7 @@ func (devices *DeviceSet) Shutdown() error {
 	return nil
 	return nil
 }
 }
 
 
-func (devices *DeviceSet) MountDevice(hash, path string) error {
+func (devices *DeviceSet) MountDevice(hash, path string, mountLabel string) error {
 	devices.Lock()
 	devices.Lock()
 	defer devices.Unlock()
 	defer devices.Unlock()
 
 
@@ -859,9 +860,11 @@ func (devices *DeviceSet) MountDevice(hash, path string) error {
 
 
 	var flags uintptr = sysMsMgcVal
 	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 {
 	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 {
 	if err != nil {
 		return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err)
 		return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err)

+ 8 - 7
runtime/graphdriver/devmapper/driver.go

@@ -22,7 +22,8 @@ func init() {
 
 
 type Driver struct {
 type Driver struct {
 	*DeviceSet
 	*DeviceSet
-	home string
+	home       string
+	MountLabel string
 }
 }
 
 
 var Init = func(home string) (graphdriver.Driver, error) {
 var Init = func(home string) (graphdriver.Driver, error) {
@@ -60,13 +61,13 @@ func (d *Driver) Cleanup() error {
 	return d.DeviceSet.Shutdown()
 	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 {
 	if err := d.DeviceSet.AddDevice(id, parent); err != nil {
 		return err
 		return err
 	}
 	}
-
 	mp := path.Join(d.home, "mnt", id)
 	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
 		return err
 	}
 	}
 
 
@@ -116,7 +117,7 @@ func (d *Driver) Remove(id string) error {
 
 
 func (d *Driver) Get(id string) (string, error) {
 func (d *Driver) Get(id string) (string, error) {
 	mp := path.Join(d.home, "mnt", id)
 	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
 		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
 	// Create the target directories if they don't exist
 	if err := osMkdirAll(mountPoint, 0755); err != nil && !osIsExist(err) {
 	if err := osMkdirAll(mountPoint, 0755); err != nil && !osIsExist(err) {
 		return err
 		return err
 	}
 	}
 	// Mount the device
 	// Mount the device
-	return d.DeviceSet.MountDevice(id, mountPoint)
+	return d.DeviceSet.MountDevice(id, mountPoint, mountLabel)
 }
 }
 
 
 func (d *Driver) Exists(id string) bool {
 func (d *Driver) Exists(id string) bool {

+ 10 - 10
runtime/graphdriver/devmapper/driver_test.go

@@ -494,7 +494,7 @@ func TestDriverCreate(t *testing.T) {
 			"?ioctl.loopctlgetfree",
 			"?ioctl.loopctlgetfree",
 		)
 		)
 
 
-		if err := d.Create("1", ""); err != nil {
+		if err := d.Create("1", "", ""); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
 		calls.Assert(t,
 		calls.Assert(t,
@@ -612,7 +612,7 @@ func TestDriverRemove(t *testing.T) {
 			"?ioctl.loopctlgetfree",
 			"?ioctl.loopctlgetfree",
 		)
 		)
 
 
-		if err := d.Create("1", ""); err != nil {
+		if err := d.Create("1", "", ""); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
 
 
@@ -668,7 +668,7 @@ func TestCleanup(t *testing.T) {
 
 
 	mountPoints := make([]string, 2)
 	mountPoints := make([]string, 2)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	// Mount the id
 	// Mount the id
@@ -678,7 +678,7 @@ func TestCleanup(t *testing.T) {
 	}
 	}
 	mountPoints[0] = p
 	mountPoints[0] = p
 
 
-	if err := d.Create("2", "1"); err != nil {
+	if err := d.Create("2", "1", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -731,7 +731,7 @@ func TestNotMounted(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer cleanup(d)
 	defer cleanup(d)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -749,7 +749,7 @@ func TestMounted(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer cleanup(d)
 	defer cleanup(d)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if _, err := d.Get("1"); err != nil {
 	if _, err := d.Get("1"); err != nil {
@@ -769,7 +769,7 @@ func TestInitCleanedDriver(t *testing.T) {
 	t.Skip("FIXME: not a unit test")
 	t.Skip("FIXME: not a unit test")
 	d := newDriver(t)
 	d := newDriver(t)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if _, err := d.Get("1"); err != nil {
 	if _, err := d.Get("1"); err != nil {
@@ -797,7 +797,7 @@ func TestMountMountedDriver(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer cleanup(d)
 	defer cleanup(d)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -816,7 +816,7 @@ func TestGetReturnsValidDevice(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer cleanup(d)
 	defer cleanup(d)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -844,7 +844,7 @@ func TestDriverGetSize(t *testing.T) {
 	d := newDriver(t)
 	d := newDriver(t)
 	defer cleanup(d)
 	defer cleanup(d)
 
 
-	if err := d.Create("1", ""); err != nil {
+	if err := d.Create("1", "", ""); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 

+ 1 - 1
runtime/graphdriver/driver.go

@@ -13,7 +13,7 @@ type InitFunc func(root string) (Driver, error)
 type Driver interface {
 type Driver interface {
 	String() string
 	String() string
 
 
-	Create(id, parent string) error
+	Create(id, parent string, mountLabel string) error
 	Remove(id string) error
 	Remove(id string) error
 
 
 	Get(id string) (dir string, err error)
 	Get(id string) (dir string, err error)

+ 1 - 1
runtime/graphdriver/vfs/driver.go

@@ -42,7 +42,7 @@ func copyDir(src, dst string) error {
 	return nil
 	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)
 	dir := d.dir(id)
 	if err := os.MkdirAll(path.Dir(dir), 0700); err != nil {
 	if err := os.MkdirAll(path.Dir(dir), 0700); err != nil {
 		return err
 		return err

+ 2 - 2
runtime/runtime.go

@@ -467,7 +467,7 @@ func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Containe
 	}
 	}
 
 
 	initID := fmt.Sprintf("%s-init", container.ID)
 	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
 		return nil, nil, err
 	}
 	}
 	initPath, err := runtime.driver.Get(initID)
 	initPath, err := runtime.driver.Get(initID)
@@ -480,7 +480,7 @@ func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Containe
 		return nil, nil, err
 		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
 		return nil, nil, err
 	}
 	}
 	resolvConf, err := utils.GetResolvConf()
 	resolvConf, err := utils.GetResolvConf()