Split up create config handling of namespaces and security

As it stands, createconfig is a huge struct. This works fine when the only caller is when we create a container with a fully created config. However, if we wish to share code for security and namespace configuration, a single large struct becomes unweildy, as well as difficult to configure with the single createConfigToOCISpec function.

This PR breaks up namespace and security configuration into their own structs, with the eventual goal of allowing the namespace/security fields to be configured by the pod create cli, and allow the infra container to share this with the pod's containers.

Signed-off-by: Peter Hunt <pehunt@redhat.com>
This commit is contained in:
Peter Hunt
2019-09-11 16:50:02 -04:00
parent 3463a7194c
commit dcf3c742b1
12 changed files with 909 additions and 712 deletions

View File

@ -26,7 +26,6 @@ import (
"github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/signal"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -195,72 +194,6 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
return ctr, createConfig, nil return ctr, createConfig, nil
} }
func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string, runtime *libpod.Runtime) error {
var (
labelOpts []string
)
if config.PidMode.IsHost() {
labelOpts = append(labelOpts, label.DisableSecOpt()...)
} else if config.PidMode.IsContainer() {
ctr, err := runtime.LookupContainer(config.PidMode.Container())
if err != nil {
return errors.Wrapf(err, "container %q not found", config.PidMode.Container())
}
secopts, err := label.DupSecOpt(ctr.ProcessLabel())
if err != nil {
return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
}
labelOpts = append(labelOpts, secopts...)
}
if config.IpcMode.IsHost() {
labelOpts = append(labelOpts, label.DisableSecOpt()...)
} else if config.IpcMode.IsContainer() {
ctr, err := runtime.LookupContainer(config.IpcMode.Container())
if err != nil {
return errors.Wrapf(err, "container %q not found", config.IpcMode.Container())
}
secopts, err := label.DupSecOpt(ctr.ProcessLabel())
if err != nil {
return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
}
labelOpts = append(labelOpts, secopts...)
}
for _, opt := range securityOpts {
if opt == "no-new-privileges" {
config.NoNewPrivs = true
} else {
con := strings.SplitN(opt, "=", 2)
if len(con) != 2 {
return fmt.Errorf("invalid --security-opt 1: %q", opt)
}
switch con[0] {
case "label":
labelOpts = append(labelOpts, con[1])
case "apparmor":
config.ApparmorProfile = con[1]
case "seccomp":
config.SeccompProfilePath = con[1]
default:
return fmt.Errorf("invalid --security-opt 2: %q", opt)
}
}
}
if config.SeccompProfilePath == "" {
var err error
config.SeccompProfilePath, err = libpod.DefaultSeccompPath()
if err != nil {
return err
}
}
config.LabelOpts = labelOpts
return nil
}
func configureEntrypoint(c *GenericCLIResults, data *inspect.ImageData) []string { func configureEntrypoint(c *GenericCLIResults, data *inspect.ImageData) []string {
entrypoint := []string{} entrypoint := []string{}
if c.IsSet("entrypoint") { if c.IsSet("entrypoint") {
@ -348,11 +281,6 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
rootfs = c.InputArgs[0] rootfs = c.InputArgs[0]
} }
sysctl, err := validateSysctl(c.StringSlice("sysctl"))
if err != nil {
return nil, errors.Wrapf(err, "invalid value for sysctl")
}
if c.String("memory") != "" { if c.String("memory") != "" {
memoryLimit, err = units.RAMInBytes(c.String("memory")) memoryLimit, err = units.RAMInBytes(c.String("memory"))
if err != nil { if err != nil {
@ -691,61 +619,96 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
pidsLimit = 0 pidsLimit = 0
} }
pid := &cc.PidConfig{
PidMode: pidMode,
}
ipc := &cc.IpcConfig{
IpcMode: ipcMode,
}
cgroup := &cc.CgroupConfig{
Cgroups: c.String("cgroups"),
Cgroupns: c.String("cgroupns"),
CgroupParent: c.String("cgroup-parent"),
CgroupMode: cgroupMode,
}
userns := &cc.UserConfig{
GroupAdd: c.StringSlice("group-add"),
IDMappings: idmappings,
UsernsMode: usernsMode,
User: user,
}
uts := &cc.UtsConfig{
UtsMode: utsMode,
NoHosts: c.Bool("no-hosts"),
HostAdd: c.StringSlice("add-host"),
Hostname: c.String("hostname"),
}
net := &cc.NetworkConfig{
DNSOpt: c.StringSlice("dns-opt"),
DNSSearch: c.StringSlice("dns-search"),
DNSServers: c.StringSlice("dns"),
HTTPProxy: c.Bool("http-proxy"),
MacAddress: c.String("mac-address"),
Network: network,
NetMode: netMode,
IPAddress: c.String("ip"),
Publish: c.StringSlice("publish"),
PublishAll: c.Bool("publish-all"),
PortBindings: portBindings,
}
sysctl, err := validateSysctl(c.StringSlice("sysctl"))
if err != nil {
return nil, errors.Wrapf(err, "invalid value for sysctl")
}
secConfig := &cc.SecurityConfig{
CapAdd: c.StringSlice("cap-add"),
CapDrop: c.StringSlice("cap-drop"),
Privileged: c.Bool("privileged"),
ReadOnlyRootfs: c.Bool("read-only"),
ReadOnlyTmpfs: c.Bool("read-only-tmpfs"),
Sysctl: sysctl,
}
if err := secConfig.SetLabelOpts(runtime, pid, ipc); err != nil {
return nil, err
}
if err := secConfig.SetSecurityOpts(runtime, c.StringArray("security-opt")); err != nil {
return nil, err
}
config := &cc.CreateConfig{ config := &cc.CreateConfig{
Annotations: annotations, Annotations: annotations,
BuiltinImgVolumes: ImageVolumes, BuiltinImgVolumes: ImageVolumes,
ConmonPidFile: c.String("conmon-pidfile"), ConmonPidFile: c.String("conmon-pidfile"),
ImageVolumeType: c.String("image-volume"), ImageVolumeType: c.String("image-volume"),
CapAdd: c.StringSlice("cap-add"),
CapDrop: c.StringSlice("cap-drop"),
CidFile: c.String("cidfile"), CidFile: c.String("cidfile"),
Cgroupns: c.String("cgroupns"),
Cgroups: c.String("cgroups"),
CgroupParent: c.String("cgroup-parent"),
Command: command, Command: command,
UserCommand: userCommand, UserCommand: userCommand,
Detach: c.Bool("detach"), Detach: c.Bool("detach"),
Devices: c.StringSlice("device"), Devices: c.StringSlice("device"),
DNSOpt: c.StringSlice("dns-opt"),
DNSSearch: c.StringSlice("dns-search"),
DNSServers: c.StringSlice("dns"),
Entrypoint: entrypoint, Entrypoint: entrypoint,
Env: env, Env: env,
// ExposedPorts: ports, // ExposedPorts: ports,
GroupAdd: c.StringSlice("group-add"),
Hostname: c.String("hostname"),
HostAdd: c.StringSlice("add-host"),
HTTPProxy: c.Bool("http-proxy"),
NoHosts: c.Bool("no-hosts"),
IDMappings: idmappings,
Init: c.Bool("init"), Init: c.Bool("init"),
InitPath: c.String("init-path"), InitPath: c.String("init-path"),
Image: imageName, Image: imageName,
ImageID: imageID, ImageID: imageID,
Interactive: c.Bool("interactive"), Interactive: c.Bool("interactive"),
// IP6Address: c.String("ipv6"), // Not implemented yet - needs CNI support for static v6 // IP6Address: c.String("ipv6"), // Not implemented yet - needs CNI support for static v6
IPAddress: c.String("ip"), Labels: labels,
Labels: labels,
// LinkLocalIP: c.StringSlice("link-local-ip"), // Not implemented yet // LinkLocalIP: c.StringSlice("link-local-ip"), // Not implemented yet
LogDriver: logDriver, LogDriver: logDriver,
LogDriverOpt: c.StringSlice("log-opt"), LogDriverOpt: c.StringSlice("log-opt"),
MacAddress: c.String("mac-address"),
Name: c.String("name"), Name: c.String("name"),
Network: network,
// NetworkAlias: c.StringSlice("network-alias"), // Not implemented - does this make sense in Podman? // NetworkAlias: c.StringSlice("network-alias"), // Not implemented - does this make sense in Podman?
IpcMode: ipcMode, Pod: podName,
NetMode: netMode, Quiet: c.Bool("quiet"),
UtsMode: utsMode,
PidMode: pidMode,
CgroupMode: cgroupMode,
Pod: podName,
Privileged: c.Bool("privileged"),
Publish: c.StringSlice("publish"),
PublishAll: c.Bool("publish-all"),
PortBindings: portBindings,
Quiet: c.Bool("quiet"),
ReadOnlyRootfs: c.Bool("read-only"),
ReadOnlyTmpfs: c.Bool("read-only-tmpfs"),
Resources: cc.CreateResourceConfig{ Resources: cc.CreateResourceConfig{
BlkioWeight: blkioWeight, BlkioWeight: blkioWeight,
BlkioWeightDevice: c.StringSlice("blkio-weight-device"), BlkioWeightDevice: c.StringSlice("blkio-weight-device"),
@ -774,30 +737,27 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}, },
RestartPolicy: c.String("restart"), RestartPolicy: c.String("restart"),
Rm: c.Bool("rm"), Rm: c.Bool("rm"),
Security: *secConfig,
StopSignal: stopSignal, StopSignal: stopSignal,
StopTimeout: c.Uint("stop-timeout"), StopTimeout: c.Uint("stop-timeout"),
Sysctl: sysctl,
Systemd: systemd, Systemd: systemd,
Tmpfs: c.StringArray("tmpfs"), Tmpfs: c.StringArray("tmpfs"),
Tty: tty, Tty: tty,
User: user,
UsernsMode: usernsMode,
MountsFlag: c.StringArray("mount"), MountsFlag: c.StringArray("mount"),
Volumes: c.StringArray("volume"), Volumes: c.StringArray("volume"),
WorkDir: workDir, WorkDir: workDir,
Rootfs: rootfs, Rootfs: rootfs,
VolumesFrom: c.StringSlice("volumes-from"), VolumesFrom: c.StringSlice("volumes-from"),
Syslog: c.Bool("syslog"), Syslog: c.Bool("syslog"),
Pid: *pid,
Ipc: *ipc,
Cgroup: *cgroup,
User: *userns,
Uts: *uts,
Network: *net,
} }
if config.Privileged {
config.LabelOpts = label.DisableSecOpt()
} else {
if err := parseSecurityOpt(config, c.StringArray("security-opt"), runtime); err != nil {
return nil, err
}
}
config.SecurityOpts = c.StringArray("security-opt")
warnings, err := verifyContainerResources(config, false) warnings, err := verifyContainerResources(config, false)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -666,55 +666,55 @@ func getPodPorts(containers []v1.Container) []ocicni.PortMapping {
return infraPorts return infraPorts
} }
func setupSecurityContext(containerConfig *createconfig.CreateConfig, containerYAML v1.Container) { func setupSecurityContext(securityConfig *createconfig.SecurityConfig, userConfig *createconfig.UserConfig, containerYAML v1.Container) {
if containerYAML.SecurityContext == nil { if containerYAML.SecurityContext == nil {
return return
} }
if containerYAML.SecurityContext.ReadOnlyRootFilesystem != nil { if containerYAML.SecurityContext.ReadOnlyRootFilesystem != nil {
containerConfig.ReadOnlyRootfs = *containerYAML.SecurityContext.ReadOnlyRootFilesystem securityConfig.ReadOnlyRootfs = *containerYAML.SecurityContext.ReadOnlyRootFilesystem
} }
if containerYAML.SecurityContext.Privileged != nil { if containerYAML.SecurityContext.Privileged != nil {
containerConfig.Privileged = *containerYAML.SecurityContext.Privileged securityConfig.Privileged = *containerYAML.SecurityContext.Privileged
} }
if containerYAML.SecurityContext.AllowPrivilegeEscalation != nil { if containerYAML.SecurityContext.AllowPrivilegeEscalation != nil {
containerConfig.NoNewPrivs = !*containerYAML.SecurityContext.AllowPrivilegeEscalation securityConfig.NoNewPrivs = !*containerYAML.SecurityContext.AllowPrivilegeEscalation
} }
if seopt := containerYAML.SecurityContext.SELinuxOptions; seopt != nil { if seopt := containerYAML.SecurityContext.SELinuxOptions; seopt != nil {
if seopt.User != "" { if seopt.User != "" {
containerConfig.SecurityOpts = append(containerConfig.SecurityOpts, fmt.Sprintf("label=user:%s", seopt.User)) securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=user:%s", seopt.User))
containerConfig.LabelOpts = append(containerConfig.LabelOpts, fmt.Sprintf("user:%s", seopt.User)) securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("user:%s", seopt.User))
} }
if seopt.Role != "" { if seopt.Role != "" {
containerConfig.SecurityOpts = append(containerConfig.SecurityOpts, fmt.Sprintf("label=role:%s", seopt.Role)) securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=role:%s", seopt.Role))
containerConfig.LabelOpts = append(containerConfig.LabelOpts, fmt.Sprintf("role:%s", seopt.Role)) securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("role:%s", seopt.Role))
} }
if seopt.Type != "" { if seopt.Type != "" {
containerConfig.SecurityOpts = append(containerConfig.SecurityOpts, fmt.Sprintf("label=type:%s", seopt.Type)) securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=type:%s", seopt.Type))
containerConfig.LabelOpts = append(containerConfig.LabelOpts, fmt.Sprintf("type:%s", seopt.Type)) securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("type:%s", seopt.Type))
} }
if seopt.Level != "" { if seopt.Level != "" {
containerConfig.SecurityOpts = append(containerConfig.SecurityOpts, fmt.Sprintf("label=level:%s", seopt.Level)) securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=level:%s", seopt.Level))
containerConfig.LabelOpts = append(containerConfig.LabelOpts, fmt.Sprintf("level:%s", seopt.Level)) securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("level:%s", seopt.Level))
} }
} }
if caps := containerYAML.SecurityContext.Capabilities; caps != nil { if caps := containerYAML.SecurityContext.Capabilities; caps != nil {
for _, capability := range caps.Add { for _, capability := range caps.Add {
containerConfig.CapAdd = append(containerConfig.CapAdd, string(capability)) securityConfig.CapAdd = append(securityConfig.CapAdd, string(capability))
} }
for _, capability := range caps.Drop { for _, capability := range caps.Drop {
containerConfig.CapDrop = append(containerConfig.CapDrop, string(capability)) securityConfig.CapDrop = append(securityConfig.CapDrop, string(capability))
} }
} }
if containerYAML.SecurityContext.RunAsUser != nil { if containerYAML.SecurityContext.RunAsUser != nil {
containerConfig.User = fmt.Sprintf("%d", *containerYAML.SecurityContext.RunAsUser) userConfig.User = fmt.Sprintf("%d", *containerYAML.SecurityContext.RunAsUser)
} }
if containerYAML.SecurityContext.RunAsGroup != nil { if containerYAML.SecurityContext.RunAsGroup != nil {
if containerConfig.User == "" { if userConfig.User == "" {
containerConfig.User = "0" userConfig.User = "0"
} }
containerConfig.User = fmt.Sprintf("%s:%d", containerConfig.User, *containerYAML.SecurityContext.RunAsGroup) userConfig.User = fmt.Sprintf("%s:%d", userConfig.User, *containerYAML.SecurityContext.RunAsGroup)
} }
} }
@ -722,6 +722,13 @@ func setupSecurityContext(containerConfig *createconfig.CreateConfig, containerY
func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, runtime *libpod.Runtime, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID string) (*createconfig.CreateConfig, error) { func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, runtime *libpod.Runtime, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID string) (*createconfig.CreateConfig, error) {
var ( var (
containerConfig createconfig.CreateConfig containerConfig createconfig.CreateConfig
pidConfig createconfig.PidConfig
networkConfig createconfig.NetworkConfig
cgroupConfig createconfig.CgroupConfig
utsConfig createconfig.UtsConfig
ipcConfig createconfig.IpcConfig
userConfig createconfig.UserConfig
securityConfig createconfig.SecurityConfig
) )
// The default for MemorySwappiness is -1, not 0 // The default for MemorySwappiness is -1, not 0
@ -737,15 +744,15 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container
imageData, _ := newImage.Inspect(ctx) imageData, _ := newImage.Inspect(ctx)
containerConfig.User = "0" userConfig.User = "0"
if imageData != nil { if imageData != nil {
containerConfig.User = imageData.Config.User userConfig.User = imageData.Config.User
} }
setupSecurityContext(&containerConfig, containerYAML) setupSecurityContext(&securityConfig, &userConfig, containerYAML)
var err error var err error
containerConfig.SeccompProfilePath, err = libpod.DefaultSeccompPath() containerConfig.Security.SeccompProfilePath, err = libpod.DefaultSeccompPath()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -768,20 +775,28 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container
containerConfig.StopSignal = 15 containerConfig.StopSignal = 15
// If the user does not pass in ID mappings, just set to basics // If the user does not pass in ID mappings, just set to basics
if containerConfig.IDMappings == nil { if userConfig.IDMappings == nil {
containerConfig.IDMappings = &storage.IDMappingOptions{} userConfig.IDMappings = &storage.IDMappingOptions{}
} }
containerConfig.NetMode = ns.NetworkMode(namespaces["net"]) networkConfig.NetMode = ns.NetworkMode(namespaces["net"])
containerConfig.IpcMode = ns.IpcMode(namespaces["ipc"]) ipcConfig.IpcMode = ns.IpcMode(namespaces["ipc"])
containerConfig.UtsMode = ns.UTSMode(namespaces["uts"]) utsConfig.UtsMode = ns.UTSMode(namespaces["uts"])
// disabled in code review per mheon // disabled in code review per mheon
//containerConfig.PidMode = ns.PidMode(namespaces["pid"]) //containerConfig.PidMode = ns.PidMode(namespaces["pid"])
containerConfig.UsernsMode = ns.UsernsMode(namespaces["user"]) userConfig.UsernsMode = ns.UsernsMode(namespaces["user"])
if len(containerConfig.WorkDir) == 0 { if len(containerConfig.WorkDir) == 0 {
containerConfig.WorkDir = "/" containerConfig.WorkDir = "/"
} }
containerConfig.Pid = pidConfig
containerConfig.Network = networkConfig
containerConfig.Uts = utsConfig
containerConfig.Ipc = ipcConfig
containerConfig.Cgroup = cgroupConfig
containerConfig.User = userConfig
containerConfig.Security = securityConfig
// Set default environment variables and incorporate data from image, if necessary // Set default environment variables and incorporate data from image, if necessary
envs := shared.EnvVariablesFromData(imageData) envs := shared.EnvVariablesFromData(imageData)

View File

@ -10,7 +10,7 @@ import (
seccomp "github.com/seccomp/containers-golang" seccomp "github.com/seccomp/containers-golang"
) )
func getSeccompConfig(config *CreateConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
var seccompConfig *spec.LinuxSeccomp var seccompConfig *spec.LinuxSeccomp
var err error var err error

View File

@ -6,6 +6,6 @@ import (
spec "github.com/opencontainers/runtime-spec/specs-go" spec "github.com/opencontainers/runtime-spec/specs-go"
) )
func getSeccompConfig(config *CreateConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
return nil, nil return nil, nil
} }

