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:
parent
e06631d6c2
commit
289270375a
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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, " ")
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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{}
|
||||
|
|
|
@ -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))
|
||||
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue