podman/pkg/specgen/oci.go

259 lines
6.6 KiB
Go

package specgen
import (
"strings"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/rootless"
createconfig "github.com/containers/libpod/pkg/spec"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
)
func (s *SpecGenerator) ToOCISpec(rt *libpod.Runtime, newImage *image.Image) (*spec.Spec, error) {
var (
inUserNS bool
)
cgroupPerm := "ro"
g, err := generate.New("linux")
if err != nil {
return nil, err
}
// Remove the default /dev/shm mount to ensure we overwrite it
g.RemoveMount("/dev/shm")
g.HostSpecific = true
addCgroup := true
canMountSys := true
isRootless := rootless.IsRootless()
if isRootless {
inUserNS = true
}
if !s.UserNS.IsHost() {
if s.UserNS.IsContainer() || s.UserNS.IsPath() {
inUserNS = true
}
if s.UserNS.IsPrivate() {
inUserNS = true
}
}
if inUserNS && s.NetNS.IsHost() {
canMountSys = false
}
if s.Privileged && canMountSys {
cgroupPerm = "rw"
g.RemoveMount("/sys")
sysMnt := spec.Mount{
Destination: "/sys",
Type: "sysfs",
Source: "sysfs",
Options: []string{"rprivate", "nosuid", "noexec", "nodev", "rw"},
}
g.AddMount(sysMnt)
} else if !canMountSys {
addCgroup = false
g.RemoveMount("/sys")
r := "ro"
if s.Privileged {
r = "rw"
}
sysMnt := spec.Mount{
Destination: "/sys",
Type: "bind", // should we use a constant for this, like createconfig?
Source: "/sys",
Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"},
}
g.AddMount(sysMnt)
if !s.Privileged && isRootless {
g.AddLinuxMaskedPaths("/sys/kernel")
}
}
gid5Available := true
if isRootless {
nGids, err := createconfig.GetAvailableGids()
if err != nil {
return nil, err
}
gid5Available = nGids >= 5
}
// When using a different user namespace, check that the GID 5 is mapped inside
// the container.
if gid5Available && (s.IDMappings != nil && len(s.IDMappings.GIDMap) > 0) {
mappingFound := false
for _, r := range s.IDMappings.GIDMap {
if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size {
mappingFound = true
break
}
}
if !mappingFound {
gid5Available = false
}
}
if !gid5Available {
// If we have no GID mappings, the gid=5 default option would fail, so drop it.
g.RemoveMount("/dev/pts")
devPts := spec.Mount{
Destination: "/dev/pts",
Type: "devpts",
Source: "devpts",
Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"},
}
g.AddMount(devPts)
}
if inUserNS && s.IpcNS.IsHost() {
g.RemoveMount("/dev/mqueue")
devMqueue := spec.Mount{
Destination: "/dev/mqueue",
Type: "bind", // constant ?
Source: "/dev/mqueue",
Options: []string{"bind", "nosuid", "noexec", "nodev"},
}
g.AddMount(devMqueue)
}
if inUserNS && s.PidNS.IsHost() {
g.RemoveMount("/proc")
procMount := spec.Mount{
Destination: "/proc",
Type: createconfig.TypeBind,
Source: "/proc",
Options: []string{"rbind", "nosuid", "noexec", "nodev"},
}
g.AddMount(procMount)
}
if addCgroup {
cgroupMnt := spec.Mount{
Destination: "/sys/fs/cgroup",
Type: "cgroup",
Source: "cgroup",
Options: []string{"rprivate", "nosuid", "noexec", "nodev", "relatime", cgroupPerm},
}
g.AddMount(cgroupMnt)
}
g.SetProcessCwd(s.WorkDir)
g.SetProcessArgs(s.Command)
g.SetProcessTerminal(s.Terminal)
for key, val := range s.Annotations {
g.AddAnnotation(key, val)
}
g.AddProcessEnv("container", "podman")
g.Config.Linux.Resources = s.ResourceLimits
// Devices
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
// already adding them all.
if err := createconfig.AddPrivilegedDevices(&g); err != nil {
return nil, err
}
} else {
for _, device := range s.Devices {
if err := createconfig.DevicesFromPath(&g, device.Path); err != nil {
return nil, err
}
}
}
// SECURITY OPTS
g.SetProcessNoNewPrivileges(s.NoNewPrivileges)
if !s.Privileged {
g.SetProcessApparmorProfile(s.ApparmorProfile)
}
createconfig.BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), &g)
for name, val := range s.Env {
g.AddProcessEnv(name, val)
}
// TODO rlimits and ulimits needs further refinement by someone more
// familiar with the code.
//if err := addRlimits(config, &g); err != nil {
// return nil, err
//}
// NAMESPACES
if err := s.pidConfigureGenerator(&g); err != nil {
return nil, err
}
if err := s.userConfigureGenerator(&g); err != nil {
return nil, err
}
if err := s.networkConfigureGenerator(&g); err != nil {
return nil, err
}
if err := s.utsConfigureGenerator(&g, rt); err != nil {
return nil, err
}
if err := s.ipcConfigureGenerator(&g); err != nil {
return nil, err
}
if err := s.cgroupConfigureGenerator(&g); err != nil {
return nil, err
}
configSpec := g.Config
if err := s.securityConfigureGenerator(&g, newImage); err != nil {
return nil, err
}
// BIND MOUNTS
configSpec.Mounts = createconfig.SupercedeUserMounts(s.Mounts, configSpec.Mounts)
// Process mounts to ensure correct options
if err := createconfig.InitFSMounts(configSpec.Mounts); err != nil {
return nil, err
}
// Add annotations
if configSpec.Annotations == nil {
configSpec.Annotations = make(map[string]string)
}
// TODO cidfile is not in specgen; when wiring up cli, we will need to move this out of here
// leaving as a reminder
//if config.CidFile != "" {
// configSpec.Annotations[libpod.InspectAnnotationCIDFile] = config.CidFile
//}
if s.Remove {
configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseTrue
} else {
configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseFalse
}
if len(s.VolumesFrom) > 0 {
configSpec.Annotations[libpod.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ",")
}
if s.Privileged {
configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseTrue
} else {
configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseFalse
}
// TODO Init might not make it into the specgen and therefore is not available here. We should deal
// with this when we wire up the CLI; leaving as a reminder
//if s.Init {
// configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseTrue
//} else {
// configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseFalse
//}
return configSpec, nil
}