Pod Security Option support

Added support for pod security options. These are applied to infra and passed down to the
containers as added (unless overridden).

Modified the inheritance process from infra, creating a new function Inherit() which reads the config, and marshals the compatible options into an intermediate struct `InfraInherit`
This is then unmarshaled into a container config and all of this is added to the CtrCreateOptions. Removes the need (mostly) for special additons which complicate the Container_create
code and pod creation.

resolves #12173

Signed-off-by: cdoern <cdoern@redhat.com>
This commit is contained in:
cdoern 2021-11-04 23:48:35 -04:00
parent e06631d6c2
commit 289270375a
13 changed files with 268 additions and 73 deletions

View File

@ -540,14 +540,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
_ = cmd.RegisterFlagCompletionFunc(secretFlagName, AutocompleteSecrets)
securityOptFlagName := "security-opt"
createFlags.StringArrayVar(
&cf.SecurityOpt,
securityOptFlagName, []string{},
"Security Options",
)
_ = cmd.RegisterFlagCompletionFunc(securityOptFlagName, AutocompleteSecurityOption)
shmSizeFlagName := "shm-size"
createFlags.String(
shmSizeFlagName, shmSize(),
@ -720,6 +712,13 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
`If a container with the same name exists, replace it`,
)
}
securityOptFlagName := "security-opt"
createFlags.StringArrayVar(
&cf.SecurityOpt,
securityOptFlagName, []string{},
"Security Options",
)
_ = cmd.RegisterFlagCompletionFunc(securityOptFlagName, AutocompleteSecurityOption)
subgidnameFlagName := "subgidname"
createFlags.StringVar(
@ -890,6 +889,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
"Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)",
)
_ = cmd.RegisterFlagCompletionFunc(deviceReadBpsFlagName, completion.AutocompleteDefault)
volumesFromFlagName := "volumes-from"
createFlags.StringArrayVar(
&cf.VolumesFrom,

View File

@ -222,6 +222,38 @@ NOTE: This cannot be modified once the pod is created.
If another pod with the same name already exists, replace and remove it. The default is **false**.
#### **--security-opt**=*option*
Security Options
- `apparmor=unconfined` : Turn off apparmor confinement for the pod
- `apparmor=your-profile` : Set the apparmor confinement profile for the pod
- `label=user:USER` : Set the label user for the pod processes
- `label=role:ROLE` : Set the label role for the pod processes
- `label=type:TYPE` : Set the label process type for the pod processes
- `label=level:LEVEL` : Set the label level for the pod processes
- `label=filetype:TYPE` : Set the label file type for the pod files
- `label=disable` : Turn off label separation for the pod
Note: Labeling can be disabled for all pods/containers by setting label=false in the **containers.conf** (`/etc/containers/containers.conf` or `$HOME/.config/containers/containers.conf`) file.
- `mask=/path/1:/path/2` : The paths to mask separated by a colon. A masked path
cannot be accessed inside the containers within the pod.
- `no-new-privileges` : Disable container processes from gaining additional privileges
- `seccomp=unconfined` : Turn off seccomp confinement for the pod
- `seccomp=profile.json` : Whitelisted syscalls seccomp Json file to be used as a seccomp filter
- `proc-opts=OPTIONS` : Comma-separated list of options to use for the /proc mount. More details for the
possible mount options are specified in the **proc(5)** man page.
- **unmask**=_ALL_ or _/path/1:/path/2_, or shell expanded paths (/proc/*): Paths to unmask separated by a colon. If set to **ALL**, it will unmask all the paths that are masked or made read only by default.
The default masked paths are **/proc/acpi, /proc/kcore, /proc/keys, /proc/latency_stats, /proc/sched_debug, /proc/scsi, /proc/timer_list, /proc/timer_stats, /sys/firmware, and /sys/fs/selinux.** The default paths that are read only are **/proc/asound, /proc/bus, /proc/fs, /proc/irq, /proc/sys, /proc/sysrq-trigger, /sys/fs/cgroup**.
Note: Labeling can be disabled for all containers by setting label=false in the **containers.conf** (`/etc/containers/containers.conf` or `$HOME/.config/containers/containers.conf`) file.
#### **--share**=*namespace*
A comma-separated list of kernel namespaces to share. If none or "" is specified, no namespaces will be shared. The namespaces to choose from are ipc, net, pid, uts.
@ -462,7 +494,7 @@ $ podman pod create --network net1:ip=10.89.1.5 --network net2:ip=10.89.10.10
```
## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**
**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **containers.conf(1)**
## HISTORY

View File

@ -400,3 +400,14 @@ type ContainerMiscConfig struct {
// and if so, what type: always or once are possible non-nil entries
InitContainerType string `json:"init_container_type,omitempty"`
}
type InfraInherit struct {
InfraSecurity ContainerSecurityConfig
InfraLabels []string `json:"labelopts,omitempty"`
InfraVolumes []*ContainerNamedVolume `json:"namedVolumes,omitempty"`
InfraOverlay []*ContainerOverlayVolume `json:"overlayVolumes,omitempty"`
InfraImageVolumes []*ContainerImageVolume `json:"ctrImageVolumes,omitempty"`
InfraUserVolumes []string `json:"userVolumes,omitempty"`
InfraResources *spec.LinuxResources `json:"resources,omitempty"`
InfraDevices []spec.LinuxDevice `json:"device_host_src,omitempty"`
}

View File

@ -273,6 +273,27 @@ func (c *Container) GetInspectMounts(namedVolumes []*ContainerNamedVolume, image
return inspectMounts, nil
}
// GetSecurityOptions retrives and returns the security related annotations and process information upon inspection
func (c *Container) GetSecurityOptions() []string {
ctrSpec := c.config.Spec
SecurityOpt := []string{}
if ctrSpec.Process != nil {
if ctrSpec.Process.NoNewPrivileges {
SecurityOpt = append(SecurityOpt, "no-new-privileges")
}
}
if label, ok := ctrSpec.Annotations[define.InspectAnnotationLabel]; ok {
SecurityOpt = append(SecurityOpt, fmt.Sprintf("label=%s", label))
}
if seccomp, ok := ctrSpec.Annotations[define.InspectAnnotationSeccomp]; ok {
SecurityOpt = append(SecurityOpt, fmt.Sprintf("seccomp=%s", seccomp))
}
if apparmor, ok := ctrSpec.Annotations[define.InspectAnnotationApparmor]; ok {
SecurityOpt = append(SecurityOpt, fmt.Sprintf("apparmor=%s", apparmor))
}
return SecurityOpt
}
// Parse mount options so we can populate them in the mount structure.
// The mount passed in will be modified.
func parseMountOptionsForInspect(options []string, mount *define.InspectMount) {
@ -422,16 +443,14 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
hostConfig.GroupAdd = make([]string, 0, len(c.config.Groups))
hostConfig.GroupAdd = append(hostConfig.GroupAdd, c.config.Groups...)
hostConfig.SecurityOpt = []string{}
if ctrSpec.Process != nil {
if ctrSpec.Process.OOMScoreAdj != nil {
hostConfig.OomScoreAdj = *ctrSpec.Process.OOMScoreAdj
}
if ctrSpec.Process.NoNewPrivileges {
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, "no-new-privileges")
}
}
hostConfig.SecurityOpt = c.GetSecurityOptions()
hostConfig.ReadonlyRootfs = ctrSpec.Root.Readonly
hostConfig.ShmSize = c.config.ShmSize
hostConfig.Runtime = "oci"
@ -456,15 +475,6 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
if ctrSpec.Annotations[define.InspectAnnotationInit] == define.InspectResponseTrue {
hostConfig.Init = true
}
if label, ok := ctrSpec.Annotations[define.InspectAnnotationLabel]; ok {
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("label=%s", label))
}
if seccomp, ok := ctrSpec.Annotations[define.InspectAnnotationSeccomp]; ok {
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("seccomp=%s", seccomp))
}
if apparmor, ok := ctrSpec.Annotations[define.InspectAnnotationApparmor]; ok {
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("apparmor=%s", apparmor))
}
}
// Resource limits

View File

@ -65,6 +65,8 @@ type InspectPodData struct {
BlkioDeviceReadBps []InspectBlkioThrottleDevice `json:"device_read_bps,omitempty"`
// VolumesFrom contains the containers that the pod inherits mounts from
VolumesFrom []string `json:"volumes_from,omitempty"`
// SecurityOpt contains the specified security labels and related SELinux information
SecurityOpts []string `json:"security_opt,omitempty"`
}
// InspectPodInfraConfig contains the configuration of the pod's infra

View File

@ -1816,6 +1816,25 @@ func WithSelectedPasswordManagement(passwd *bool) CtrCreateOption {
}
}
// WithInfraConfig allows for inheritance of compatible config entities from the infra container
func WithInfraConfig(compatibleOptions InfraInherit) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
compatMarshal, err := json.Marshal(compatibleOptions)
if err != nil {
return errors.New("Could not marshal compatible options")
}
err = json.Unmarshal(compatMarshal, ctr.config)
if err != nil {
return errors.New("Could not unmarshal compatible options into contrainer config")
}
return nil
}
}
// Pod Creation Options
// WithPodCreateCommand adds the full command plus arguments of the current

View File

@ -586,6 +586,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
var inspectMounts []define.InspectMount
var devices []define.InspectDevice
var deviceLimits []define.InspectBlkioThrottleDevice
var infraSecurity []string
if p.state.InfraContainerID != "" {
infra, err := p.runtime.GetContainer(p.state.InfraContainerID)
if err != nil {
@ -603,6 +604,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
infraConfig.UserNS = p.UserNSMode()
namedVolumes, mounts := infra.sortUserVolumes(infra.config.Spec)
inspectMounts, err = infra.GetInspectMounts(namedVolumes, infra.config.ImageVolumes, mounts)
infraSecurity = infra.GetSecurityOptions()
if err != nil {
return nil, err
}
@ -678,6 +680,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
Devices: devices,
BlkioDeviceReadBps: deviceLimits,
VolumesFrom: p.VolumesFrom(),
SecurityOpts: infraSecurity,
}
return &inspectData, nil

View File

@ -42,6 +42,7 @@ func PodCreate(w http.ResponseWriter, r *http.Request) {
infraOptions := entities.NewInfraContainerCreateOptions() // options for pulling the image and FillOutSpec
infraOptions.Net = &entities.NetOptions{}
infraOptions.Devices = psg.Devices
infraOptions.SecurityOpt = psg.SecurityOpt
err = specgenutil.FillOutSpecGen(psg.InfraContainerSpec, &infraOptions, []string{}) // necessary for default values in many cases (userns, idmappings)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error filling out specgen"))

View File

@ -138,6 +138,7 @@ type PodCreateOptions struct {
Userns specgen.Namespace `json:"-"`
Volume []string `json:"volume,omitempty"`
VolumesFrom []string `json:"volumes_from,omitempty"`
SecurityOpt []string `json:"security_opt,omitempty"`
}
// PodLogsOptions describes the options to extract pod logs.
@ -230,7 +231,7 @@ type ContainerCreateOptions struct {
Rm bool
RootFS bool
Secrets []string
SecurityOpt []string
SecurityOpt []string `json:"security_opt,omitempty"`
SdNotifyMode string
ShmSize string
SignaturePolicy string
@ -312,6 +313,7 @@ func ToPodSpecGen(s specgen.PodSpecGenerator, p *PodCreateOptions) (*specgen.Pod
s.Hostname = p.Hostname
s.Labels = p.Labels
s.Devices = p.Devices
s.SecurityOpt = p.SecurityOpt
s.NoInfra = !p.Infra
if p.InfraCommand != nil && len(*p.InfraCommand) > 0 {
s.InfraCommand = strings.Split(*p.InfraCommand, " ")

View File

@ -2,13 +2,14 @@ package generate
import (
"context"
"fmt"
"encoding/json"
"path/filepath"
"strings"
cdi "github.com/container-orchestrated-devices/container-device-interface/pkg"
"github.com/containers/common/libimage"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/namespaces"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/util"
@ -29,43 +30,30 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
// If joining a pod, retrieve the pod for use, and its infra container
var pod *libpod.Pod
var infraConfig *libpod.ContainerConfig
var infra *libpod.Container
if s.Pod != "" {
pod, err = rt.LookupPod(s.Pod)
if err != nil {
return nil, nil, nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod)
}
if pod.HasInfraContainer() {
infra, err := pod.InfraContainer()
infra, err = pod.InfraContainer()
if err != nil {
return nil, nil, nil, err
}
infraConfig = infra.Config()
}
}
if infraConfig != nil && (len(infraConfig.NamedVolumes) > 0 || len(infraConfig.UserVolumes) > 0 || len(infraConfig.ImageVolumes) > 0 || len(infraConfig.OverlayVolumes) > 0) {
s.VolumesFrom = append(s.VolumesFrom, infraConfig.ID)
options := []libpod.CtrCreateOption{}
compatibleOptions := &libpod.InfraInherit{}
var infraSpec *spec.Spec
if infra != nil {
options, infraSpec, compatibleOptions, err = Inherit(*infra)
if err != nil {
return nil, nil, nil, err
}
}
if infraConfig != nil && len(infraConfig.Spec.Linux.Devices) > 0 {
s.DevicesFrom = append(s.DevicesFrom, infraConfig.ID)
}
if infraConfig != nil && infraConfig.Spec.Linux.Resources != nil && infraConfig.Spec.Linux.Resources.BlockIO != nil && len(infraConfig.Spec.Linux.Resources.BlockIO.ThrottleReadBpsDevice) > 0 {
tempDev := make(map[string]spec.LinuxThrottleDevice)
for _, val := range infraConfig.Spec.Linux.Resources.BlockIO.ThrottleReadBpsDevice {
nodes, err := util.FindDeviceNodes()
if err != nil {
return nil, nil, nil, err
}
key := fmt.Sprintf("%d:%d", val.Major, val.Minor)
tempDev[nodes[key]] = spec.LinuxThrottleDevice{Rate: uint64(val.Rate)}
}
for i, dev := range s.ThrottleReadBpsDevice {
tempDev[i] = dev
}
s.ThrottleReadBpsDevice = tempDev
}
if err := FinishThrottleDevices(s); err != nil {
return nil, nil, nil, err
}
@ -119,8 +107,6 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
s.CgroupNS = defaultNS
}
options := []libpod.CtrCreateOption{}
if s.ContainerCreateCommand != nil {
options = append(options, libpod.WithCreateCommand(s.ContainerCreateCommand))
}
@ -165,7 +151,8 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
return nil, nil, nil, err
}
opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, imageData, command)
infraVolumes := (len(compatibleOptions.InfraVolumes) > 0 || len(compatibleOptions.InfraUserVolumes) > 0 || len(compatibleOptions.InfraImageVolumes) > 0)
opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, imageData, command, infraVolumes, *compatibleOptions)
if err != nil {
return nil, nil, nil, err
}
@ -178,27 +165,29 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
logrus.Debugf("setting container name %s", s.Name)
options = append(options, libpod.WithName(s.Name))
}
if len(s.DevicesFrom) > 0 {
for _, dev := range s.DevicesFrom {
ctr, err := rt.GetContainer(dev)
if err != nil {
return nil, nil, nil, err
}
devices := ctr.DeviceHostSrc()
s.Devices = append(s.Devices, devices...)
}
}
if len(s.Devices) > 0 {
opts = extractCDIDevices(s)
opts = ExtractCDIDevices(s)
options = append(options, opts...)
}
runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts, pod, command)
runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts, pod, command, compatibleOptions)
if err != nil {
return nil, nil, nil, err
}
if len(s.HostDeviceList) > 0 {
options = append(options, libpod.WithHostDevice(s.HostDeviceList))
}
if infraSpec != nil && infraSpec.Linux != nil { // if we are inheriting Linux info from a pod...
// Pass Security annotations
if len(infraSpec.Annotations[define.InspectAnnotationLabel]) > 0 && len(runtimeSpec.Annotations[define.InspectAnnotationLabel]) == 0 {
runtimeSpec.Annotations[define.InspectAnnotationLabel] = infraSpec.Annotations[define.InspectAnnotationLabel]
}
if len(infraSpec.Annotations[define.InspectAnnotationSeccomp]) > 0 && len(runtimeSpec.Annotations[define.InspectAnnotationSeccomp]) == 0 {
runtimeSpec.Annotations[define.InspectAnnotationSeccomp] = infraSpec.Annotations[define.InspectAnnotationSeccomp]
}
if len(infraSpec.Annotations[define.InspectAnnotationApparmor]) > 0 && len(runtimeSpec.Annotations[define.InspectAnnotationApparmor]) == 0 {
runtimeSpec.Annotations[define.InspectAnnotationApparmor] = infraSpec.Annotations[define.InspectAnnotationApparmor]
}
}
return runtimeSpec, s, options, err
}
func ExecuteCreate(ctx context.Context, rt *libpod.Runtime, runtimeSpec *spec.Spec, s *specgen.SpecGenerator, infra bool, options ...libpod.CtrCreateOption) (*libpod.Container, error) {
@ -210,7 +199,7 @@ func ExecuteCreate(ctx context.Context, rt *libpod.Runtime, runtimeSpec *spec.Sp
return ctr, rt.PrepareVolumeOnCreateContainer(ctx, ctr)
}
func extractCDIDevices(s *specgen.SpecGenerator) []libpod.CtrCreateOption {
func ExtractCDIDevices(s *specgen.SpecGenerator) []libpod.CtrCreateOption {
devs := make([]spec.LinuxDevice, 0, len(s.Devices))
var cdiDevs []string
var options []libpod.CtrCreateOption
@ -224,19 +213,16 @@ func extractCDIDevices(s *specgen.SpecGenerator) []libpod.CtrCreateOption {
cdiDevs = append(cdiDevs, device.Path)
continue
}
devs = append(devs, device)
}
s.Devices = devs
if len(cdiDevs) > 0 {
options = append(options, libpod.WithCDI(cdiDevs))
}
return options
}
func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, overlays []*specgen.OverlayVolume, imageData *libimage.ImageData, command []string) ([]libpod.CtrCreateOption, error) {
func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, overlays []*specgen.OverlayVolume, imageData *libimage.ImageData, command []string, infraVolumes bool, compatibleOptions libpod.InfraInherit) ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption
var err error
@ -317,7 +303,10 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
for _, imageVolume := range s.ImageVolumes {
destinations = append(destinations, imageVolume.Destination)
}
options = append(options, libpod.WithUserVolumes(destinations))
if len(destinations) > 0 || !infraVolumes {
options = append(options, libpod.WithUserVolumes(destinations))
}
if len(volumes) != 0 {
var vols []*libpod.ContainerNamedVolume
@ -405,7 +394,7 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if len(s.SelinuxOpts) > 0 {
options = append(options, libpod.WithSecLabels(s.SelinuxOpts))
} else {
if pod != nil {
if pod != nil && len(compatibleOptions.InfraLabels) == 0 {
// duplicate the security options from the pod
processLabel, err := pod.ProcessLabel()
if err != nil {
@ -498,3 +487,33 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
return options, nil
}
func Inherit(infra libpod.Container) (opts []libpod.CtrCreateOption, infraS *spec.Spec, compat *libpod.InfraInherit, err error) {
options := []libpod.CtrCreateOption{}
compatibleOptions := &libpod.InfraInherit{}
infraConf := infra.Config()
infraSpec := infraConf.Spec
config, err := json.Marshal(infraConf)
if err != nil {
return nil, nil, nil, err
}
err = json.Unmarshal(config, compatibleOptions)
if err != nil {
return nil, nil, nil, err
}
if infraSpec.Linux != nil && infraSpec.Linux.Resources != nil {
resources, err := json.Marshal(infraSpec.Linux.Resources)
if err != nil {
return nil, nil, nil, err
}
err = json.Unmarshal(resources, &compatibleOptions.InfraResources)
if err != nil {
return nil, nil, nil, err
}
}
if compatibleOptions != nil {
options = append(options, libpod.WithInfraConfig(*compatibleOptions))
}
return options, infraSpec, compatibleOptions, nil
}

View File

@ -2,6 +2,7 @@ package generate
import (
"context"
"encoding/json"
"path"
"strings"
@ -174,7 +175,7 @@ func getCGroupPermissons(unmask []string) string {
}
// SpecGenToOCI returns the base configuration for the container.
func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *libimage.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string) (*spec.Spec, error) {
func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *libimage.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string, compatibleOptions *libpod.InfraInherit) (*spec.Spec, error) {
cgroupPerm := getCGroupPermissons(s.Unmask)
g, err := generate.New("linux")
@ -299,9 +300,32 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
g.AddAnnotation(key, val)
}
g.Config.Linux.Resources = s.ResourceLimits
if compatibleOptions.InfraResources == nil && s.ResourceLimits != nil {
g.Config.Linux.Resources = s.ResourceLimits
} else if s.ResourceLimits != nil { // if we have predefined resource limits we need to make sure we keep the infra and container limits
originalResources, err := json.Marshal(s.ResourceLimits)
if err != nil {
return nil, err
}
infraResources, err := json.Marshal(compatibleOptions.InfraResources)
if err != nil {
return nil, err
}
err = json.Unmarshal(infraResources, s.ResourceLimits) // put infra's resource limits in the container
if err != nil {
return nil, err
}
err = json.Unmarshal(originalResources, s.ResourceLimits) // make sure we did not override anything
if err != nil {
return nil, err
}
g.Config.Linux.Resources = s.ResourceLimits
} else {
g.Config.Linux.Resources = compatibleOptions.InfraResources
}
// Devices
var userDevices []spec.LinuxDevice
if s.Privileged {
// If privileged, we need to add all the host devices to the
// spec. We do not add the user provided ones because we are
@ -316,14 +340,19 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
return nil, err
}
}
if len(compatibleOptions.InfraDevices) > 0 && len(s.Devices) == 0 {
userDevices = compatibleOptions.InfraDevices
} else {
userDevices = s.Devices
}
// add default devices specified by caller
for _, device := range s.Devices {
for _, device := range userDevices {
if err = DevicesFromPath(&g, device.Path); err != nil {
return nil, err
}
}
}
s.HostDeviceList = s.Devices
s.HostDeviceList = userDevices
// set the devices cgroup when not running in a user namespace
if !inUserNS && !s.Privileged {

View File

@ -196,6 +196,7 @@ type PodSpecGenerator struct {
PodCgroupConfig
PodResourceConfig
PodStorageConfig
PodSecurityConfig
InfraContainerSpec *SpecGenerator `json:"-"`
}
@ -210,6 +211,10 @@ type PodResourceConfig struct {
ThrottleReadBpsDevice map[string]spec.LinuxThrottleDevice `json:"throttleReadBpsDevice,omitempty"`
}
type PodSecurityConfig struct {
SecurityOpt []string `json:"security_opt,omitempty"`
}
// NewPodSpecGenerator creates a new pod spec
func NewPodSpecGenerator() *PodSpecGenerator {
return &PodSpecGenerator{}

View File

@ -9,6 +9,8 @@ import (
"strconv"
"strings"
"github.com/containers/common/pkg/apparmor"
"github.com/containers/common/pkg/seccomp"
"github.com/containers/common/pkg/sysinfo"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/util"
@ -16,6 +18,7 @@ import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
"github.com/opencontainers/selinux/go-selinux"
)
var _ = Describe("Podman pod create", func() {
@ -967,4 +970,63 @@ ENTRYPOINT ["sleep","99999"]
Expect(inspect).Should(Exit(0))
Expect(inspect.OutputToString()).Should(Equal("host"))
})
It("podman pod create --security-opt", func() {
if !selinux.GetEnabled() {
Skip("SELinux not enabled")
}
podCreate := podmanTest.Podman([]string{"pod", "create", "--security-opt", "label=type:spc_t", "--security-opt", "seccomp=unconfined"})
podCreate.WaitWithDefaultTimeout()
Expect(podCreate).Should(Exit(0))
ctrCreate := podmanTest.Podman([]string{"container", "create", "--pod", podCreate.OutputToString(), ALPINE})
ctrCreate.WaitWithDefaultTimeout()
Expect(ctrCreate).Should(Exit(0))
ctrInspect := podmanTest.InspectContainer(ctrCreate.OutputToString())
Expect(ctrInspect[0].HostConfig.SecurityOpt).To(Equal([]string{"label=type:spc_t", "seccomp=unconfined"}))
podCreate = podmanTest.Podman([]string{"pod", "create", "--security-opt", "label=disable"})
podCreate.WaitWithDefaultTimeout()
Expect(podCreate).Should(Exit(0))
ctrCreate = podmanTest.Podman([]string{"container", "run", "-it", "--pod", podCreate.OutputToString(), ALPINE, "cat", "/proc/self/attr/current"})
ctrCreate.WaitWithDefaultTimeout()
Expect(ctrCreate).Should(Exit(0))
match, _ := ctrCreate.GrepString("spc_t")
Expect(match).Should(BeTrue())
})
It("podman pod create --security-opt seccomp", func() {
if !seccomp.IsEnabled() {
Skip("seccomp is not enabled")
}
podCreate := podmanTest.Podman([]string{"pod", "create", "--security-opt", "seccomp=unconfined"})
podCreate.WaitWithDefaultTimeout()
Expect(podCreate).Should(Exit(0))
ctrCreate := podmanTest.Podman([]string{"container", "create", "--pod", podCreate.OutputToString(), ALPINE})
ctrCreate.WaitWithDefaultTimeout()
Expect(ctrCreate).Should(Exit(0))
ctrInspect := podmanTest.InspectContainer(ctrCreate.OutputToString())
Expect(ctrInspect[0].HostConfig.SecurityOpt).To(Equal([]string{"seccomp=unconfined"}))
})
It("podman pod create --security-opt apparmor test", func() {
if !apparmor.IsEnabled() {
Skip("Apparmor is not enabled")
}
podCreate := podmanTest.Podman([]string{"pod", "create", "--security-opt", fmt.Sprintf("apparmor=%s", apparmor.Profile)})
podCreate.WaitWithDefaultTimeout()
Expect(podCreate).Should(Exit(0))
ctrCreate := podmanTest.Podman([]string{"container", "create", "--pod", podCreate.OutputToString(), ALPINE})
ctrCreate.WaitWithDefaultTimeout()
Expect(ctrCreate).Should(Exit(0))
inspect := podmanTest.InspectContainer(ctrCreate.OutputToString())
Expect(inspect[0].AppArmorProfile).To(Equal(apparmor.Profile))
})
})