Parse SecurityOpts

This should turn on handling of SELinux, NoNewPrivs, seccomp and Apparmor

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>

Closes: #15
Approved by: rhatdan
This commit is contained in:
Daniel J Walsh
2017-11-03 14:46:51 +00:00
committed by Atomic Bot
parent 79a26cbd6d
commit 098389dc3e
6 changed files with 174 additions and 72 deletions

View File

@ -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"
@ -94,7 +97,6 @@ type createConfig struct {
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
@ -107,6 +109,11 @@ type createConfig struct {
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) {
@ -324,7 +375,6 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er
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"),
@ -339,5 +389,11 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er
workDir: c.String("workdir"),
}
if !config.privileged {
if err := parseSecurityOpt(config, c.StringSlice("security-opt")); err != nil {
return nil, err
}
}
return config, nil
}

View File

@ -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

View File

@ -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:

View File

@ -95,6 +95,7 @@ type containerConfig struct {
// Information on the image used for the root filesystem
RootfsImageID string `json:"rootfsImageID,omitempty"`
RootfsImageName string `json:"rootfsImageName,omitempty"`
MountLabel string `json:"MountLabel,omitempty"`
UseImageConfig bool `json:"useImageConfig"`
// Whether to keep container STDIN open
Stdin bool
@ -223,8 +224,7 @@ func (c *Container) setupImageRootfs() error {
return errors.Wrapf(ErrInvalidArg, "must provide image ID and image name to use an image")
}
// TODO SELinux mount label
containerInfo, err := c.runtime.storageService.CreateContainerStorage(c.runtime.imageContext, c.config.RootfsImageName, c.config.RootfsImageID, c.config.Name, c.config.ID, "")
containerInfo, err := c.runtime.storageService.CreateContainerStorage(c.runtime.imageContext, c.config.RootfsImageName, c.config.RootfsImageID, c.config.Name, c.config.ID, c.config.MountLabel)
if err != nil {
return errors.Wrapf(err, "error creating container storage")
}

View File

@ -255,6 +255,18 @@ func WithRootFSFromPath(path string) CtrCreateOption {
}
}
// WithSELinuxMountLabel sets the mount label for SELinux
func WithSELinuxMountLabel(mountLabel string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return ErrCtrFinalized
}
ctr.config.MountLabel = mountLabel
return nil
}
}
// WithRootFSFromImage sets up a fresh root filesystem using the given image
// If useImageConfig is specified, image volumes, environment variables, and
// other configuration from the image will be added to the config

View File

@ -18,3 +18,21 @@ ALPINE="docker.io/library/alpine:latest"
echo "$output"
[ "$status" -eq 0 ]
}
@test "run selinux test" {
if [ ! -e /usr/sbin/selinuxenabled ] || /usr/sbin/selinuxenabled; then
skip "SELinux not enabled"
fi
firstLabel=$(${KPOD_BINARY} ${KPOD_OPTIONS} run ${ALPINE} cat /proc/self/attr/current)
run ${KPOD_BINARY} ${KPOD_OPTIONS} run ${ALPINE} cat /proc/self/attr/current
echo "$output"
[ "$status" -eq 0 ]
[ "$output" != "${firstLabel}" ]
run bash -c "${KPOD_BINARY} ${KPOD_OPTIONS} run --security-opt label:level=s0:c1,c2 ${ALPINE} cat /proc/self/attr/current | grep s0:c1,c2"
echo "$output"
[ "$status" -eq 0 ]
}