diff --git a/label/label.go b/label/label.go index 434e1c57..5c8228cd 100644 --- a/label/label.go +++ b/label/label.go @@ -18,6 +18,10 @@ func SetFileLabel(path string, fileLabel string) error { return nil } +func Relabel(path string, fileLabel string, relabel string) error { + return nil +} + func GetPidCon(pid int) (string, error) { return "", nil } diff --git a/label/label_selinux.go b/label/label_selinux.go index 04521444..aa502a3d 100644 --- a/label/label_selinux.go +++ b/label/label_selinux.go @@ -66,6 +66,23 @@ func SetFileLabel(path string, fileLabel string) error { return nil } +// Change the label of path to the filelabel string. If the relabel string +// is "z", relabel will change the MCS label to s0. This will allow all +// containers to share the content. If the relabel string is a "Z" then +// the MCS label should continue to be used. SELinux will use this field +// to make sure the content can not be shared by other containes. +func Relabel(path string, fileLabel string, relabel string) error { + if fileLabel == "" { + return nil + } + if relabel == "z" { + c := selinux.NewContext(fileLabel) + c["level"] = "s0" + fileLabel = c.Get() + } + return selinux.Chcon(path, fileLabel, true) +} + func GetPidCon(pid int) (string, error) { if !selinux.SelinuxEnabled() { return "", nil diff --git a/mount/init.go b/mount/init.go index 34fad6dd..10f07389 100644 --- a/mount/init.go +++ b/mount/init.go @@ -44,7 +44,7 @@ func InitializeMountNamespace(rootfs, console string, mountConfig *MountConfig) if err := mountSystem(rootfs, mountConfig); err != nil { return fmt.Errorf("mount system %s", err) } - if err := setupBindmounts(rootfs, mountConfig.Mounts); err != nil { + if err := setupBindmounts(rootfs, mountConfig); err != nil { return fmt.Errorf("bind mounts %s", err) } if err := nodes.CreateDeviceNodes(rootfs, mountConfig.DeviceNodes); err != nil { @@ -144,7 +144,8 @@ func setupDevSymlinks(rootfs string) error { return nil } -func setupBindmounts(rootfs string, bindMounts Mounts) error { +func setupBindmounts(rootfs string, mountConfig *MountConfig) error { + bindMounts := mountConfig.Mounts for _, m := range bindMounts.OfType("bind") { var ( flags = syscall.MS_BIND | syscall.MS_REC @@ -176,6 +177,11 @@ func setupBindmounts(rootfs string, bindMounts Mounts) error { return fmt.Errorf("remounting %s into %s %s", m.Source, dest, err) } } + if m.Relabel != "" { + if err := label.Relabel(m.Source, mountConfig.MountLabel, m.Relabel); err != nil { + return fmt.Errorf("relabeling %s to %s %s", m.Source, mountConfig.MountLabel, err) + } + } if m.Private { if err := system.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil { return fmt.Errorf("mounting %s private %s", dest, err) diff --git a/mount/types.go b/mount/types.go index a2659e58..063bbac1 100644 --- a/mount/types.go +++ b/mount/types.go @@ -30,6 +30,7 @@ type Mount struct { Source string `json:"source,omitempty"` // Source path, in the host namespace Destination string `json:"destination,omitempty"` // Destination path, in the container Writable bool `json:"writable,omitempty"` + Relabel string `json:"relabel,omitempty"` // Relabel source if set, "z" indicates shared, "Z" indicates unshared Private bool `json:"private,omitempty"` } diff --git a/selinux/selinux.go b/selinux/selinux.go index 6cf7bd71..709eb9d8 100644 --- a/selinux/selinux.go +++ b/selinux/selinux.go @@ -9,6 +9,7 @@ import ( "github.com/dotcloud/docker/pkg/system" "io" "os" + "path/filepath" "regexp" "strconv" "strings" @@ -76,7 +77,7 @@ func SelinuxEnabled() bool { } selinuxEnabledChecked = true if fs := getSelinuxMountPoint(); fs != "" { - if con, _ := getcon(); con != "kernel" { + if con, _ := Getcon(); con != "kernel" { selinuxEnabled = true } } @@ -145,6 +146,12 @@ func Setfilecon(path string, scon string) error { return system.Lsetxattr(path, xattrNameSelinux, []byte(scon), 0) } +// Return the SELinux label for this path +func Getfilecon(path string) (string, error) { + con, err := system.Lgetxattr(path, xattrNameSelinux) + return string(con), err +} + func Setfscreatecon(scon string) error { return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", system.Gettid()), scon) } @@ -153,7 +160,8 @@ func Getfscreatecon() (string, error) { return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", system.Gettid())) } -func getcon() (string, error) { +// Return the SELinux label of the current process thread. +func Getcon() (string, error) { return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", system.Gettid())) } @@ -396,3 +404,36 @@ func CopyLevel(src, dest string) (string, error) { tcon["level"] = scon["level"] return tcon.Get(), nil } + +// Prevent users from relabing system files +func badPrefix(fpath string) error { + var badprefixes = []string{"/usr"} + + for _, prefix := range badprefixes { + if fpath == prefix || strings.HasPrefix(fpath, fmt.Sprintf("%s/", prefix)) { + return fmt.Errorf("Relabeling content in %s is not allowed.", prefix) + } + } + return nil +} + +// Change the fpath file object to the SELinux label scon. +// If the fpath is a directory and recurse is true Chcon will walk the +// directory tree setting the label +func Chcon(fpath string, scon string, recurse bool) error { + if !SelinuxEnabled() { + return nil + } + if err := badPrefix(fpath); err != nil { + return err + } + callback := func(p string, info os.FileInfo, err error) error { + return Setfilecon(p, scon) + } + + if recurse { + return filepath.Walk(fpath, callback) + } + + return Setfilecon(fpath, scon) +}