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

View File

@ -400,3 +400,14 @@ type ContainerMiscConfig struct {
// and if so, what type: always or once are possible non-nil entries // and if so, what type: always or once are possible non-nil entries
InitContainerType string `json:"init_container_type,omitempty"` 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 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. // Parse mount options so we can populate them in the mount structure.
// The mount passed in will be modified. // The mount passed in will be modified.
func parseMountOptionsForInspect(options []string, mount *define.InspectMount) { 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 = make([]string, 0, len(c.config.Groups))
hostConfig.GroupAdd = append(hostConfig.GroupAdd, c.config.Groups...) hostConfig.GroupAdd = append(hostConfig.GroupAdd, c.config.Groups...)
hostConfig.SecurityOpt = []string{}
if ctrSpec.Process != nil { if ctrSpec.Process != nil {
if ctrSpec.Process.OOMScoreAdj != nil { if ctrSpec.Process.OOMScoreAdj != nil {
hostConfig.OomScoreAdj = *ctrSpec.Process.OOMScoreAdj 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.ReadonlyRootfs = ctrSpec.Root.Readonly
hostConfig.ShmSize = c.config.ShmSize hostConfig.ShmSize = c.config.ShmSize
hostConfig.Runtime = "oci" hostConfig.Runtime = "oci"
@ -456,15 +475,6 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
if ctrSpec.Annotations[define.InspectAnnotationInit] == define.InspectResponseTrue { if ctrSpec.Annotations[define.InspectAnnotationInit] == define.InspectResponseTrue {
hostConfig.Init = true 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 // Resource limits

View File

@ -65,6 +65,8 @@ type InspectPodData struct {
BlkioDeviceReadBps []InspectBlkioThrottleDevice `json:"device_read_bps,omitempty"` BlkioDeviceReadBps []InspectBlkioThrottleDevice `json:"device_read_bps,omitempty"`
// VolumesFrom contains the containers that the pod inherits mounts from // VolumesFrom contains the containers that the pod inherits mounts from
VolumesFrom []string `json:"volumes_from,omitempty"` 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 // 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 // Pod Creation Options
// WithPodCreateCommand adds the full command plus arguments of the current // 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 inspectMounts []define.InspectMount
var devices []define.InspectDevice var devices []define.InspectDevice
var deviceLimits []define.InspectBlkioThrottleDevice var deviceLimits []define.InspectBlkioThrottleDevice
var infraSecurity []string
if p.state.InfraContainerID != "" { if p.state.InfraContainerID != "" {
infra, err := p.runtime.GetContainer(p.state.InfraContainerID) infra, err := p.runtime.GetContainer(p.state.InfraContainerID)
if err != nil { if err != nil {
@ -603,6 +604,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
infraConfig.UserNS = p.UserNSMode() infraConfig.UserNS = p.UserNSMode()
namedVolumes, mounts := infra.sortUserVolumes(infra.config.Spec) namedVolumes, mounts := infra.sortUserVolumes(infra.config.Spec)
inspectMounts, err = infra.GetInspectMounts(namedVolumes, infra.config.ImageVolumes, mounts) inspectMounts, err = infra.GetInspectMounts(namedVolumes, infra.config.ImageVolumes, mounts)
infraSecurity = infra.GetSecurityOptions()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -678,6 +680,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
Devices: devices, Devices: devices,
BlkioDeviceReadBps: deviceLimits, BlkioDeviceReadBps: deviceLimits,
VolumesFrom: p.VolumesFrom(), VolumesFrom: p.VolumesFrom(),
SecurityOpts: infraSecurity,
} }
return &inspectData, nil 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 := entities.NewInfraContainerCreateOptions() // options for pulling the image and FillOutSpec
infraOptions.Net = &entities.NetOptions{} infraOptions.Net = &entities.NetOptions{}
infraOptions.Devices = psg.Devices infraOptions.Devices = psg.Devices
infraOptions.SecurityOpt = psg.SecurityOpt
err = specgenutil.FillOutSpecGen(psg.InfraContainerSpec, &infraOptions, []string{}) // necessary for default values in many cases (userns, idmappings) err = specgenutil.FillOutSpecGen(psg.InfraContainerSpec, &infraOptions, []string{}) // necessary for default values in many cases (userns, idmappings)
if err != nil { if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error filling out specgen")) 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:"-"` Userns specgen.Namespace `json:"-"`
Volume []string `json:"volume,omitempty"` Volume []string `json:"volume,omitempty"`
VolumesFrom []string `json:"volumes_from,omitempty"` VolumesFrom []string `json:"volumes_from,omitempty"`
SecurityOpt []string `json:"security_opt,omitempty"`
} }
// PodLogsOptions describes the options to extract pod logs. // PodLogsOptions describes the options to extract pod logs.
@ -230,7 +231,7 @@ type ContainerCreateOptions struct {
Rm bool Rm bool
RootFS bool RootFS bool
Secrets []string Secrets []string
SecurityOpt []string SecurityOpt []string `json:"security_opt,omitempty"`
SdNotifyMode string SdNotifyMode string
ShmSize string ShmSize string
SignaturePolicy string SignaturePolicy string
@ -312,6 +313,7 @@ func ToPodSpecGen(s specgen.PodSpecGenerator, p *PodCreateOptions) (*specgen.Pod
s.Hostname = p.Hostname s.Hostname = p.Hostname
s.Labels = p.Labels s.Labels = p.Labels
s.Devices = p.Devices s.Devices = p.Devices
s.SecurityOpt = p.SecurityOpt
s.NoInfra = !p.Infra s.NoInfra = !p.Infra
if p.InfraCommand != nil && len(*p.InfraCommand) > 0 { if p.InfraCommand != nil && len(*p.InfraCommand) > 0 {
s.InfraCommand = strings.Split(*p.InfraCommand, " ") s.InfraCommand = strings.Split(*p.InfraCommand, " ")

View File

@ -2,13 +2,14 @@ package generate
import ( import (
"context" "context"
"fmt" "encoding/json"
"path/filepath" "path/filepath"
"strings" "strings"
cdi "github.com/container-orchestrated-devices/container-device-interface/pkg" cdi "github.com/container-orchestrated-devices/container-device-interface/pkg"
"github.com/containers/common/libimage" "github.com/containers/common/libimage"
"github.com/containers/podman/v3/libpod" "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/namespaces"
"github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/util" "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 // If joining a pod, retrieve the pod for use, and its infra container
var pod *libpod.Pod var pod *libpod.Pod
var infraConfig *libpod.ContainerConfig var infra *libpod.Container
if s.Pod != "" { if s.Pod != "" {
pod, err = rt.LookupPod(s.Pod) pod, err = rt.LookupPod(s.Pod)
if err != nil { if err != nil {
return nil, nil, nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod) return nil, nil, nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod)
} }
if pod.HasInfraContainer() { if pod.HasInfraContainer() {
infra, err := pod.InfraContainer() infra, err = pod.InfraContainer()
if err != nil { if err != nil {
return nil, nil, nil, err 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) { options := []libpod.CtrCreateOption{}
s.VolumesFrom = append(s.VolumesFrom, infraConfig.ID) compatibleOptions := &libpod.InfraInherit{}
} var infraSpec *spec.Spec
if infra != nil {
if infraConfig != nil && len(infraConfig.Spec.Linux.Devices) > 0 { options, infraSpec, compatibleOptions, err = Inherit(*infra)
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 { if err != nil {
return nil, nil, nil, err 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 { if err := FinishThrottleDevices(s); err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
@ -119,8 +107,6 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
s.CgroupNS = defaultNS s.CgroupNS = defaultNS
} }
options := []libpod.CtrCreateOption{}
if s.ContainerCreateCommand != nil { if s.ContainerCreateCommand != nil {
options = append(options, libpod.WithCreateCommand(s.ContainerCreateCommand)) 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 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 { if err != nil {
return nil, nil, nil, err 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) logrus.Debugf("setting container name %s", s.Name)
options = append(options, libpod.WithName(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 { if len(s.Devices) > 0 {
opts = extractCDIDevices(s) opts = ExtractCDIDevices(s)
options = append(options, opts...) 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 { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
if len(s.HostDeviceList) > 0 { if len(s.HostDeviceList) > 0 {
options = append(options, libpod.WithHostDevice(s.HostDeviceList)) 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 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) { 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) 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)) devs := make([]spec.LinuxDevice, 0, len(s.Devices))
var cdiDevs []string var cdiDevs []string
var options []libpod.CtrCreateOption var options []libpod.CtrCreateOption
@ -224,19 +213,16 @@ func extractCDIDevices(s *specgen.SpecGenerator) []libpod.CtrCreateOption {
cdiDevs = append(cdiDevs, device.Path) cdiDevs = append(cdiDevs, device.Path)
continue continue
} }
devs = append(devs, device) devs = append(devs, device)
} }
s.Devices = devs s.Devices = devs
if len(cdiDevs) > 0 { if len(cdiDevs) > 0 {
options = append(options, libpod.WithCDI(cdiDevs)) options = append(options, libpod.WithCDI(cdiDevs))
} }
return options 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 options []libpod.CtrCreateOption
var err error var err error
@ -317,7 +303,10 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
for _, imageVolume := range s.ImageVolumes { for _, imageVolume := range s.ImageVolumes {
destinations = append(destinations, imageVolume.Destination) destinations = append(destinations, imageVolume.Destination)
} }
if len(destinations) > 0 || !infraVolumes {
options = append(options, libpod.WithUserVolumes(destinations)) options = append(options, libpod.WithUserVolumes(destinations))
}
if len(volumes) != 0 { if len(volumes) != 0 {
var vols []*libpod.ContainerNamedVolume var vols []*libpod.ContainerNamedVolume
@ -405,7 +394,7 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if len(s.SelinuxOpts) > 0 { if len(s.SelinuxOpts) > 0 {
options = append(options, libpod.WithSecLabels(s.SelinuxOpts)) options = append(options, libpod.WithSecLabels(s.SelinuxOpts))
} else { } else {
if pod != nil { if pod != nil && len(compatibleOptions.InfraLabels) == 0 {
// duplicate the security options from the pod // duplicate the security options from the pod
processLabel, err := pod.ProcessLabel() processLabel, err := pod.ProcessLabel()
if err != nil { if err != nil {
@ -498,3 +487,33 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
return options, nil 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 ( import (
"context" "context"
"encoding/json"
"path" "path"
"strings" "strings"
@ -174,7 +175,7 @@ func getCGroupPermissons(unmask []string) string {
} }
// SpecGenToOCI returns the base configuration for the container. // 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) cgroupPerm := getCGroupPermissons(s.Unmask)
g, err := generate.New("linux") 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.AddAnnotation(key, val)
} }
if compatibleOptions.InfraResources == nil && s.ResourceLimits != nil {
g.Config.Linux.Resources = s.ResourceLimits 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 // Devices
var userDevices []spec.LinuxDevice
if s.Privileged { if s.Privileged {
// If privileged, we need to add all the host devices to the // If privileged, we need to add all the host devices to the
// spec. We do not add the user provided ones because we are // 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 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 // add default devices specified by caller
for _, device := range s.Devices { for _, device := range userDevices {
if err = DevicesFromPath(&g, device.Path); err != nil { if err = DevicesFromPath(&g, device.Path); err != nil {
return nil, err return nil, err
} }
} }
} }
s.HostDeviceList = s.Devices s.HostDeviceList = userDevices
// set the devices cgroup when not running in a user namespace // set the devices cgroup when not running in a user namespace
if !inUserNS && !s.Privileged { if !inUserNS && !s.Privileged {

View File

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

View File

@ -9,6 +9,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/containers/common/pkg/apparmor"
"github.com/containers/common/pkg/seccomp"
"github.com/containers/common/pkg/sysinfo" "github.com/containers/common/pkg/sysinfo"
"github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/util" "github.com/containers/podman/v3/pkg/util"
@ -16,6 +18,7 @@ import (
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec" . "github.com/onsi/gomega/gexec"
"github.com/opencontainers/selinux/go-selinux"
) )
var _ = Describe("Podman pod create", func() { var _ = Describe("Podman pod create", func() {
@ -967,4 +970,63 @@ ENTRYPOINT ["sleep","99999"]
Expect(inspect).Should(Exit(0)) Expect(inspect).Should(Exit(0))
Expect(inspect.OutputToString()).Should(Equal("host")) 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))
})
}) })