mirror of
https://github.com/containers/podman.git
synced 2025-05-20 16:47:39 +08:00

Implement a policy for selecting a seccomp profile. In addition to the default behaviour (default profile unless --security-opt seccomp is set) add a second policy doing a lookup in the image annotation. If the image has the "io.containers.seccomp.profile" set its value will be interpreted as a seccomp profile. The policy can be selected via the new --seccomp-policy CLI flag. Once the containers.conf support is merged into libpod, we can add an option there as well. Note that this feature is marked as experimental and may change in the future. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
430 lines
14 KiB
Go
430 lines
14 KiB
Go
package createconfig
|
|
|
|
import (
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"github.com/containers/image/v5/manifest"
|
|
"github.com/containers/libpod/libpod"
|
|
"github.com/containers/libpod/libpod/define"
|
|
"github.com/containers/libpod/pkg/namespaces"
|
|
"github.com/containers/storage"
|
|
"github.com/docker/go-connections/nat"
|
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/opencontainers/runtime-tools/generate"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// Type constants
|
|
const (
|
|
bps = iota
|
|
iops
|
|
)
|
|
|
|
// CreateResourceConfig represents resource elements in CreateConfig
|
|
// structures
|
|
type CreateResourceConfig struct {
|
|
BlkioWeight uint16 // blkio-weight
|
|
BlkioWeightDevice []string // blkio-weight-device
|
|
CPUPeriod uint64 // cpu-period
|
|
CPUQuota int64 // cpu-quota
|
|
CPURtPeriod uint64 // cpu-rt-period
|
|
CPURtRuntime int64 // cpu-rt-runtime
|
|
CPUShares uint64 // cpu-shares
|
|
CPUs float64 // cpus
|
|
CPUsetCPUs string
|
|
CPUsetMems string // cpuset-mems
|
|
DeviceReadBps []string // device-read-bps
|
|
DeviceReadIOps []string // device-read-iops
|
|
DeviceWriteBps []string // device-write-bps
|
|
DeviceWriteIOps []string // device-write-iops
|
|
DisableOomKiller bool // oom-kill-disable
|
|
KernelMemory int64 // kernel-memory
|
|
Memory int64 //memory
|
|
MemoryReservation int64 // memory-reservation
|
|
MemorySwap int64 //memory-swap
|
|
MemorySwappiness int // memory-swappiness
|
|
OomScoreAdj int //oom-score-adj
|
|
PidsLimit int64 // pids-limit
|
|
ShmSize int64
|
|
Ulimit []string //ulimit
|
|
}
|
|
|
|
// PidConfig configures the pid namespace for the container
|
|
type PidConfig struct {
|
|
PidMode namespaces.PidMode //pid
|
|
}
|
|
|
|
// IpcConfig configures the ipc namespace for the container
|
|
type IpcConfig struct {
|
|
IpcMode namespaces.IpcMode //ipc
|
|
}
|
|
|
|
// CgroupConfig configures the cgroup namespace for the container
|
|
type CgroupConfig struct {
|
|
Cgroups string
|
|
Cgroupns string
|
|
CgroupParent string // cgroup-parent
|
|
CgroupMode namespaces.CgroupMode //cgroup
|
|
}
|
|
|
|
// UserConfig configures the user namespace for the container
|
|
type UserConfig struct {
|
|
GroupAdd []string // group-add
|
|
IDMappings *storage.IDMappingOptions
|
|
UsernsMode namespaces.UsernsMode //userns
|
|
User string //user
|
|
}
|
|
|
|
// UtsConfig configures the uts namespace for the container
|
|
type UtsConfig struct {
|
|
UtsMode namespaces.UTSMode //uts
|
|
NoHosts bool
|
|
HostAdd []string //add-host
|
|
Hostname string
|
|
}
|
|
|
|
// NetworkConfig configures the network namespace for the container
|
|
type NetworkConfig struct {
|
|
DNSOpt []string //dns-opt
|
|
DNSSearch []string //dns-search
|
|
DNSServers []string //dns
|
|
ExposedPorts map[nat.Port]struct{}
|
|
HTTPProxy bool
|
|
IP6Address string //ipv6
|
|
IPAddress string //ip
|
|
LinkLocalIP []string // link-local-ip
|
|
MacAddress string //mac-address
|
|
NetMode namespaces.NetworkMode //net
|
|
Network string //network
|
|
NetworkAlias []string //network-alias
|
|
PortBindings nat.PortMap
|
|
Publish []string //publish
|
|
PublishAll bool //publish-all
|
|
}
|
|
|
|
// SeccompPolicy determines which seccomp profile gets applied to the container.
|
|
type SeccompPolicy int
|
|
|
|
const (
|
|
// SeccompPolicyDefault - if set use SecurityConfig.SeccompProfilePath,
|
|
// otherwise use the default profile. The SeccompProfilePath might be
|
|
// explicitly set by the user.
|
|
SeccompPolicyDefault SeccompPolicy = iota
|
|
// SeccompPolicyImage - if set use SecurityConfig.SeccompProfileFromImage,
|
|
// otherwise follow SeccompPolicyDefault.
|
|
SeccompPolicyImage
|
|
)
|
|
|
|
// Map for easy lookups of supported policies.
|
|
var supportedSeccompPolicies = map[string]SeccompPolicy{
|
|
"": SeccompPolicyDefault,
|
|
"default": SeccompPolicyDefault,
|
|
"image": SeccompPolicyImage,
|
|
}
|
|
|
|
// LookupSeccompPolicy looksup the corresponding SeccompPolicy for the specified
|
|
// string. If none is found, an errors is returned including the list of
|
|
// supported policies.
|
|
// Note that an empty string resolved to SeccompPolicyDefault.
|
|
func LookupSeccompPolicy(s string) (SeccompPolicy, error) {
|
|
policy, exists := supportedSeccompPolicies[s]
|
|
if exists {
|
|
return policy, nil
|
|
}
|
|
|
|
// Sort the keys first as maps are non-deterministic.
|
|
keys := []string{}
|
|
for k := range supportedSeccompPolicies {
|
|
if k != "" {
|
|
keys = append(keys, k)
|
|
}
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
return -1, errors.Errorf("invalid seccomp policy %q: valid policies are %+q", s, keys)
|
|
}
|
|
|
|
// SecurityConfig configures the security features for the container
|
|
type SecurityConfig struct {
|
|
CapAdd []string // cap-add
|
|
CapDrop []string // cap-drop
|
|
LabelOpts []string //SecurityOpts
|
|
NoNewPrivs bool //SecurityOpts
|
|
ApparmorProfile string //SecurityOpts
|
|
SeccompProfilePath string //SecurityOpts
|
|
SeccompProfileFromImage string // seccomp profile from the container image
|
|
SeccompPolicy SeccompPolicy
|
|
SecurityOpts []string
|
|
Privileged bool //privileged
|
|
ReadOnlyRootfs bool //read-only
|
|
ReadOnlyTmpfs bool //read-only-tmpfs
|
|
Sysctl map[string]string //sysctl
|
|
}
|
|
|
|
// CreateConfig is a pre OCI spec structure. It represents user input from varlink or the CLI
|
|
type CreateConfig struct {
|
|
Annotations map[string]string
|
|
Args []string
|
|
CidFile string
|
|
ConmonPidFile string
|
|
Command []string // Full command that will be used
|
|
UserCommand []string // User-entered command (or image CMD)
|
|
Detach bool // detach
|
|
Devices []string // device
|
|
Entrypoint []string //entrypoint
|
|
Env map[string]string //env
|
|
HealthCheck *manifest.Schema2HealthConfig
|
|
Init bool // init
|
|
InitPath string //init-path
|
|
Image string
|
|
ImageID string
|
|
BuiltinImgVolumes map[string]struct{} // volumes defined in the image config
|
|
ImageVolumeType string // how to handle the image volume, either bind, tmpfs, or ignore
|
|
Interactive bool //interactive
|
|
Labels map[string]string //label
|
|
LogDriver string // log-driver
|
|
LogDriverOpt []string // log-opt
|
|
Name string //name
|
|
PodmanPath string
|
|
Pod string //pod
|
|
Quiet bool //quiet
|
|
Resources CreateResourceConfig
|
|
RestartPolicy string
|
|
Rm bool //rm
|
|
StopSignal syscall.Signal // stop-signal
|
|
StopTimeout uint // stop-timeout
|
|
Systemd bool
|
|
Tmpfs []string // tmpfs
|
|
Tty bool //tty
|
|
Mounts []spec.Mount
|
|
MountsFlag []string // mounts
|
|
NamedVolumes []*libpod.ContainerNamedVolume
|
|
Volumes []string //volume
|
|
VolumesFrom []string
|
|
WorkDir string //workdir
|
|
Rootfs string
|
|
Security SecurityConfig
|
|
Syslog bool // Whether to enable syslog on exit commands
|
|
|
|
// Namespaces
|
|
Pid PidConfig
|
|
Ipc IpcConfig
|
|
Cgroup CgroupConfig
|
|
User UserConfig
|
|
Uts UtsConfig
|
|
Network NetworkConfig
|
|
}
|
|
|
|
func u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
|
|
func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm }
|
|
|
|
// CreateBlockIO returns a LinuxBlockIO struct from a CreateConfig
|
|
func (c *CreateConfig) CreateBlockIO() (*spec.LinuxBlockIO, error) {
|
|
return c.createBlockIO()
|
|
}
|
|
|
|
func (c *CreateConfig) createExitCommand(runtime *libpod.Runtime) ([]string, error) {
|
|
config, err := runtime.GetConfig()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// We need a cleanup process for containers in the current model.
|
|
// But we can't assume that the caller is Podman - it could be another
|
|
// user of the API.
|
|
// As such, provide a way to specify a path to Podman, so we can
|
|
// still invoke a cleanup process.
|
|
cmd := c.PodmanPath
|
|
if cmd == "" {
|
|
cmd, _ = os.Executable()
|
|
}
|
|
|
|
command := []string{cmd,
|
|
"--root", config.StorageConfig.GraphRoot,
|
|
"--runroot", config.StorageConfig.RunRoot,
|
|
"--log-level", logrus.GetLevel().String(),
|
|
"--cgroup-manager", config.CgroupManager,
|
|
"--tmpdir", config.TmpDir,
|
|
}
|
|
if config.OCIRuntime != "" {
|
|
command = append(command, []string{"--runtime", config.OCIRuntime}...)
|
|
}
|
|
if config.StorageConfig.GraphDriverName != "" {
|
|
command = append(command, []string{"--storage-driver", config.StorageConfig.GraphDriverName}...)
|
|
}
|
|
for _, opt := range config.StorageConfig.GraphDriverOptions {
|
|
command = append(command, []string{"--storage-opt", opt}...)
|
|
}
|
|
if config.EventsLogger != "" {
|
|
command = append(command, []string{"--events-backend", config.EventsLogger}...)
|
|
}
|
|
|
|
if c.Syslog {
|
|
command = append(command, "--syslog", "true")
|
|
}
|
|
command = append(command, []string{"container", "cleanup"}...)
|
|
|
|
if c.Rm {
|
|
command = append(command, "--rm")
|
|
}
|
|
|
|
return command, nil
|
|
}
|
|
|
|
// GetContainerCreateOptions takes a CreateConfig and returns a slice of CtrCreateOptions
|
|
func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod, mounts []spec.Mount, namedVolumes []*libpod.ContainerNamedVolume) ([]libpod.CtrCreateOption, error) {
|
|
var options []libpod.CtrCreateOption
|
|
var err error
|
|
|
|
if c.Interactive {
|
|
options = append(options, libpod.WithStdin())
|
|
}
|
|
if c.Systemd {
|
|
options = append(options, libpod.WithSystemd())
|
|
}
|
|
if c.Name != "" {
|
|
logrus.Debugf("setting container name %s", c.Name)
|
|
options = append(options, libpod.WithName(c.Name))
|
|
}
|
|
if c.Pod != "" {
|
|
logrus.Debugf("adding container to pod %s", c.Pod)
|
|
options = append(options, runtime.WithPod(pod))
|
|
}
|
|
|
|
if len(mounts) != 0 || len(namedVolumes) != 0 {
|
|
destinations := []string{}
|
|
|
|
// Take all mount and named volume destinations.
|
|
for _, mount := range mounts {
|
|
destinations = append(destinations, mount.Destination)
|
|
}
|
|
for _, volume := range namedVolumes {
|
|
destinations = append(destinations, volume.Dest)
|
|
}
|
|
|
|
options = append(options, libpod.WithUserVolumes(destinations))
|
|
}
|
|
|
|
if len(namedVolumes) != 0 {
|
|
options = append(options, libpod.WithNamedVolumes(namedVolumes))
|
|
}
|
|
|
|
if len(c.UserCommand) != 0 {
|
|
options = append(options, libpod.WithCommand(c.UserCommand))
|
|
}
|
|
|
|
// Add entrypoint unconditionally
|
|
// If it's empty it's because it was explicitly set to "" or the image
|
|
// does not have one
|
|
options = append(options, libpod.WithEntrypoint(c.Entrypoint))
|
|
|
|
// TODO: MNT, USER, CGROUP
|
|
options = append(options, libpod.WithStopSignal(c.StopSignal))
|
|
options = append(options, libpod.WithStopTimeout(c.StopTimeout))
|
|
|
|
logPath := getLoggingPath(c.LogDriverOpt)
|
|
if logPath != "" {
|
|
options = append(options, libpod.WithLogPath(logPath))
|
|
}
|
|
|
|
if c.LogDriver != "" {
|
|
options = append(options, libpod.WithLogDriver(c.LogDriver))
|
|
}
|
|
|
|
secOpts, err := c.Security.ToCreateOptions()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
options = append(options, secOpts...)
|
|
|
|
nsOpts, err := c.Cgroup.ToCreateOptions(runtime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
options = append(options, nsOpts...)
|
|
|
|
nsOpts, err = c.Ipc.ToCreateOptions(runtime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
options = append(options, nsOpts...)
|
|
|
|
nsOpts, err = c.Pid.ToCreateOptions(runtime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
options = append(options, nsOpts...)
|
|
|
|
nsOpts, err = c.Network.ToCreateOptions(runtime, &c.User)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
options = append(options, nsOpts...)
|
|
|
|
nsOpts, err = c.Uts.ToCreateOptions(runtime, pod)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
options = append(options, nsOpts...)
|
|
|
|
nsOpts, err = c.User.ToCreateOptions(runtime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
options = append(options, nsOpts...)
|
|
|
|
useImageVolumes := c.ImageVolumeType == TypeBind
|
|
// Gather up the options for NewContainer which consist of With... funcs
|
|
options = append(options, libpod.WithRootFSFromImage(c.ImageID, c.Image, useImageVolumes))
|
|
options = append(options, libpod.WithConmonPidFile(c.ConmonPidFile))
|
|
options = append(options, libpod.WithLabels(c.Labels))
|
|
options = append(options, libpod.WithShmSize(c.Resources.ShmSize))
|
|
if c.Rootfs != "" {
|
|
options = append(options, libpod.WithRootFS(c.Rootfs))
|
|
}
|
|
// Default used if not overridden on command line
|
|
|
|
if c.RestartPolicy != "" {
|
|
if c.RestartPolicy == "unless-stopped" {
|
|
return nil, errors.Wrapf(define.ErrInvalidArg, "the unless-stopped restart policy is not supported")
|
|
}
|
|
|
|
split := strings.Split(c.RestartPolicy, ":")
|
|
if len(split) > 1 {
|
|
numTries, err := strconv.Atoi(split[1])
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "%s is not a valid number of retries for restart policy", split[1])
|
|
}
|
|
if numTries < 0 {
|
|
return nil, errors.Wrapf(define.ErrInvalidArg, "restart policy requires a positive number of retries")
|
|
}
|
|
options = append(options, libpod.WithRestartRetries(uint(numTries)))
|
|
}
|
|
options = append(options, libpod.WithRestartPolicy(split[0]))
|
|
}
|
|
|
|
// Always use a cleanup process to clean up Podman after termination
|
|
exitCmd, err := c.createExitCommand(runtime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
options = append(options, libpod.WithExitCommand(exitCmd))
|
|
|
|
if c.HealthCheck != nil {
|
|
options = append(options, libpod.WithHealthCheck(c.HealthCheck))
|
|
logrus.Debugf("New container has a health check")
|
|
}
|
|
return options, nil
|
|
}
|
|
|
|
// AddPrivilegedDevices iterates through host devices and adds all
|
|
// host devices to the spec
|
|
func (c *CreateConfig) AddPrivilegedDevices(g *generate.Generator) error {
|
|
return c.addPrivilegedDevices(g)
|
|
}
|