mirror of
https://github.com/containers/podman.git
synced 2025-05-20 16:47:39 +08:00
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:

committed by
Atomic Bot

parent
79a26cbd6d
commit
098389dc3e
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 ]
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user