mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/projectatomic/libpod/libpod"
|
"github.com/projectatomic/libpod/libpod"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
@ -55,58 +58,62 @@ type createResourceConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type createConfig struct {
|
type createConfig struct {
|
||||||
args []string
|
args []string
|
||||||
capAdd []string // cap-add
|
capAdd []string // cap-add
|
||||||
capDrop []string // cap-drop
|
capDrop []string // cap-drop
|
||||||
cidFile string
|
cidFile string
|
||||||
cgroupParent string // cgroup-parent
|
cgroupParent string // cgroup-parent
|
||||||
command []string
|
command []string
|
||||||
detach bool // detach
|
detach bool // detach
|
||||||
devices []*pb.Device // device
|
devices []*pb.Device // device
|
||||||
dnsOpt []string //dns-opt
|
dnsOpt []string //dns-opt
|
||||||
dnsSearch []string //dns-search
|
dnsSearch []string //dns-search
|
||||||
dnsServers []string //dns
|
dnsServers []string //dns
|
||||||
entrypoint string //entrypoint
|
entrypoint string //entrypoint
|
||||||
env []string //env
|
env []string //env
|
||||||
expose []string //expose
|
expose []string //expose
|
||||||
groupAdd []uint32 // group-add
|
groupAdd []uint32 // group-add
|
||||||
hostname string //hostname
|
hostname string //hostname
|
||||||
image string
|
image string
|
||||||
interactive bool //interactive
|
interactive bool //interactive
|
||||||
ip6Address string //ipv6
|
ip6Address string //ipv6
|
||||||
ipAddress string //ip
|
ipAddress string //ip
|
||||||
labels map[string]string //label
|
labels map[string]string //label
|
||||||
linkLocalIP []string // link-local-ip
|
linkLocalIP []string // link-local-ip
|
||||||
logDriver string // log-driver
|
logDriver string // log-driver
|
||||||
logDriverOpt []string // log-opt
|
logDriverOpt []string // log-opt
|
||||||
macAddress string //mac-address
|
macAddress string //mac-address
|
||||||
name string //name
|
name string //name
|
||||||
network string //network
|
network string //network
|
||||||
networkAlias []string //network-alias
|
networkAlias []string //network-alias
|
||||||
nsIPC string // ipc
|
nsIPC string // ipc
|
||||||
nsNet string //net
|
nsNet string //net
|
||||||
nsPID string //pid
|
nsPID string //pid
|
||||||
nsUser string
|
nsUser string
|
||||||
pod string //pod
|
pod string //pod
|
||||||
privileged bool //privileged
|
privileged bool //privileged
|
||||||
publish []string //publish
|
publish []string //publish
|
||||||
publishAll bool //publish-all
|
publishAll bool //publish-all
|
||||||
readOnlyRootfs bool //read-only
|
readOnlyRootfs bool //read-only
|
||||||
resources createResourceConfig
|
resources createResourceConfig
|
||||||
rm bool //rm
|
rm bool //rm
|
||||||
securityOpts []string //security-opt
|
sigProxy bool //sig-proxy
|
||||||
sigProxy bool //sig-proxy
|
stopSignal string // stop-signal
|
||||||
stopSignal string // stop-signal
|
stopTimeout int64 // stop-timeout
|
||||||
stopTimeout int64 // stop-timeout
|
storageOpts []string //storage-opt
|
||||||
storageOpts []string //storage-opt
|
sysctl map[string]string //sysctl
|
||||||
sysctl map[string]string //sysctl
|
tmpfs []string // tmpfs
|
||||||
tmpfs []string // tmpfs
|
tty bool //tty
|
||||||
tty bool //tty
|
user uint32 //user
|
||||||
user uint32 //user
|
group uint32 // group
|
||||||
group uint32 // group
|
volumes []string //volume
|
||||||
volumes []string //volume
|
volumesFrom []string //volumes-from
|
||||||
volumesFrom []string //volumes-from
|
workDir string //workdir
|
||||||
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" +
|
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
|
// Gather up the options for NewContainer which consist of With... funcs
|
||||||
options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false))
|
options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false))
|
||||||
|
options = append(options, libpod.WithSELinuxMountLabel(createConfig.mountLabel))
|
||||||
ctr, err := runtime.NewContainer(runtimeSpec, options...)
|
ctr, err := runtime.NewContainer(runtimeSpec, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -183,6 +191,49 @@ func createCmd(c *cli.Context) error {
|
|||||||
return nil
|
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
|
// Parses CLI options related to container creation into a config which can be
|
||||||
// parsed into an OCI runtime spec
|
// parsed into an OCI runtime spec
|
||||||
func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, error) {
|
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"),
|
pidsLimit: c.Int64("pids-limit"),
|
||||||
ulimit: c.StringSlice("ulimit"),
|
ulimit: c.StringSlice("ulimit"),
|
||||||
},
|
},
|
||||||
rm: c.Bool("rm"),
|
rm: c.Bool("rm"),
|
||||||
securityOpts: c.StringSlice("security-opt"),
|
sigProxy: c.Bool("sig-proxy"),
|
||||||
sigProxy: c.Bool("sig-proxy"),
|
stopSignal: c.String("stop-signal"),
|
||||||
stopSignal: c.String("stop-signal"),
|
stopTimeout: c.Int64("stop-timeout"),
|
||||||
stopTimeout: c.Int64("stop-timeout"),
|
storageOpts: c.StringSlice("storage-opt"),
|
||||||
storageOpts: c.StringSlice("storage-opt"),
|
sysctl: sysctl,
|
||||||
sysctl: sysctl,
|
tmpfs: c.StringSlice("tmpfs"),
|
||||||
tmpfs: c.StringSlice("tmpfs"),
|
tty: c.Bool("tty"),
|
||||||
tty: c.Bool("tty"),
|
user: uid,
|
||||||
user: uid,
|
group: gid,
|
||||||
group: gid,
|
volumes: c.StringSlice("volume"),
|
||||||
volumes: c.StringSlice("volume"),
|
volumesFrom: c.StringSlice("volumes-from"),
|
||||||
volumesFrom: c.StringSlice("volumes-from"),
|
workDir: c.String("workdir"),
|
||||||
workDir: c.String("workdir"),
|
}
|
||||||
|
|
||||||
|
if !config.privileged {
|
||||||
|
if err := parseSecurityOpt(config, c.StringSlice("security-opt")); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
|
@ -69,6 +69,7 @@ func runCmd(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
// Gather up the options for NewContainer which consist of With... funcs
|
// Gather up the options for NewContainer which consist of With... funcs
|
||||||
options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false))
|
options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false))
|
||||||
|
options = append(options, libpod.WithSELinuxMountLabel(createConfig.mountLabel))
|
||||||
ctr, err := runtime.NewContainer(runtimeSpec, options...)
|
ctr, err := runtime.NewContainer(runtimeSpec, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
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
|
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{
|
Capabilities: &configSpec.LinuxCapabilities{
|
||||||
// Rlimits []PosixRlimit // Where does this come from
|
// Rlimits []PosixRlimit // Where does this come from
|
||||||
// Type string
|
// Type string
|
||||||
// Hard uint64
|
// Hard uint64
|
||||||
// Limit uint64
|
// Limit uint64
|
||||||
// NoNewPrivileges bool // No user input for this
|
|
||||||
// ApparmorProfile string // No user input for this
|
|
||||||
OOMScoreAdj: &config.resources.oomScoreAdj,
|
OOMScoreAdj: &config.resources.oomScoreAdj,
|
||||||
// Selinuxlabel
|
|
||||||
},
|
},
|
||||||
Hooks: &configSpec.Hooks{},
|
Hooks: &configSpec.Hooks{},
|
||||||
//Annotations
|
//Annotations
|
||||||
@ -116,7 +132,6 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
|
|||||||
//CgroupsPath:
|
//CgroupsPath:
|
||||||
//Namespaces: []LinuxNamespace
|
//Namespaces: []LinuxNamespace
|
||||||
//Devices
|
//Devices
|
||||||
Seccomp: &configSpec.LinuxSeccomp{
|
|
||||||
// DefaultAction:
|
// DefaultAction:
|
||||||
// Architectures
|
// Architectures
|
||||||
// Syscalls:
|
// Syscalls:
|
||||||
|
@ -95,6 +95,7 @@ type containerConfig struct {
|
|||||||
// Information on the image used for the root filesystem
|
// Information on the image used for the root filesystem
|
||||||
RootfsImageID string `json:"rootfsImageID,omitempty"`
|
RootfsImageID string `json:"rootfsImageID,omitempty"`
|
||||||
RootfsImageName string `json:"rootfsImageName,omitempty"`
|
RootfsImageName string `json:"rootfsImageName,omitempty"`
|
||||||
|
MountLabel string `json:"MountLabel,omitempty"`
|
||||||
UseImageConfig bool `json:"useImageConfig"`
|
UseImageConfig bool `json:"useImageConfig"`
|
||||||
// Whether to keep container STDIN open
|
// Whether to keep container STDIN open
|
||||||
Stdin bool
|
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")
|
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, c.config.MountLabel)
|
||||||
containerInfo, err := c.runtime.storageService.CreateContainerStorage(c.runtime.imageContext, c.config.RootfsImageName, c.config.RootfsImageID, c.config.Name, c.config.ID, "")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error creating container storage")
|
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
|
// WithRootFSFromImage sets up a fresh root filesystem using the given image
|
||||||
// If useImageConfig is specified, image volumes, environment variables, and
|
// If useImageConfig is specified, image volumes, environment variables, and
|
||||||
// other configuration from the image will be added to the config
|
// other configuration from the image will be added to the config
|
||||||
|
@ -18,3 +18,21 @@ ALPINE="docker.io/library/alpine:latest"
|
|||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$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