View File

@ -8,7 +8,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
func getSeccompConfig(config *CreateConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
return nil, errors.New("function not supported on non-linux OS's") return nil, errors.New("function not supported on non-linux OS's")
} }
func addDevice(g *generate.Generator, device string) error { func addDevice(g *generate.Generator, device string) error {

View File

@ -1,7 +1,6 @@
package createconfig package createconfig
import ( import (
"net"
"os" "os"
"strconv" "strconv"
"strings" "strings"
@ -12,7 +11,6 @@ import (
"github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/namespaces" "github.com/containers/libpod/pkg/namespaces"
"github.com/containers/storage" "github.com/containers/storage"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
spec "github.com/opencontainers/runtime-spec/specs-go" spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/runtime-tools/generate"
@ -55,89 +53,126 @@ type CreateResourceConfig struct {
Ulimit []string //ulimit Ulimit []string //ulimit
} }
// CreateConfig is a pre OCI spec structure. It represents user input from varlink or the CLI // PidConfig configures the pid namespace for the container
type CreateConfig struct { type PidConfig struct {
Annotations map[string]string PidMode namespaces.PidMode //pid
Args []string }
// 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
}
// SecurityConfig configures the security features for the container
type SecurityConfig struct {
CapAdd []string // cap-add CapAdd []string // cap-add
CapDrop []string // cap-drop CapDrop []string // cap-drop
CidFile string
ConmonPidFile string
Cgroupns string
Cgroups string
CgroupParent string // cgroup-parent
Command []string // Full command that will be used
UserCommand []string // User-entered command (or image CMD)
Detach bool // detach
Devices []string // device
DNSOpt []string //dns-opt
DNSSearch []string //dns-search
DNSServers []string //dns
Entrypoint []string //entrypoint
Env map[string]string //env
ExposedPorts map[nat.Port]struct{}
GroupAdd []string // group-add
HealthCheck *manifest.Schema2HealthConfig
NoHosts bool
HostAdd []string //add-host
Hostname string //hostname
HTTPProxy bool
Init bool // init
InitPath string //init-path
Image string
ImageID string
BuiltinImgVolumes map[string]struct{} // volumes defined in the image config
IDMappings *storage.IDMappingOptions
ImageVolumeType string // how to handle the image volume, either bind, tmpfs, or ignore
Interactive bool //interactive
IpcMode namespaces.IpcMode //ipc
IP6Address string //ipv6
IPAddress string //ip
Labels map[string]string //label
LinkLocalIP []string // link-local-ip
LogDriver string // log-driver
LogDriverOpt []string // log-opt
MacAddress string //mac-address
Name string //name
NetMode namespaces.NetworkMode //net
Network string //network
NetworkAlias []string //network-alias
PidMode namespaces.PidMode //pid
Pod string //pod
PodmanPath string
CgroupMode namespaces.CgroupMode //cgroup
PortBindings nat.PortMap
Privileged bool //privileged
Publish []string //publish
PublishAll bool //publish-all
Quiet bool //quiet
ReadOnlyRootfs bool //read-only
ReadOnlyTmpfs bool //read-only-tmpfs
Resources CreateResourceConfig
RestartPolicy string
Rm bool //rm
StopSignal syscall.Signal // stop-signal
StopTimeout uint // stop-timeout
Sysctl map[string]string //sysctl
Systemd bool
Tmpfs []string // tmpfs
Tty bool //tty
UsernsMode namespaces.UsernsMode //userns
User string //user
UtsMode namespaces.UTSMode //uts
Mounts []spec.Mount
MountsFlag []string // mounts
NamedVolumes []*libpod.ContainerNamedVolume
Volumes []string //volume
VolumesFrom []string
WorkDir string //workdir
LabelOpts []string //SecurityOpts LabelOpts []string //SecurityOpts
NoNewPrivs bool //SecurityOpts NoNewPrivs bool //SecurityOpts
ApparmorProfile string //SecurityOpts ApparmorProfile string //SecurityOpts
SeccompProfilePath string //SecurityOpts SeccompProfilePath string //SecurityOpts
SecurityOpts []string SecurityOpts []string
Rootfs string Privileged bool //privileged
Syslog bool // Whether to enable syslog on exit commands 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 u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
@ -199,7 +234,6 @@ func (c *CreateConfig) createExitCommand(runtime *libpod.Runtime) ([]string, err
// GetContainerCreateOptions takes a CreateConfig and returns a slice of CtrCreateOptions // 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) { func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod, mounts []spec.Mount, namedVolumes []*libpod.ContainerNamedVolume) ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption var options []libpod.CtrCreateOption
var portBindings []ocicni.PortMapping
var err error var err error
if c.Interactive { if c.Interactive {
@ -216,15 +250,6 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
logrus.Debugf("adding container to pod %s", c.Pod) logrus.Debugf("adding container to pod %s", c.Pod)
options = append(options, runtime.WithPod(pod)) options = append(options, runtime.WithPod(pod))
} }
if c.Cgroups == "disabled" {
options = append(options, libpod.WithNoCgroups())
}
if len(c.PortBindings) > 0 {
portBindings, err = c.CreatePortBindings()
if err != nil {
return nil, errors.Wrapf(err, "unable to create port bindings")
}
}
if len(mounts) != 0 || len(namedVolumes) != 0 { if len(mounts) != 0 || len(namedVolumes) != 0 {
destinations := []string{} destinations := []string{}
@ -253,130 +278,10 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
// does not have one // does not have one
options = append(options, libpod.WithEntrypoint(c.Entrypoint)) options = append(options, libpod.WithEntrypoint(c.Entrypoint))
networks := make([]string, 0)
userNetworks := c.NetMode.UserDefined()
if IsPod(userNetworks) {
userNetworks = ""
}
if userNetworks != "" {
for _, netName := range strings.Split(userNetworks, ",") {
if netName == "" {
return nil, errors.Wrapf(err, "container networks %q invalid", networks)
}
networks = append(networks, netName)
}
}
if c.NetMode.IsNS() {
ns := c.NetMode.NS()
if ns == "" {
return nil, errors.Errorf("invalid empty user-defined network namespace")
}
_, err := os.Stat(ns)
if err != nil {
return nil, err
}
} else if c.NetMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.NetMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.NetMode.Container())
}
options = append(options, libpod.WithNetNSFrom(connectedCtr))
} else if !c.NetMode.IsHost() && !c.NetMode.IsNone() {
hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost()
options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
}
if c.CgroupMode.IsNS() {
ns := c.CgroupMode.NS()
if ns == "" {
return nil, errors.Errorf("invalid empty user-defined network namespace")
}
_, err := os.Stat(ns)
if err != nil {
return nil, err
}
} else if c.CgroupMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.CgroupMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.CgroupMode.Container())
}
options = append(options, libpod.WithCgroupNSFrom(connectedCtr))
}
if c.UsernsMode.IsNS() {
ns := c.UsernsMode.NS()
if ns == "" {
return nil, errors.Errorf("invalid empty user-defined user namespace")
}
_, err := os.Stat(ns)
if err != nil {
return nil, err
}
options = append(options, libpod.WithIDMappings(*c.IDMappings))
} else if c.UsernsMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container())
}
options = append(options, libpod.WithUserNSFrom(connectedCtr))
} else {
options = append(options, libpod.WithIDMappings(*c.IDMappings))
}
if c.PidMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.PidMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.PidMode.Container())
}
options = append(options, libpod.WithPIDNSFrom(connectedCtr))
}
if c.IpcMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.IpcMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container())
}
options = append(options, libpod.WithIPCNSFrom(connectedCtr))
}
if IsPod(string(c.UtsMode)) {
options = append(options, libpod.WithUTSNSFromPod(pod))
}
if c.UtsMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.UtsMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.UtsMode.Container())
}
options = append(options, libpod.WithUTSNSFrom(connectedCtr))
}
// TODO: MNT, USER, CGROUP // TODO: MNT, USER, CGROUP
options = append(options, libpod.WithStopSignal(c.StopSignal)) options = append(options, libpod.WithStopSignal(c.StopSignal))
options = append(options, libpod.WithStopTimeout(c.StopTimeout)) options = append(options, libpod.WithStopTimeout(c.StopTimeout))
if len(c.DNSSearch) > 0 {
options = append(options, libpod.WithDNSSearch(c.DNSSearch))
}
if len(c.DNSServers) > 0 {
if len(c.DNSServers) == 1 && strings.ToLower(c.DNSServers[0]) == "none" {
options = append(options, libpod.WithUseImageResolvConf())
} else {
options = append(options, libpod.WithDNS(c.DNSServers))
}
}
if len(c.DNSOpt) > 0 {
options = append(options, libpod.WithDNSOption(c.DNSOpt))
}
if c.NoHosts {
options = append(options, libpod.WithUseImageHosts())
}
if len(c.HostAdd) > 0 && !c.NoHosts {
options = append(options, libpod.WithHosts(c.HostAdd))
}
logPath := getLoggingPath(c.LogDriverOpt) logPath := getLoggingPath(c.LogDriverOpt)
if logPath != "" { if logPath != "" {
options = append(options, libpod.WithLogPath(logPath)) options = append(options, libpod.WithLogPath(logPath))
@ -386,54 +291,59 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
options = append(options, libpod.WithLogDriver(c.LogDriver)) options = append(options, libpod.WithLogDriver(c.LogDriver))
} }
if c.IPAddress != "" { secOpts, err := c.Security.ToCreateOptions()
ip := net.ParseIP(c.IPAddress) if err != nil {
if ip == nil { return nil, err
return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress)
} else if ip.To4() == nil {
return nil, errors.Wrapf(define.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress)
}
options = append(options, libpod.WithStaticIP(ip))
} }
options = append(options, secOpts...)
if c.MacAddress != "" { nsOpts, err := c.Cgroup.ToCreateOptions(runtime)
mac, err := net.ParseMAC(c.MacAddress) if err != nil {
if err != nil { return nil, err
return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err)
}
options = append(options, libpod.WithStaticMAC(mac))
} }
options = append(options, nsOpts...)
options = append(options, libpod.WithPrivileged(c.Privileged)) 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 useImageVolumes := c.ImageVolumeType == TypeBind
// Gather up the options for NewContainer which consist of With... funcs // 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.WithRootFSFromImage(c.ImageID, c.Image, useImageVolumes))
options = append(options, libpod.WithSecLabels(c.LabelOpts))
options = append(options, libpod.WithConmonPidFile(c.ConmonPidFile)) options = append(options, libpod.WithConmonPidFile(c.ConmonPidFile))
options = append(options, libpod.WithLabels(c.Labels)) options = append(options, libpod.WithLabels(c.Labels))
options = append(options, libpod.WithUser(c.User))
if c.IpcMode.IsHost() {
options = append(options, libpod.WithShmDir("/dev/shm"))
} else if c.IpcMode.IsContainer() {
ctr, err := runtime.LookupContainer(c.IpcMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container())
}
options = append(options, libpod.WithShmDir(ctr.ShmDir()))
}
options = append(options, libpod.WithShmSize(c.Resources.ShmSize)) options = append(options, libpod.WithShmSize(c.Resources.ShmSize))
options = append(options, libpod.WithGroups(c.GroupAdd))
if c.Rootfs != "" { if c.Rootfs != "" {
options = append(options, libpod.WithRootFS(c.Rootfs)) options = append(options, libpod.WithRootFS(c.Rootfs))
} }
// Default used if not overridden on command line // Default used if not overridden on command line
if c.CgroupParent != "" {
options = append(options, libpod.WithCgroupParent(c.CgroupParent))
}
if c.RestartPolicy != "" { if c.RestartPolicy != "" {
if c.RestartPolicy == "unless-stopped" { if c.RestartPolicy == "unless-stopped" {
return nil, errors.Wrapf(define.ErrInvalidArg, "the unless-stopped restart policy is not supported") return nil, errors.Wrapf(define.ErrInvalidArg, "the unless-stopped restart policy is not supported")
@ -467,38 +377,6 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
return options, nil return options, nil
} }
// CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands
func (c *CreateConfig) CreatePortBindings() ([]ocicni.PortMapping, error) {
return NatToOCIPortBindings(c.PortBindings)
}
// NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice
func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) {
var portBindings []ocicni.PortMapping
for containerPb, hostPb := range ports {
var pm ocicni.PortMapping
pm.ContainerPort = int32(containerPb.Int())
for _, i := range hostPb {
var hostPort int
var err error
pm.HostIP = i.HostIP
if i.HostPort == "" {
hostPort = containerPb.Int()
} else {
hostPort, err = strconv.Atoi(i.HostPort)
if err != nil {
return nil, errors.Wrapf(err, "unable to convert host port to integer")
}
}
pm.HostPort = int32(hostPort)
pm.Protocol = containerPb.Proto()
portBindings = append(portBindings, pm)
}
}
return portBindings, nil
}
// AddPrivilegedDevices iterates through host devices and adds all // AddPrivilegedDevices iterates through host devices and adds all
// host devices to the spec // host devices to the spec
func (c *CreateConfig) AddPrivilegedDevices(g *generate.Generator) error { func (c *CreateConfig) AddPrivilegedDevices(g *generate.Generator) error {

433
pkg/spec/namespaces.go Normal file
View File

@ -0,0 +1,433 @@
package createconfig
import (
"net"
"os"
"strconv"
"strings"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/cgroups"
"github.com/cri-o/ocicni/pkg/ocicni"
"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"
)
func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) {
var portBindings []ocicni.PortMapping
var err error
if len(c.PortBindings) > 0 {
portBindings, err = NatToOCIPortBindings(c.PortBindings)
if err != nil {
return nil, errors.Wrapf(err, "unable to create port bindings")
}
}
options := make([]libpod.CtrCreateOption, 0)
userNetworks := c.NetMode.UserDefined()
networks := make([]string, 0)
if IsPod(userNetworks) {
userNetworks = ""
}
if userNetworks != "" {
for _, netName := range strings.Split(userNetworks, ",") {
if netName == "" {
return nil, errors.Errorf("container networks %q invalid", userNetworks)
}
networks = append(networks, netName)
}
}
if c.NetMode.IsNS() {
ns := c.NetMode.NS()
if ns == "" {
return nil, errors.Errorf("invalid empty user-defined network namespace")
}
_, err := os.Stat(ns)
if err != nil {
return nil, err
}
} else if c.NetMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.NetMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.NetMode.Container())
}
options = append(options, libpod.WithNetNSFrom(connectedCtr))
} else if !c.NetMode.IsHost() && !c.NetMode.IsNone() {
postConfigureNetNS := userns.getPostConfigureNetNS()
options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
}
if len(c.DNSSearch) > 0 {
options = append(options, libpod.WithDNSSearch(c.DNSSearch))
}
if len(c.DNSServers) > 0 {
if len(c.DNSServers) == 1 && strings.ToLower(c.DNSServers[0]) == "none" {
options = append(options, libpod.WithUseImageResolvConf())
} else {
options = append(options, libpod.WithDNS(c.DNSServers))
}
}
if len(c.DNSOpt) > 0 {
options = append(options, libpod.WithDNSOption(c.DNSOpt))
}
if c.IPAddress != "" {
ip := net.ParseIP(c.IPAddress)
if ip == nil {
return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress)
} else if ip.To4() == nil {
return nil, errors.Wrapf(define.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress)
}
options = append(options, libpod.WithStaticIP(ip))
}
if c.MacAddress != "" {
mac, err := net.ParseMAC(c.MacAddress)
if err != nil {
return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err)
}
options = append(options, libpod.WithStaticMAC(mac))
}
return options, nil
}
func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error {
netMode := c.NetMode
if netMode.IsHost() {
logrus.Debug("Using host netmode")
if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil {
return err
}
} else if netMode.IsNone() {
logrus.Debug("Using none netmode")
} else if netMode.IsBridge() {
logrus.Debug("Using bridge netmode")
} else if netCtr := netMode.Container(); netCtr != "" {
logrus.Debugf("using container %s netmode", netCtr)
} else if IsNS(string(netMode)) {
logrus.Debug("Using ns netmode")
if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), NS(string(netMode))); err != nil {
return err
}
} else if IsPod(string(netMode)) {
logrus.Debug("Using pod netmode, unless pod is not sharing")
} else if netMode.IsSlirp4netns() {
logrus.Debug("Using slirp4netns netmode")
} else if netMode.IsUserDefined() {
logrus.Debug("Using user defined netmode")
} else {
return errors.Errorf("unknown network mode")
}
if c.HTTPProxy {
for _, envSpec := range []string{
"http_proxy",
"HTTP_PROXY",
"https_proxy",
"HTTPS_PROXY",
"ftp_proxy",
"FTP_PROXY",
"no_proxy",
"NO_PROXY",
} {
envVal := os.Getenv(envSpec)
if envVal != "" {
g.AddProcessEnv(envSpec, envVal)
}
}
}
if g.Config.Annotations == nil {
g.Config.Annotations = make(map[string]string)
}
if c.PublishAll {
g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
} else {
g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
}
return nil
}
// NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice
func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) {
var portBindings []ocicni.PortMapping
for containerPb, hostPb := range ports {
var pm ocicni.PortMapping
pm.ContainerPort = int32(containerPb.Int())
for _, i := range hostPb {
var hostPort int
var err error
pm.HostIP = i.HostIP
if i.HostPort == "" {
hostPort = containerPb.Int()
} else {
hostPort, err = strconv.Atoi(i.HostPort)
if err != nil {
return nil, errors.Wrapf(err, "unable to convert host port to integer")
}
}
pm.HostPort = int32(hostPort)
pm.Protocol = containerPb.Proto()
portBindings = append(portBindings, pm)
}
}
return portBindings, nil
}
func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if c.CgroupMode.IsNS() {
ns := c.CgroupMode.NS()
if ns == "" {
return nil, errors.Errorf("invalid empty user-defined network namespace")
}
_, err := os.Stat(ns)
if err != nil {
return nil, err
}
} else if c.CgroupMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.CgroupMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.CgroupMode.Container())
}
options = append(options, libpod.WithCgroupNSFrom(connectedCtr))
}
if c.CgroupParent != "" {
options = append(options, libpod.WithCgroupParent(c.CgroupParent))
}
if c.Cgroups == "disabled" {
options = append(options, libpod.WithNoCgroups())
}
return options, nil
}
func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if c.UsernsMode.IsNS() {
ns := c.UsernsMode.NS()
if ns == "" {
return nil, errors.Errorf("invalid empty user-defined user namespace")
}
_, err := os.Stat(ns)
if err != nil {
return nil, err
}
options = append(options, libpod.WithIDMappings(*c.IDMappings))
} else if c.UsernsMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container())
}
options = append(options, libpod.WithUserNSFrom(connectedCtr))
} else {
options = append(options, libpod.WithIDMappings(*c.IDMappings))
}
options = append(options, libpod.WithUser(c.User))
options = append(options, libpod.WithGroups(c.GroupAdd))
return options, nil
}
func (c *UserConfig) ConfigureGenerator(g *generate.Generator) error {
if IsNS(string(c.UsernsMode)) {
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), NS(string(c.UsernsMode))); err != nil {
return err
}
// runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping
g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
}
if (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost() {
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
return err
}
}
for _, uidmap := range c.IDMappings.UIDMap {
g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
}
for _, gidmap := range c.IDMappings.GIDMap {
g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
}
return nil
}
func (c *UserConfig) getPostConfigureNetNS() bool {
hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost()
return postConfigureNetNS
}
func (c *UserConfig) InNS(isRootless bool) bool {
hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
return isRootless || (hasUserns && !c.UsernsMode.IsHost())
}
func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if c.IpcMode.IsHost() {
options = append(options, libpod.WithShmDir("/dev/shm"))
} else if c.IpcMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.IpcMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container())
}
options = append(options, libpod.WithIPCNSFrom(connectedCtr))
options = append(options, libpod.WithShmDir(connectedCtr.ShmDir()))
}
return options, nil
}
func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error {
ipcMode := c.IpcMode
if IsNS(string(ipcMode)) {
return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), NS(string(ipcMode)))
}
if ipcMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.IPCNamespace))
}
if ipcCtr := ipcMode.Container(); ipcCtr != "" {
logrus.Debugf("Using container %s ipcmode", ipcCtr)
}
return nil
}
func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error {
cgroupMode := c.CgroupMode
if cgroupMode.IsDefaultValue() {
// If the value is not specified, default to "private" on cgroups v2 and "host" on cgroups v1.
unified, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return err
}
if unified {
cgroupMode = "private"
} else {
cgroupMode = "host"
}
}
if cgroupMode.IsNS() {
return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), NS(string(cgroupMode)))
}
if cgroupMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.CgroupNamespace))
}
if cgroupMode.IsPrivate() {
return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "")
}
if cgCtr := cgroupMode.Container(); cgCtr != "" {
logrus.Debugf("Using container %s cgroup mode", cgCtr)
}
return nil
}
func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if c.PidMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.PidMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.PidMode.Container())
}
options = append(options, libpod.WithPIDNSFrom(connectedCtr))
}
return options, nil
}
func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error {
pidMode := c.PidMode
if IsNS(string(pidMode)) {
return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), NS(string(pidMode)))
}
if pidMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
}
if pidCtr := pidMode.Container(); pidCtr != "" {
logrus.Debugf("using container %s pidmode", pidCtr)
}
if IsPod(string(pidMode)) {
logrus.Debug("using pod pidmode")
}
return nil
}
func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if IsPod(string(c.UtsMode)) {
options = append(options, libpod.WithUTSNSFromPod(pod))
}
if c.UtsMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.UtsMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.UtsMode.Container())
}
options = append(options, libpod.WithUTSNSFrom(connectedCtr))
}
if c.NoHosts {
options = append(options, libpod.WithUseImageHosts())
}
if len(c.HostAdd) > 0 && !c.NoHosts {
options = append(options, libpod.WithHosts(c.HostAdd))
}
return options, nil
}
func (c *UtsConfig) ConfigureGenerator(g *generate.Generator, net *NetworkConfig, runtime *libpod.Runtime) error {
hostname := c.Hostname
var err error
if hostname == "" {
if utsCtrID := c.UtsMode.Container(); utsCtrID != "" {
utsCtr, err := runtime.GetContainer(utsCtrID)
if err != nil {
return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID)
}
hostname = utsCtr.Hostname()
} else if net.NetMode.IsHost() || c.UtsMode.IsHost() {
hostname, err = os.Hostname()
if err != nil {
return errors.Wrap(err, "unable to retrieve hostname of the host")
}
} else {
logrus.Debug("No hostname set; container's hostname will default to runtime default")
}
}
g.RemoveHostname()
if c.Hostname != "" || !c.UtsMode.IsHost() {
// Set the hostname in the OCI configuration only
// if specified by the user or if we are creating
// a new UTS namespace.
g.SetHostname(hostname)
}
g.AddProcessEnv("HOSTNAME", hostname)
utsMode := c.UtsMode
if IsNS(string(utsMode)) {
return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), NS(string(utsMode)))
}
if utsMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.UTSNamespace))
}
if utsCtr := utsMode.Container(); utsCtr != "" {
logrus.Debugf("using container %s utsmode", utsCtr)
}
return nil
}

