From bc3c671e21236db6c7f5133385cf37d8adeef6d7 Mon Sep 17 00:00:00 2001 From: Dan Walsh Date: Thu, 17 Jul 2014 14:57:57 -0400 Subject: [PATCH] Add label.InitLabels functioni. Allows generation of labels based on options This will allow us to do the following with docker. Customize the way that a labeling system like SELinux will run on a container. --label-opt="user:USER" : Set the label user for the container --label-opt="role:ROLE" : Set the label role for the container --label-opt="type:TYPE" : Set the label type for the container --label-opt="level:LEVEL" : Set the label level for the container --label-opt="disabled" : Turn off label confinement for the container Since we are passing a list of string options instead of a space separated string of options, I will change function calls to use InitLabels instead of GenLabels. Genlabels interface is Deprecated. Docker-DCO-1.1-Signed-off-by: Dan Walsh (github: rhatdan) --- label/label.go | 9 +++++- label/label_selinux.go | 56 ++++++++++++++++++++++++++++--------- label/label_selinux_test.go | 48 +++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 label/label_selinux_test.go diff --git a/label/label.go b/label/label.go index 5c8228cdd..73869b36c 100644 --- a/label/label.go +++ b/label/label.go @@ -2,6 +2,13 @@ package label +// InitLabels returns the process label and file labels to be used within +// the container. A list of options can be passed into this function to alter +// the labels. +func InitLabels(options []string) (string, string, error) { + return "", "", nil +} + func GenLabels(options string) (string, string, error) { return "", "", nil } @@ -22,7 +29,7 @@ func Relabel(path string, fileLabel string, relabel string) error { return nil } -func GetPidCon(pid int) (string, error) { +func GetPidLabel(pid int) (string, error) { return "", nil } diff --git a/label/label_selinux.go b/label/label_selinux.go index aa502a3d6..5b1380a2b 100644 --- a/label/label_selinux.go +++ b/label/label_selinux.go @@ -9,30 +9,49 @@ import ( "github.com/docker/libcontainer/selinux" ) -func GenLabels(options string) (string, string, error) { +// InitLabels returns the process label and file labels to be used within +// the container. A list of options can be passed into this function to alter +// the labels. The labels returned will include a random MCS String, that is +// guaranteed to be unique. +func InitLabels(options []string) (string, string, error) { if !selinux.SelinuxEnabled() { return "", "", nil } var err error processLabel, mountLabel := selinux.GetLxcContexts() if processLabel != "" { - var ( - 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] + pcon := selinux.NewContext(processLabel) + mcon := selinux.NewContext(mountLabel) + for _, opt := range options { + if opt == "disable" { + return "", "", nil + } + if i := strings.Index(opt, ":"); i == -1 { + return "", "", fmt.Errorf("Bad SELinux Option") + } + con := strings.SplitN(opt, ":", 2) + pcon[con[0]] = con[1] + if con[0] == "level" || con[0] == "user" { + mcon[con[0]] = con[1] } - processLabel = pcon.Get() - mountLabel, err = selinux.CopyLevel(processLabel, mountLabel) } + processLabel = pcon.Get() + mountLabel = mcon.Get() } return processLabel, mountLabel, err } +// DEPRECATED: The GenLabels function is only to be used during the transition to the official API. +func GenLabels(options string) (string, string, error) { + return InitLabels(strings.Fields(options)) +} + +// FormatMountLabel returns a string to be used by the mount command. +// The format of this string will be used to alter the labeling of the mountpoint. +// The string returned is suitable to be used as the options field of the mount command. +// If you need to have additional mount point options, you can pass them in as +// the first parameter. Second parameter is the label that you wish to apply +// to all content in the mount point. func FormatMountLabel(src, mountLabel string) string { if mountLabel != "" { switch src { @@ -45,6 +64,8 @@ func FormatMountLabel(src, mountLabel string) string { return src } +// SetProcessLabel takes a process label and tells the kernel to assign the +// label to the next program executed by the current process. func SetProcessLabel(processLabel string) error { if selinux.SelinuxEnabled() { return selinux.Setexeccon(processLabel) @@ -52,6 +73,9 @@ func SetProcessLabel(processLabel string) error { return nil } +// GetProcessLabel returns the process label that the kernel will assign +// to the next program executed by the current process. If "" is returned +// this indicates that the default labeling will happen for the process. func GetProcessLabel() (string, error) { if selinux.SelinuxEnabled() { return selinux.Getexeccon() @@ -59,6 +83,7 @@ func GetProcessLabel() (string, error) { return "", nil } +// SetFileLabel modifies the "path" label to the specified file label func SetFileLabel(path string, fileLabel string) error { if selinux.SelinuxEnabled() && fileLabel != "" { return selinux.Setfilecon(path, fileLabel) @@ -83,17 +108,22 @@ func Relabel(path string, fileLabel string, relabel string) error { return selinux.Chcon(path, fileLabel, true) } -func GetPidCon(pid int) (string, error) { +// GetPidLabel will return the label of the process running with the specified pid +func GetPidLabel(pid int) (string, error) { if !selinux.SelinuxEnabled() { return "", nil } return selinux.Getpidcon(pid) } +// Init initialises the labeling system func Init() { selinux.SelinuxEnabled() } +// ReserveLabel will record the fact that the MCS label has already been used. +// This will prevent InitLabels from using the MCS label in a newly created +// container func ReserveLabel(label string) error { selinux.ReserveLabel(label) return nil diff --git a/label/label_selinux_test.go b/label/label_selinux_test.go new file mode 100644 index 000000000..c83654f6b --- /dev/null +++ b/label/label_selinux_test.go @@ -0,0 +1,48 @@ +// +build selinux,linux + +package label + +import ( + "testing" + + "github.com/docker/libcontainer/selinux" +) + +func TestInit(t *testing.T) { + if selinux.SelinuxEnabled() { + var testNull []string + plabel, mlabel, err := InitLabels(testNull) + if err != nil { + t.Log("InitLabels Failed") + t.Fatal(err) + } + testDisabled := []string{"disable"} + plabel, mlabel, err = InitLabels(testDisabled) + if err != nil { + t.Log("InitLabels Disabled Failed") + t.Fatal(err) + } + if plabel != "" { + t.Log("InitLabels Disabled Failed") + t.Fatal() + } + testUser := []string{"user:user_u", "role:user_r", "type:user_t", "level:s0:c1,c15"} + plabel, mlabel, err = InitLabels(testUser) + if err != nil { + t.Log("InitLabels User Failed") + t.Fatal(err) + } + if plabel != "user_u:user_r:user_t:s0:c1,c15" || mlabel != "user_u:object_r:svirt_sandbox_file_t:s0:c1,c15" { + t.Log("InitLabels User Failed") + t.Log(plabel, mlabel) + t.Fatal(err) + } + + testBadData := []string{"user", "role:user_r", "type:user_t", "level:s0:c1,c15"} + plabel, mlabel, err = InitLabels(testBadData) + if err == nil { + t.Log("InitLabels Bad Failed") + t.Fatal(err) + } + } +}