mirror of
https://github.com/containers/podman.git
synced 2025-05-20 16:47:39 +08:00
Merge pull request #12208 from cdoern/podSecurityOpt
Pod Security Option support and Infra Inheritance changes
This commit is contained in:
@ -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,
|
||||
|
@ -231,6 +231,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.
|
||||
@ -471,7 +503,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))
|
||||
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user