172
pkg/spec/security.go Normal file
View File

@ -0,0 +1,172 @@
package createconfig
import (
"fmt"
"strings"
"github.com/containers/libpod/libpod"
"github.com/docker/docker/oci/caps"
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
)
func (c *SecurityConfig) ToCreateOptions() ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
options = append(options, libpod.WithSecLabels(c.LabelOpts))
options = append(options, libpod.WithPrivileged(c.Privileged))
return options, nil
}
func (c *SecurityConfig) SetLabelOpts(runtime *libpod.Runtime, pidConfig *PidConfig, ipcConfig *IpcConfig) error {
if c.Privileged {
c.LabelOpts = label.DisableSecOpt()
return nil
}
var labelOpts []string
if pidConfig.PidMode.IsHost() {
labelOpts = append(labelOpts, label.DisableSecOpt()...)
} else if pidConfig.PidMode.IsContainer() {
ctr, err := runtime.LookupContainer(pidConfig.PidMode.Container())
if err != nil {
return errors.Wrapf(err, "container %q not found", pidConfig.PidMode.Container())
}
secopts, err := label.DupSecOpt(ctr.ProcessLabel())
if err != nil {
return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
}
labelOpts = append(labelOpts, secopts...)
}
if ipcConfig.IpcMode.IsHost() {
labelOpts = append(labelOpts, label.DisableSecOpt()...)
} else if ipcConfig.IpcMode.IsContainer() {
ctr, err := runtime.LookupContainer(ipcConfig.IpcMode.Container())
if err != nil {
return errors.Wrapf(err, "container %q not found", ipcConfig.IpcMode.Container())
}
secopts, err := label.DupSecOpt(ctr.ProcessLabel())
if err != nil {
return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
}
labelOpts = append(labelOpts, secopts...)
}
c.LabelOpts = append(c.LabelOpts, labelOpts...)
return nil
}
func (c *SecurityConfig) SetSecurityOpts(runtime *libpod.Runtime, securityOpts []string) error {
for _, opt := range securityOpts {
if opt == "no-new-privileges" {
c.NoNewPrivs = true
} else {
con := strings.SplitN(opt, "=", 2)
if len(con) != 2 {
return fmt.Errorf("invalid --security-opt 1: %q", opt)
}
switch con[0] {
case "label":
c.LabelOpts = append(c.LabelOpts, con[1])
case "apparmor":
c.ApparmorProfile = con[1]
case "seccomp":
c.SeccompProfilePath = con[1]
default:
return fmt.Errorf("invalid --security-opt 2: %q", opt)
}
}
}
if c.SeccompProfilePath == "" {
var err error
c.SeccompProfilePath, err = libpod.DefaultSeccompPath()
if err != nil {
return err
}
}
c.SecurityOpts = securityOpts
return nil
}
func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserConfig) error {
// HANDLE CAPABILITIES
// NOTE: Must happen before SECCOMP
if c.Privileged {
g.SetupPrivileged(true)
}
useNotRoot := func(user string) bool {
if user == "" || user == "root" || user == "0" {
return false
}
return true
}
configSpec := g.Config
var err error
var caplist []string
bounding := configSpec.Process.Capabilities.Bounding
if useNotRoot(user.User) {
configSpec.Process.Capabilities.Bounding = caplist
}
caplist, err = caps.TweakCapabilities(configSpec.Process.Capabilities.Bounding, c.CapAdd, c.CapDrop, nil, false)
if err != nil {
return err
}
configSpec.Process.Capabilities.Bounding = caplist
configSpec.Process.Capabilities.Permitted = caplist
configSpec.Process.Capabilities.Inheritable = caplist
configSpec.Process.Capabilities.Effective = caplist
configSpec.Process.Capabilities.Ambient = caplist
if useNotRoot(user.User) {
caplist, err = caps.TweakCapabilities(bounding, c.CapAdd, c.CapDrop, nil, false)
if err != nil {
return err
}
}
configSpec.Process.Capabilities.Bounding = caplist
// HANDLE SECCOMP
if c.SeccompProfilePath != "unconfined" {
seccompConfig, err := getSeccompConfig(c, configSpec)
if err != nil {
return err
}
configSpec.Linux.Seccomp = seccompConfig
}
// Clear default Seccomp profile from Generator for privileged containers
if c.SeccompProfilePath == "unconfined" || c.Privileged {
configSpec.Linux.Seccomp = nil
}
for _, opt := range c.SecurityOpts {
// Split on both : and =
splitOpt := strings.Split(opt, "=")
if len(splitOpt) == 1 {
splitOpt = strings.Split(opt, ":")
}
if len(splitOpt) < 2 {
continue
}
switch splitOpt[0] {
case "label":
configSpec.Annotations[libpod.InspectAnnotationLabel] = splitOpt[1]
case "seccomp":
configSpec.Annotations[libpod.InspectAnnotationSeccomp] = splitOpt[1]
case "apparmor":
configSpec.Annotations[libpod.InspectAnnotationApparmor] = splitOpt[1]
}
}
g.SetRootReadonly(c.ReadOnlyRootfs)
for sysctlKey, sysctlVal := range c.Sysctl {
g.AddLinuxSysctl(sysctlKey, sysctlVal)
}
return nil
}

