From 098389dc3e7bbba7c266ad24c909f3a5422e2908 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Fri, 3 Nov 2017 14:46:51 +0000 Subject: Parse SecurityOpts This should turn on handling of SELinux, NoNewPrivs, seccomp and Apparmor Signed-off-by: Daniel J Walsh Closes: #15 Approved by: rhatdan --- cmd/kpod/create.go | 188 ++++++++++++++++++++++++++++++++++------------------- cmd/kpod/run.go | 1 + cmd/kpod/spec.go | 23 +++++-- 3 files changed, 142 insertions(+), 70 deletions(-) (limited to 'cmd') diff --git a/cmd/kpod/create.go b/cmd/kpod/create.go index ddfe9e5ed..9d4d08b08 100644 --- a/cmd/kpod/create.go +++ b/cmd/kpod/create.go @@ -2,9 +2,12 @@ package main import ( "fmt" + "os" "strconv" + "strings" "github.com/docker/go-units" + "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/projectatomic/libpod/libpod" "github.com/urfave/cli" @@ -55,58 +58,62 @@ type createResourceConfig struct { } type createConfig struct { - args []string - capAdd []string // cap-add - capDrop []string // cap-drop - cidFile string - cgroupParent string // cgroup-parent - command []string - detach bool // detach - devices []*pb.Device // device - dnsOpt []string //dns-opt - dnsSearch []string //dns-search - dnsServers []string //dns - entrypoint string //entrypoint - env []string //env - expose []string //expose - groupAdd []uint32 // group-add - hostname string //hostname - image string - interactive bool //interactive - ip6Address string //ipv6 - ipAddress string //ip - labels map[string]string //label - linkLocalIP []string // link-local-ip - logDriver string // log-driver - logDriverOpt []string // log-opt - macAddress string //mac-address - name string //name - network string //network - networkAlias []string //network-alias - nsIPC string // ipc - nsNet string //net - nsPID string //pid - nsUser string - pod string //pod - privileged bool //privileged - publish []string //publish - publishAll bool //publish-all - readOnlyRootfs bool //read-only - resources createResourceConfig - rm bool //rm - securityOpts []string //security-opt - sigProxy bool //sig-proxy - stopSignal string // stop-signal - stopTimeout int64 // stop-timeout - storageOpts []string //storage-opt - sysctl map[string]string //sysctl - tmpfs []string // tmpfs - tty bool //tty - user uint32 //user - group uint32 // group - volumes []string //volume - volumesFrom []string //volumes-from - workDir string //workdir + args []string + capAdd []string // cap-add + capDrop []string // cap-drop + cidFile string + cgroupParent string // cgroup-parent + command []string + detach bool // detach + devices []*pb.Device // device + dnsOpt []string //dns-opt + dnsSearch []string //dns-search + dnsServers []string //dns + entrypoint string //entrypoint + env []string //env + expose []string //expose + groupAdd []uint32 // group-add + hostname string //hostname + image string + interactive bool //interactive + ip6Address string //ipv6 + ipAddress string //ip + labels map[string]string //label + linkLocalIP []string // link-local-ip + logDriver string // log-driver + logDriverOpt []string // log-opt + macAddress string //mac-address + name string //name + network string //network + networkAlias []string //network-alias + nsIPC string // ipc + nsNet string //net + nsPID string //pid + nsUser string + pod string //pod + privileged bool //privileged + publish []string //publish + publishAll bool //publish-all + readOnlyRootfs bool //read-only + resources createResourceConfig + rm bool //rm + sigProxy bool //sig-proxy + stopSignal string // stop-signal + stopTimeout int64 // stop-timeout + storageOpts []string //storage-opt + sysctl map[string]string //sysctl + tmpfs []string // tmpfs + tty bool //tty + user uint32 //user + group uint32 // group + volumes []string //volume + volumesFrom []string //volumes-from + workDir string //workdir + mountLabel string //SecurityOpts + processLabel string //SecurityOpts + noNewPrivileges bool //SecurityOpts + apparmorProfile string //SecurityOpts + seccompProfilePath string //SecurityOpts } var createDescription = "Creates a new container from the given image or" + @@ -169,6 +176,7 @@ func createCmd(c *cli.Context) error { } // Gather up the options for NewContainer which consist of With... funcs options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false)) + options = append(options, libpod.WithSELinuxMountLabel(createConfig.mountLabel)) ctr, err := runtime.NewContainer(runtimeSpec, options...) if err != nil { return err @@ -183,6 +191,49 @@ func createCmd(c *cli.Context) error { return nil } +const seccompDefaultPath = "/etc/crio/seccomp.json" + +func parseSecurityOpt(config *createConfig, securityOpts []string) error { + var ( + labelOpts []string + err error + ) + + for _, opt := range securityOpts { + if opt == "no-new-privileges" { + config.noNewPrivileges = true + } else { + con := strings.SplitN(opt, "=", 2) + if len(con) != 2 { + return fmt.Errorf("Invalid --security-opt 1: %q", opt) + } + + switch con[0] { + case "label": + labelOpts = append(labelOpts, con[1]) + case "apparmor": + config.apparmorProfile = con[1] + case "seccomp": + config.seccompProfilePath = con[1] + default: + return fmt.Errorf("Invalid --security-opt 2: %q", opt) + } + } + } + + if config.seccompProfilePath == "" { + if _, err := os.Stat(seccompDefaultPath); err != nil { + if !os.IsNotExist(err) { + return errors.Wrapf(err, "can't check if %q exists", seccompDefaultPath) + } + } else { + config.seccompProfilePath = seccompDefaultPath + } + } + config.processLabel, config.mountLabel, err = label.InitLabels(labelOpts) + return err +} + // Parses CLI options related to container creation into a config which can be // parsed into an OCI runtime spec func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, error) { @@ -323,20 +374,25 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er pidsLimit: c.Int64("pids-limit"), ulimit: c.StringSlice("ulimit"), }, - rm: c.Bool("rm"), - securityOpts: c.StringSlice("security-opt"), - sigProxy: c.Bool("sig-proxy"), - stopSignal: c.String("stop-signal"), - stopTimeout: c.Int64("stop-timeout"), - storageOpts: c.StringSlice("storage-opt"), - sysctl: sysctl, - tmpfs: c.StringSlice("tmpfs"), - tty: c.Bool("tty"), - user: uid, - group: gid, - volumes: c.StringSlice("volume"), - volumesFrom: c.StringSlice("volumes-from"), - workDir: c.String("workdir"), + rm: c.Bool("rm"), + sigProxy: c.Bool("sig-proxy"), + stopSignal: c.String("stop-signal"), + stopTimeout: c.Int64("stop-timeout"), + storageOpts: c.StringSlice("storage-opt"), + sysctl: sysctl, + tmpfs: c.StringSlice("tmpfs"), + tty: c.Bool("tty"), + user: uid, + group: gid, + volumes: c.StringSlice("volume"), + volumesFrom: c.StringSlice("volumes-from"), + workDir: c.String("workdir"), + } + + if !config.privileged { + if err := parseSecurityOpt(config, c.StringSlice("security-opt")); err != nil { + return nil, err + } } return config, nil diff --git a/cmd/kpod/run.go b/cmd/kpod/run.go index f1563edc3..cefa1d2d0 100644 --- a/cmd/kpod/run.go +++ b/cmd/kpod/run.go @@ -69,6 +69,7 @@ func runCmd(c *cli.Context) error { } // Gather up the options for NewContainer which consist of With... funcs options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false)) + options = append(options, libpod.WithSELinuxMountLabel(createConfig.mountLabel)) ctr, err := runtime.NewContainer(runtimeSpec, options...) if err != nil { return err diff --git a/cmd/kpod/spec.go b/cmd/kpod/spec.go index b6fb8b128..d30c0d1a5 100644 --- a/cmd/kpod/spec.go +++ b/cmd/kpod/spec.go @@ -1,7 +1,9 @@ package main import ( + "encoding/json" "fmt" + "io/ioutil" "strings" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -91,16 +93,30 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) { configSpec.Linux.Resources.Pids.Limit = config.resources.pidsLimit } + // SECURITY OPTS + configSpec.Process.NoNewPrivileges = config.noNewPrivileges + configSpec.Process.ApparmorProfile = config.apparmorProfile + configSpec.Process.SelinuxLabel = config.processLabel + configSpec.Linux.MountLabel = config.mountLabel + if config.seccompProfilePath != "" && config.seccompProfilePath != "unconfined" { + seccompProfile, err := ioutil.ReadFile(config.seccompProfilePath) + if err != nil { + return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.seccompProfilePath) + } + var seccompConfig spec.LinuxSeccomp + if err := json.Unmarshal(seccompProfile, &seccompConfig); err != nil { + return nil, errors.Wrapf(err, "decoding seccomp profile (%s) failed", config.seccompProfilePath) + } + configSpec.Linux.Seccomp = &seccompConfig + } + /* Capabilities: &configSpec.LinuxCapabilities{ // Rlimits []PosixRlimit // Where does this come from // Type string // Hard uint64 // Limit uint64 - // NoNewPrivileges bool // No user input for this - // ApparmorProfile string // No user input for this OOMScoreAdj: &config.resources.oomScoreAdj, - // Selinuxlabel }, Hooks: &configSpec.Hooks{}, //Annotations @@ -116,7 +132,6 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) { //CgroupsPath: //Namespaces: []LinuxNamespace //Devices - Seccomp: &configSpec.LinuxSeccomp{ // DefaultAction: // Architectures // Syscalls: -- cgit v1.2.3-54-g00ecf