Skip to content

Commit

Permalink
Parse SecurityOpts
Browse files Browse the repository at this point in the history
This should turn on handling of SELinux, NoNewPrivs, seccomp and Apparmor

Signed-off-by: Daniel J Walsh <[email protected]>

Closes: #15
Approved by: rhatdan
  • Loading branch information
rhatdan authored and rh-atomic-bot committed Nov 3, 2017
1 parent 79a26cb commit 098389d
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 72 deletions.
188 changes: 122 additions & 66 deletions cmd/kpod/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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" +
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions cmd/kpod/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 19 additions & 4 deletions cmd/kpod/spec.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"strings"

spec "github.com/opencontainers/runtime-spec/specs-go"
Expand Down Expand Up @@ -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
Expand All @@ -116,7 +132,6 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
//CgroupsPath:
//Namespaces: []LinuxNamespace
//Devices
Seccomp: &configSpec.LinuxSeccomp{
// DefaultAction:
// Architectures
// Syscalls:
Expand Down
4 changes: 2 additions & 2 deletions libpod/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
}
Expand Down
12 changes: 12 additions & 0 deletions libpod/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions test/kpod_run.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]

}

0 comments on commit 098389d

Please sign in to comment.