View File

@ -1,7 +1,6 @@
package createconfig package createconfig
import ( import (
"os"
"strings" "strings"
"github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod"
@ -10,13 +9,11 @@ import (
"github.com/containers/libpod/pkg/cgroups" "github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/sysinfo" "github.com/containers/libpod/pkg/sysinfo"
"github.com/docker/docker/oci/caps"
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/opencontainers/runc/libcontainer/user" "github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go" spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
) )
const cpuPeriod = 100000 const cpuPeriod = 100000
@ -47,14 +44,13 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
canMountSys := true canMountSys := true
isRootless := rootless.IsRootless() isRootless := rootless.IsRootless()
hasUserns := config.UsernsMode.IsContainer() || config.UsernsMode.IsNS() || len(config.IDMappings.UIDMap) > 0 || len(config.IDMappings.GIDMap) > 0 inUserNS := config.User.InNS(isRootless)
inUserNS := isRootless || (hasUserns && !config.UsernsMode.IsHost())
if inUserNS && config.NetMode.IsHost() { if inUserNS && config.Network.NetMode.IsHost() {
canMountSys = false canMountSys = false
} }
if config.Privileged && canMountSys { if config.Security.Privileged && canMountSys {
cgroupPerm = "rw" cgroupPerm = "rw"
g.RemoveMount("/sys") g.RemoveMount("/sys")
sysMnt := spec.Mount{ sysMnt := spec.Mount{
@ -68,7 +64,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
addCgroup = false addCgroup = false
g.RemoveMount("/sys") g.RemoveMount("/sys")
r := "ro" r := "ro"
if config.Privileged { if config.Security.Privileged {
r = "rw" r = "rw"
} }
sysMnt := spec.Mount{ sysMnt := spec.Mount{
@ -78,7 +74,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"}, Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"},
} }
g.AddMount(sysMnt) g.AddMount(sysMnt)
if !config.Privileged && isRootless { if !config.Security.Privileged && isRootless {
g.AddLinuxMaskedPaths("/sys/kernel") g.AddLinuxMaskedPaths("/sys/kernel")
} }
} }
@ -92,9 +88,9 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
} }
// When using a different user namespace, check that the GID 5 is mapped inside // When using a different user namespace, check that the GID 5 is mapped inside
// the container. // the container.
if gid5Available && len(config.IDMappings.GIDMap) > 0 { if gid5Available && len(config.User.IDMappings.GIDMap) > 0 {
mappingFound := false mappingFound := false
for _, r := range config.IDMappings.GIDMap { for _, r := range config.User.IDMappings.GIDMap {
if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size { if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size {
mappingFound = true mappingFound = true
break break
@ -117,7 +113,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
g.AddMount(devPts) g.AddMount(devPts)
} }
if inUserNS && config.IpcMode.IsHost() { if inUserNS && config.Ipc.IpcMode.IsHost() {
g.RemoveMount("/dev/mqueue") g.RemoveMount("/dev/mqueue")
devMqueue := spec.Mount{ devMqueue := spec.Mount{
Destination: "/dev/mqueue", Destination: "/dev/mqueue",
@ -127,7 +123,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
} }
g.AddMount(devMqueue) g.AddMount(devMqueue)
} }
if inUserNS && config.PidMode.IsHost() { if inUserNS && config.Pid.PidMode.IsHost() {
g.RemoveMount("/proc") g.RemoveMount("/proc")
procMount := spec.Mount{ procMount := spec.Mount{
Destination: "/proc", Destination: "/proc",
@ -154,55 +150,6 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
for key, val := range config.Annotations { for key, val := range config.Annotations {
g.AddAnnotation(key, val) g.AddAnnotation(key, val)
} }
g.SetRootReadonly(config.ReadOnlyRootfs)
if config.HTTPProxy {
for _, envSpec := range []string{
"http_proxy",
"HTTP_PROXY",
"https_proxy",
"HTTPS_PROXY",
"ftp_proxy",
"FTP_PROXY",
"no_proxy",
"NO_PROXY",
} {
envVal := os.Getenv(envSpec)
if envVal != "" {
g.AddProcessEnv(envSpec, envVal)
}
}
}
hostname := config.Hostname
if hostname == "" {
if utsCtrID := config.UtsMode.Container(); utsCtrID != "" {
utsCtr, err := runtime.GetContainer(utsCtrID)
if err != nil {
return nil, errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID)
}
hostname = utsCtr.Hostname()
} else if config.NetMode.IsHost() || config.UtsMode.IsHost() {
hostname, err = os.Hostname()
if err != nil {
return nil, errors.Wrap(err, "unable to retrieve hostname of the host")
}
} else {
logrus.Debug("No hostname set; container's hostname will default to runtime default")
}
}
g.RemoveHostname()
if config.Hostname != "" || !config.UtsMode.IsHost() {
// Set the hostname in the OCI configuration only
// if specified by the user or if we are creating
// a new UTS namespace.
g.SetHostname(hostname)
}
g.AddProcessEnv("HOSTNAME", hostname)
for sysctlKey, sysctlVal := range config.Sysctl {
g.AddLinuxSysctl(sysctlKey, sysctlVal)
}
g.AddProcessEnv("container", "podman") g.AddProcessEnv("container", "podman")
addedResources := false addedResources := false
@ -272,7 +219,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
} }
// Devices // Devices
if config.Privileged { if config.Security.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
// already adding them all. // already adding them all.
@ -287,17 +234,11 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
} }
} }
for _, uidmap := range config.IDMappings.UIDMap {
g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
}
for _, gidmap := range config.IDMappings.GIDMap {
g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
}
// SECURITY OPTS // SECURITY OPTS
g.SetProcessNoNewPrivileges(config.NoNewPrivs) g.SetProcessNoNewPrivileges(config.Security.NoNewPrivs)
if !config.Privileged { if !config.Security.Privileged {
g.SetProcessApparmorProfile(config.ApparmorProfile) g.SetProcessApparmorProfile(config.Security.ApparmorProfile)
} }
blockAccessToKernelFilesystems(config, &g) blockAccessToKernelFilesystems(config, &g)
@ -341,54 +282,35 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
return nil, err return nil, err
} }
if err := addPidNS(config, &g); err != nil { // NAMESPACES
if err := config.Pid.ConfigureGenerator(&g); err != nil {
return nil, err return nil, err
} }
if err := addUserNS(config, &g); err != nil { if err := config.User.ConfigureGenerator(&g); err != nil {
return nil, err return nil, err
} }
if err := addNetNS(config, &g); err != nil { if err := config.Network.ConfigureGenerator(&g); err != nil {
return nil, err return nil, err
} }
if err := addUTSNS(config, &g); err != nil { if err := config.Uts.ConfigureGenerator(&g, &config.Network, runtime); err != nil {
return nil, err return nil, err
} }
if err := addIpcNS(config, &g); err != nil { if err := config.Ipc.ConfigureGenerator(&g); err != nil {
return nil, err return nil, err
} }
if err := addCgroupNS(config, &g); err != nil { if err := config.Cgroup.ConfigureGenerator(&g); err != nil {
return nil, err return nil, err
} }
configSpec := g.Config configSpec := g.Config
// HANDLE CAPABILITIES if err := config.Security.ConfigureGenerator(&g, &config.User); err != nil {
// NOTE: Must happen before SECCOMP return nil, err
if !config.Privileged {
if err := setupCapabilities(config, configSpec); err != nil {
return nil, err
}
} else {
g.SetupPrivileged(true)
}
// HANDLE SECCOMP
if config.SeccompProfilePath != "unconfined" {
seccompConfig, err := getSeccompConfig(config, configSpec)
if err != nil {
return nil, err
}
configSpec.Linux.Seccomp = seccompConfig
}
// Clear default Seccomp profile from Generator for privileged containers
if config.SeccompProfilePath == "unconfined" || config.Privileged {
configSpec.Linux.Seccomp = nil
} }
// BIND MOUNTS // BIND MOUNTS
@ -430,7 +352,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
} }
} }
switch config.Cgroups { switch config.Cgroup.Cgroups {
case "disabled": case "disabled":
if addedResources { if addedResources {
return nil, errors.New("cannot specify resource limits when cgroups are disabled is specified") return nil, errors.New("cannot specify resource limits when cgroups are disabled is specified")
@ -461,48 +383,23 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
configSpec.Annotations[libpod.InspectAnnotationVolumesFrom] = strings.Join(config.VolumesFrom, ",") configSpec.Annotations[libpod.InspectAnnotationVolumesFrom] = strings.Join(config.VolumesFrom, ",")
} }
if config.Privileged { if config.Security.Privileged {
configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseTrue configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseTrue
} else { } else {
configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseFalse configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseFalse
} }
if config.PublishAll {
configSpec.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
} else {
configSpec.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
}
if config.Init { if config.Init {
configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseTrue configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseTrue
} else { } else {
configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseFalse configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseFalse
} }
for _, opt := range config.SecurityOpts {
// Split on both : and =
splitOpt := strings.Split(opt, "=")
if len(splitOpt) == 1 {
splitOpt = strings.Split(opt, ":")
}
if len(splitOpt) < 2 {
continue
}
switch splitOpt[0] {
case "label":
configSpec.Annotations[libpod.InspectAnnotationLabel] = splitOpt[1]
case "seccomp":
configSpec.Annotations[libpod.InspectAnnotationSeccomp] = splitOpt[1]
case "apparmor":
configSpec.Annotations[libpod.InspectAnnotationApparmor] = splitOpt[1]
}
}
return configSpec, nil return configSpec, nil
} }
func blockAccessToKernelFilesystems(config *CreateConfig, g *generate.Generator) { func blockAccessToKernelFilesystems(config *CreateConfig, g *generate.Generator) {
if !config.Privileged { if !config.Security.Privileged {
for _, mp := range []string{ for _, mp := range []string{
"/proc/acpi", "/proc/acpi",
"/proc/kcore", "/proc/kcore",
@ -518,7 +415,7 @@ func blockAccessToKernelFilesystems(config *CreateConfig, g *generate.Generator)
g.AddLinuxMaskedPaths(mp) g.AddLinuxMaskedPaths(mp)
} }
if config.PidMode.IsHost() && rootless.IsRootless() { if config.Pid.PidMode.IsHost() && rootless.IsRootless() {
return return
} }
@ -535,130 +432,6 @@ func blockAccessToKernelFilesystems(config *CreateConfig, g *generate.Generator)
} }
} }
func addPidNS(config *CreateConfig, g *generate.Generator) error {
pidMode := config.PidMode
if IsNS(string(pidMode)) {
return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), NS(string(pidMode)))
}
if pidMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
}
if pidCtr := pidMode.Container(); pidCtr != "" {
logrus.Debugf("using container %s pidmode", pidCtr)
}
if IsPod(string(pidMode)) {
logrus.Debug("using pod pidmode")
}
return nil
}
func addUserNS(config *CreateConfig, g *generate.Generator) error {
if IsNS(string(config.UsernsMode)) {
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), NS(string(config.UsernsMode))); err != nil {
return err
}
// runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping
g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
}
if (len(config.IDMappings.UIDMap) > 0 || len(config.IDMappings.GIDMap) > 0) && !config.UsernsMode.IsHost() {
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
return err
}
}
return nil
}
func addNetNS(config *CreateConfig, g *generate.Generator) error {
netMode := config.NetMode
if netMode.IsHost() {
logrus.Debug("Using host netmode")
return g.RemoveLinuxNamespace(string(spec.NetworkNamespace))
} else if netMode.IsNone() {
logrus.Debug("Using none netmode")
return nil
} else if netMode.IsBridge() {
logrus.Debug("Using bridge netmode")
return nil
} else if netCtr := netMode.Container(); netCtr != "" {
logrus.Debugf("using container %s netmode", netCtr)
return nil
} else if IsNS(string(netMode)) {
logrus.Debug("Using ns netmode")
return g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), NS(string(netMode)))
} else if IsPod(string(netMode)) {
logrus.Debug("Using pod netmode, unless pod is not sharing")
return nil
} else if netMode.IsSlirp4netns() {
logrus.Debug("Using slirp4netns netmode")
return nil
} else if netMode.IsUserDefined() {
logrus.Debug("Using user defined netmode")
return nil
}
return errors.Errorf("unknown network mode")
}
func addUTSNS(config *CreateConfig, g *generate.Generator) error {
utsMode := config.UtsMode
if IsNS(string(utsMode)) {
return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), NS(string(utsMode)))
}
if utsMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.UTSNamespace))
}
if utsCtr := utsMode.Container(); utsCtr != "" {
logrus.Debugf("using container %s utsmode", utsCtr)
}
return nil
}
func addIpcNS(config *CreateConfig, g *generate.Generator) error {
ipcMode := config.IpcMode
if IsNS(string(ipcMode)) {
return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), NS(string(ipcMode)))
}
if ipcMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.IPCNamespace))
}
if ipcCtr := ipcMode.Container(); ipcCtr != "" {
logrus.Debugf("Using container %s ipcmode", ipcCtr)
}
return nil
}
func addCgroupNS(config *CreateConfig, g *generate.Generator) error {
cgroupMode := config.CgroupMode
if cgroupMode.IsDefaultValue() {
// If the value is not specified, default to "private" on cgroups v2 and "host" on cgroups v1.
unified, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return err
}
if unified {
cgroupMode = "private"
} else {
cgroupMode = "host"
}
}
if cgroupMode.IsNS() {
return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), NS(string(cgroupMode)))
}
if cgroupMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.CgroupNamespace))
}
if cgroupMode.IsPrivate() {
return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "")
}
if cgCtr := cgroupMode.Container(); cgCtr != "" {
logrus.Debugf("Using container %s cgroup mode", cgCtr)
}
return nil
}
func addRlimits(config *CreateConfig, g *generate.Generator) error { func addRlimits(config *CreateConfig, g *generate.Generator) error {
var ( var (
kernelMax uint64 = 1048576 kernelMax uint64 = 1048576
@ -702,37 +475,3 @@ func addRlimits(config *CreateConfig, g *generate.Generator) error {
return nil return nil
} }
func setupCapabilities(config *CreateConfig, configSpec *spec.Spec) error {
useNotRoot := func(user string) bool {
if user == "" || user == "root" || user == "0" {
return false
}
return true
}
var err error
var caplist []string
bounding := configSpec.Process.Capabilities.Bounding
if useNotRoot(config.User) {
configSpec.Process.Capabilities.Bounding = caplist
}
caplist, err = caps.TweakCapabilities(configSpec.Process.Capabilities.Bounding, config.CapAdd, config.CapDrop, nil, false)
if err != nil {
return err
}
configSpec.Process.Capabilities.Bounding = caplist
configSpec.Process.Capabilities.Permitted = caplist
configSpec.Process.Capabilities.Inheritable = caplist
configSpec.Process.Capabilities.Effective = caplist
configSpec.Process.Capabilities.Ambient = caplist
if useNotRoot(config.User) {
caplist, err = caps.TweakCapabilities(bounding, config.CapAdd, config.CapDrop, nil, false)
if err != nil {
return err
}
}
configSpec.Process.Capabilities.Bounding = caplist
return nil
}

View File

@ -21,9 +21,9 @@ var (
func makeTestCreateConfig() *CreateConfig { func makeTestCreateConfig() *CreateConfig {
cc := new(CreateConfig) cc := new(CreateConfig)
cc.Resources = CreateResourceConfig{} cc.Resources = CreateResourceConfig{}
cc.IDMappings = new(storage.IDMappingOptions) cc.User.IDMappings = new(storage.IDMappingOptions)
cc.IDMappings.UIDMap = []idtools.IDMap{} cc.User.IDMappings.UIDMap = []idtools.IDMap{}
cc.IDMappings.GIDMap = []idtools.IDMap{} cc.User.IDMappings.GIDMap = []idtools.IDMap{}
return cc return cc
} }

View File

@ -160,7 +160,7 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
} }
// If requested, add tmpfs filesystems for read-only containers. // If requested, add tmpfs filesystems for read-only containers.
if config.ReadOnlyRootfs && config.ReadOnlyTmpfs { if config.Security.ReadOnlyRootfs && config.Security.ReadOnlyTmpfs {
readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"} readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"}
options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"} options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"}
for _, dest := range readonlyTmpfs { for _, dest := range readonlyTmpfs {
@ -807,7 +807,7 @@ func (config *CreateConfig) addContainerInitBinary(path string) (spec.Mount, err
if path == "" { if path == "" {
return mount, fmt.Errorf("please specify a path to the container-init binary") return mount, fmt.Errorf("please specify a path to the container-init binary")
} }
if !config.PidMode.IsPrivate() { if !config.Pid.PidMode.IsPrivate() {
return mount, fmt.Errorf("cannot add init binary as PID 1 (PID namespace isn't private)") return mount, fmt.Errorf("cannot add init binary as PID 1 (PID namespace isn't private)")
} }
if config.Systemd { if config.Systemd {

2
vendor/modules.txt vendored
View File

@ -382,8 +382,8 @@ github.com/opencontainers/runtime-tools/filepath
github.com/opencontainers/runtime-tools/specerror github.com/opencontainers/runtime-tools/specerror
github.com/opencontainers/runtime-tools/error github.com/opencontainers/runtime-tools/error
# github.com/opencontainers/selinux v1.3.0 # github.com/opencontainers/selinux v1.3.0
github.com/opencontainers/selinux/go-selinux/label
github.com/opencontainers/selinux/go-selinux github.com/opencontainers/selinux/go-selinux
github.com/opencontainers/selinux/go-selinux/label
# github.com/openshift/api v3.9.1-0.20190810003144-27fb16909b15+incompatible # github.com/openshift/api v3.9.1-0.20190810003144-27fb16909b15+incompatible
github.com/openshift/api/config/v1 github.com/openshift/api/config/v1
# github.com/openshift/imagebuilder v1.1.1 # github.com/openshift/imagebuilder v1.1.1