Prechádzať zdrojové kódy

Merge pull request #4211 from rhatdan/master

Add SELinux support
Tianon Gravi 11 rokov pred
rodič
commit
79e8ef28e4

+ 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()