summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel J Walsh <dwalsh@redhat.com>2017-11-03 14:46:51 +0000
committerAtomic Bot <atomic-devel@projectatomic.io>2017-11-03 20:55:10 +0000
commit098389dc3e7bbba7c266ad24c909f3a5422e2908 (patch)
tree6b060ab5edc032bf63acb37489241b788c0f9381
parent79a26cbd6dc5bff97726c4280db45362ddc83881 (diff)
downloadpodman-098389dc3e7bbba7c266ad24c909f3a5422e2908.tar.gz
podman-098389dc3e7bbba7c266ad24c909f3a5422e2908.tar.bz2
podman-098389dc3e7bbba7c266ad24c909f3a5422e2908.zip
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
-rw-r--r--cmd/kpod/create.go188
-rw-r--r--cmd/kpod/run.go1
-rw-r--r--cmd/kpod/spec.go23
-rw-r--r--libpod/container.go4
-rw-r--r--libpod/options.go12
-rw-r--r--test/kpod_run.bats18
6 files changed, 174 insertions, 72 deletions
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:
diff --git a/libpod/container.go b/libpod/container.go
index 9f9d4ef3e..7c2f921f3 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -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")
}
diff --git a/libpod/options.go b/libpod/options.go
index 982655fc0..10cb605c2 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -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
diff --git a/test/kpod_run.bats b/test/kpod_run.bats
index 4945691a7..d0cac96de 100644
--- a/test/kpod_run.bats
+++ b/test/kpod_run.bats
@@ -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 ]
+
+}