From 3cc90bd2d843722a0c3577c324fabea397ad7083 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 3 Mar 2016 10:44:33 -0800 Subject: [PATCH] Add support for process overrides of settings This commit adds support to libcontainer to allow caps, no new privs, apparmor, and selinux process label to the process struct so that it can be used together of override the base settings on the container config per individual process. Signed-off-by: Michael Crosby --- exec.go | 31 ++++++++++++++++++++++++++++- libcontainer/configs/config.go | 6 +++--- libcontainer/container_linux.go | 15 +++++++++++++- libcontainer/init_linux.go | 3 +++ libcontainer/process.go | 10 ++++++++++ libcontainer/setns_init_linux.go | 8 ++++---- libcontainer/standard_init_linux.go | 6 +++--- spec.go | 10 +++------- utils.go | 8 ++++++-- 9 files changed, 76 insertions(+), 21 deletions(-) diff --git a/exec.go b/exec.go index 528d3ade..4c04e658 100644 --- a/exec.go +++ b/exec.go @@ -48,7 +48,7 @@ following will output a list of processes running in the container: Usage: "UID (format: [:])", }, cli.StringFlag{ - Name: "process,p", + Name: "process, p", Usage: "path to the process.json", }, cli.BoolFlag{ @@ -60,6 +60,23 @@ following will output a list of processes running in the container: Value: "", Usage: "specify the file to write the process id to", }, + cli.StringFlag{ + Name: "process-label", + Usage: "set the asm process label for the process commonly used with selinux", + }, + cli.StringFlag{ + Name: "apparmor", + Usage: "set the apparmor profile for the process", + }, + cli.BoolFlag{ + Name: "no-new-privs", + Usage: "set the no new privileges value for the process", + }, + cli.StringSliceFlag{ + Name: "cap, c", + Value: &cli.StringSlice{}, + Usage: "add a capability to the bounding set for the process", + }, }, Action: func(context *cli.Context) { if os.Geteuid() != 0 { @@ -118,6 +135,15 @@ func getProcess(context *cli.Context, bundle string) (*specs.Process, error) { if context.String("cwd") != "" { p.Cwd = context.String("cwd") } + if ap := context.String("apparmor"); ap != "" { + p.ApparmorProfile = ap + } + if l := context.String("process-label"); l != "" { + p.SelinuxLabel = l + } + if caps := context.StringSlice("cap"); len(caps) > 0 { + p.Capabilities = caps + } // append the passed env variables for _, e := range context.StringSlice("env") { p.Env = append(p.Env, e) @@ -126,6 +152,9 @@ func getProcess(context *cli.Context, bundle string) (*specs.Process, error) { if context.IsSet("tty") { p.Terminal = context.Bool("tty") } + if context.IsSet("no-new-privs") { + p.NoNewPrivileges = context.Bool("no-new-privs") + } // override the user, if passed if context.String("user") != "" { u := strings.SplitN(context.String("user"), ":", 2) diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go index 0325d58a..668fa5e3 100644 --- a/libcontainer/configs/config.go +++ b/libcontainer/configs/config.go @@ -128,11 +128,11 @@ type Config struct { // AppArmorProfile specifies the profile to apply to the process running in the container and is // change at the time the process is execed - AppArmorProfile string `json:"apparmor_profile"` + AppArmorProfile string `json:"apparmor_profile,omitempty"` // ProcessLabel specifies the label to apply to the process running in the container. It is // commonly used by selinux - ProcessLabel string `json:"process_label"` + ProcessLabel string `json:"process_label,omitempty"` // Rlimits specifies the resource limits, such as max open files, to set in the container // If Rlimits are not set, the container will inherit rlimits from the parent process @@ -172,7 +172,7 @@ type Config struct { Seccomp *Seccomp `json:"seccomp"` // NoNewPrivileges controls whether processes in the container can gain additional privileges. - NoNewPrivileges bool `json:"no_new_privileges"` + NoNewPrivileges bool `json:"no_new_privileges,omitempty"` // Hooks are a collection of actions to perform at various container lifecycle events. // Hooks are not able to be marshaled to json but they are also not needed to. diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index 01781f36..8e308fa6 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -319,7 +319,7 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, } func (c *linuxContainer) newInitConfig(process *Process) *initConfig { - return &initConfig{ + cfg := &initConfig{ Config: c.config, Args: process.Args, Env: process.Env, @@ -329,7 +329,20 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig { Capabilities: process.Capabilities, PassedFilesCount: len(process.ExtraFiles), ContainerId: c.ID(), + NoNewPrivileges: c.config.NoNewPrivileges, + AppArmorProfile: c.config.AppArmorProfile, + ProcessLabel: c.config.ProcessLabel, } + if process.NoNewPrivileges != nil { + cfg.NoNewPrivileges = *process.NoNewPrivileges + } + if process.AppArmorProfile != "" { + cfg.AppArmorProfile = process.AppArmorProfile + } + if process.Label != "" { + cfg.ProcessLabel = process.Label + } + return cfg } func newPipe() (parent *os.File, child *os.File, err error) { diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go index dd641e87..5b78b85a 100644 --- a/libcontainer/init_linux.go +++ b/libcontainer/init_linux.go @@ -48,6 +48,9 @@ type initConfig struct { Env []string `json:"env"` Cwd string `json:"cwd"` Capabilities []string `json:"capabilities"` + ProcessLabel string `json:"process_label"` + AppArmorProfile string `json:"apparmor_profile"` + NoNewPrivileges bool `json:"no_new_privileges"` User string `json:"user"` Config *configs.Config `json:"config"` Console string `json:"console"` diff --git a/libcontainer/process.go b/libcontainer/process.go index 8b4c558b..9b82cfac 100644 --- a/libcontainer/process.go +++ b/libcontainer/process.go @@ -48,6 +48,16 @@ type Process struct { // All capabilities not specified will be dropped from the processes capability mask Capabilities []string + // AppArmorProfile specifies the profile to apply to the process and is + // changed at the time the process is execed + AppArmorProfile string + + // Label specifies the label to apply to the process. It is commonly used by selinux + Label string + + // NoNewPrivileges controls whether processes can gain additional privileges. + NoNewPrivileges *bool + ops processOperations } diff --git a/libcontainer/setns_init_linux.go b/libcontainer/setns_init_linux.go index de356bce..2dfec9b7 100644 --- a/libcontainer/setns_init_linux.go +++ b/libcontainer/setns_init_linux.go @@ -34,7 +34,7 @@ func (l *linuxSetnsInit) Init() error { if err := setOomScoreAdj(l.config.Config.OomScoreAdj); err != nil { return err } - if l.config.Config.NoNewPrivileges { + if l.config.NoNewPrivileges { if err := system.Prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil { return err } @@ -47,11 +47,11 @@ func (l *linuxSetnsInit) Init() error { if err := finalizeNamespace(l.config); err != nil { return err } - if err := apparmor.ApplyProfile(l.config.Config.AppArmorProfile); err != nil { + if err := apparmor.ApplyProfile(l.config.AppArmorProfile); err != nil { return err } - if l.config.Config.ProcessLabel != "" { - if err := label.SetProcessLabel(l.config.Config.ProcessLabel); err != nil { + if l.config.ProcessLabel != "" { + if err := label.SetProcessLabel(l.config.ProcessLabel); err != nil { return err } } diff --git a/libcontainer/standard_init_linux.go b/libcontainer/standard_init_linux.go index 935b2eea..134205d5 100644 --- a/libcontainer/standard_init_linux.go +++ b/libcontainer/standard_init_linux.go @@ -91,10 +91,10 @@ func (l *linuxStandardInit) Init() error { return err } } - if err := apparmor.ApplyProfile(l.config.Config.AppArmorProfile); err != nil { + if err := apparmor.ApplyProfile(l.config.AppArmorProfile); err != nil { return err } - if err := label.SetProcessLabel(l.config.Config.ProcessLabel); err != nil { + if err := label.SetProcessLabel(l.config.ProcessLabel); err != nil { return err } @@ -117,7 +117,7 @@ func (l *linuxStandardInit) Init() error { if err != nil { return err } - if l.config.Config.NoNewPrivileges { + if l.config.NoNewPrivileges { if err := system.Prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil { return err } diff --git a/spec.go b/spec.go index e0e8abdf..9c9146d3 100644 --- a/spec.go +++ b/spec.go @@ -245,10 +245,9 @@ func createLibcontainerConfig(cgroupName string, spec *specs.LinuxSpec) (*config rootfsPath = filepath.Join(cwd, rootfsPath) } config := &configs.Config{ - Rootfs: rootfsPath, - Capabilities: spec.Process.Capabilities, - Readonlyfs: spec.Root.Readonly, - Hostname: spec.Hostname, + Rootfs: rootfsPath, + Readonlyfs: spec.Root.Readonly, + Hostname: spec.Hostname, Labels: []string{ "bundle=" + cwd, }, @@ -303,9 +302,6 @@ func createLibcontainerConfig(cgroupName string, spec *specs.LinuxSpec) (*config } config.Seccomp = seccomp config.Sysctl = spec.Linux.Sysctl - config.ProcessLabel = spec.Process.SelinuxLabel - config.AppArmorProfile = spec.Process.ApparmorProfile - config.NoNewPrivileges = spec.Process.NoNewPrivileges if oomScoreAdj := spec.Linux.Resources.OOMScoreAdj; oomScoreAdj != nil { config.OomScoreAdj = *oomScoreAdj } diff --git a/utils.go b/utils.go index 0477973a..64412189 100644 --- a/utils.go +++ b/utils.go @@ -219,8 +219,12 @@ func newProcess(p specs.Process) *libcontainer.Process { Args: p.Args, Env: p.Env, // TODO: fix libcontainer's API to better support uid/gid in a typesafe way. - User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID), - Cwd: p.Cwd, + User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID), + Cwd: p.Cwd, + Capabilities: p.Capabilities, + Label: p.SelinuxLabel, + NoNewPrivileges: &p.NoNewPrivileges, + AppArmorProfile: p.ApparmorProfile, } }