mirror of https://github.com/containers/podman.git
Merge pull request #10589 from umohnani8/pod-userns
Add support for pod inside of user namespace.
This commit is contained in:
commit
e136ad485c
|
@ -184,6 +184,9 @@ func createInit(c *cobra.Command) error {
|
|||
if c.Flag("cpu-quota").Changed && c.Flag("cpus").Changed {
|
||||
return errors.Errorf("--cpu-quota and --cpus cannot be set together")
|
||||
}
|
||||
if c.Flag("pod").Changed && !strings.HasPrefix(c.Flag("pod").Value.String(), "new:") && c.Flag("userns").Changed {
|
||||
return errors.Errorf("--userns and --pod cannot be set together")
|
||||
}
|
||||
|
||||
noHosts, err := c.Flags().GetBool("no-hosts")
|
||||
if err != nil {
|
||||
|
@ -309,6 +312,12 @@ func createPodIfNecessary(s *specgen.SpecGenerator, netOpts *entities.NetOptions
|
|||
if len(podName) < 1 {
|
||||
return nil, errors.Errorf("new pod name must be at least one character")
|
||||
}
|
||||
|
||||
userns, err := specgen.ParseUserNamespace(cliVals.UserNS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
createOptions := entities.PodCreateOptions{
|
||||
Name: podName,
|
||||
Infra: true,
|
||||
|
@ -318,6 +327,7 @@ func createPodIfNecessary(s *specgen.SpecGenerator, netOpts *entities.NetOptions
|
|||
Cpus: cliVals.CPUS,
|
||||
CpusetCpus: cliVals.CPUSetCPUs,
|
||||
Pid: cliVals.PID,
|
||||
Userns: userns,
|
||||
}
|
||||
// Unset config values we passed to the pod to prevent them being used twice for the container and pod.
|
||||
s.ContainerBasicConfig.Hostname = ""
|
||||
|
|
|
@ -48,6 +48,7 @@ var (
|
|||
podIDFile string
|
||||
replace bool
|
||||
share string
|
||||
userns string
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -72,6 +73,10 @@ func init() {
|
|||
flags.StringVar(&createOptions.CGroupParent, cgroupParentflagName, "", "Set parent cgroup for the pod")
|
||||
_ = createCommand.RegisterFlagCompletionFunc(cgroupParentflagName, completion.AutocompleteDefault)
|
||||
|
||||
usernsFlagName := "userns"
|
||||
flags.StringVar(&userns, usernsFlagName, os.Getenv("PODMAN_USERNS"), "User namespace to use")
|
||||
_ = createCommand.RegisterFlagCompletionFunc(usernsFlagName, common.AutocompleteUserNamespace)
|
||||
|
||||
flags.BoolVar(&createOptions.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with")
|
||||
|
||||
infraConmonPidfileFlagName := "infra-conmon-pidfile"
|
||||
|
@ -178,6 +183,11 @@ func create(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
}
|
||||
|
||||
createOptions.Userns, err = specgen.ParseUserNamespace(userns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cmd.Flag("pod-id-file").Changed {
|
||||
podIDFD, err = util.OpenExclusiveFile(podIDFile)
|
||||
if err != nil && os.IsExist(err) {
|
||||
|
|
|
@ -1123,9 +1123,9 @@ Podman allocates unique ranges of UIDs and GIDs from the `containers` subpordina
|
|||
|
||||
Valid `auto`options:
|
||||
|
||||
- *gidmapping*=_HOST_GID:CONTAINER_GID:SIZE_: to force a GID mapping to be present in the user namespace.
|
||||
- *gidmapping*=_CONTAINER_GID:HOST_GID:SIZE_: to force a GID mapping to be present in the user namespace.
|
||||
- *size*=_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace.
|
||||
- *uidmapping*=_HOST_UID:CONTAINER_UID:SIZE_: to force a UID mapping to be present in the user namespace.
|
||||
- *uidmapping*=_CONTAINER_UID:HOST_UID:SIZE_: to force a UID mapping to be present in the user namespace.
|
||||
|
||||
**container:**_id_: join the user namespace of the specified container.
|
||||
|
||||
|
|
|
@ -164,6 +164,19 @@ podman generates a UUID for each pod, and if a name is not assigned
|
|||
to the container with **--name** then a random string name will be generated
|
||||
for it. The name is useful any place you need to identify a pod.
|
||||
|
||||
#### **--userns**=*mode*
|
||||
|
||||
Set the user namespace mode for all the containers in a pod. It defaults to the **PODMAN_USERNS** environment variable. An empty value ("") means user namespaces are disabled.
|
||||
|
||||
Valid _mode_ values are:
|
||||
|
||||
- *auto[:*_OPTIONS,..._*]*: automatically create a namespace. It is possible to specify these options to `auto`:
|
||||
- *gidmapping=*_CONTAINER_GID:HOST_GID:SIZE_ to force a GID mapping to be present in the user namespace.
|
||||
- *size=*_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace.
|
||||
- *uidmapping=*_CONTAINER_UID:HOST_UID:SIZE_ to force a UID mapping to be present in the user namespace.
|
||||
- *host*: run in the user namespace of the caller. The processes running in the container will have the same privileges on the host as any other process launched by the calling user (default).
|
||||
- *keep-id*: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is ignored for containers created by the root user.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
```
|
||||
|
|
|
@ -1181,9 +1181,9 @@ Podman allocates unique ranges of UIDs and GIDs from the `containers` subpordina
|
|||
|
||||
Valid `auto`options:
|
||||
|
||||
- *gidmapping*=_HOST_GID:CONTAINER_GID:SIZE_: to force a GID mapping to be present in the user namespace.
|
||||
- *gidmapping*=_CONTAINER_GID:HOST_GID:SIZE_: to force a GID mapping to be present in the user namespace.
|
||||
- *size*=_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace.
|
||||
- *uidmapping*=_HOST_UID:CONTAINER_UID:SIZE_: to force a UID mapping to be present in the user namespace.
|
||||
- *uidmapping*=_CONTAINER_UID:HOST_UID:SIZE_: to force a UID mapping to be present in the user namespace.
|
||||
|
||||
**container:**_id_: join the user namespace of the specified container.
|
||||
|
||||
|
|
|
@ -1020,8 +1020,8 @@ func (c *Container) RWSize() (int64, error) {
|
|||
}
|
||||
|
||||
// IDMappings returns the UID/GID mapping used for the container
|
||||
func (c *Container) IDMappings() (storage.IDMappingOptions, error) {
|
||||
return c.config.IDMappings, nil
|
||||
func (c *Container) IDMappings() storage.IDMappingOptions {
|
||||
return c.config.IDMappings
|
||||
}
|
||||
|
||||
// RootUID returns the root user mapping from container
|
||||
|
|
|
@ -367,6 +367,12 @@ func (c *Container) setupStorageMapping(dest, from *storage.IDMappingOptions) {
|
|||
return
|
||||
}
|
||||
*dest = *from
|
||||
// If we are creating a container inside a pod, we always want to inherit the
|
||||
// userns settings from the infra container. So clear the auto userns settings
|
||||
// so that we don't request storage for a new uid/gid map.
|
||||
if c.PodID() != "" && !c.IsInfra() {
|
||||
dest.AutoUserNs = false
|
||||
}
|
||||
if dest.AutoUserNs {
|
||||
overrides := c.getUserOverrides()
|
||||
dest.AutoUserNsOpts.PasswdFile = overrides.ContainerEtcPasswdPath
|
||||
|
|
|
@ -659,7 +659,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if c.config.IDMappings.AutoUserNs {
|
||||
if c.config.UserNsCtr == "" && c.config.IDMappings.AutoUserNs {
|
||||
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1884,7 +1884,7 @@ func (c *Container) generateResolvConf() (string, error) {
|
|||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Join(c.state.RunDir, "resolv.conf"), nil
|
||||
return destPath, nil
|
||||
}
|
||||
|
||||
// generateHosts creates a containers hosts file
|
||||
|
|
|
@ -105,6 +105,8 @@ type InspectPodInfraConfig struct {
|
|||
CPUSetCPUs string `json:"cpuset_cpus,omitempty"`
|
||||
// Pid is the PID namespace mode of the pod's infra container
|
||||
PidNS string `json:"pid_ns,omitempty"`
|
||||
// UserNS is the usernamespace that all the containers in the pod will join.
|
||||
UserNS string `json:"userns,omitempty"`
|
||||
}
|
||||
|
||||
// InspectPodContainerInfo contains information on a container in a pod.
|
||||
|
|
|
@ -956,8 +956,9 @@ func WithUserNSFrom(nsCtr *Container) CtrCreateOption {
|
|||
}
|
||||
|
||||
ctr.config.UserNsCtr = nsCtr.ID()
|
||||
ctr.config.IDMappings = nsCtr.config.IDMappings
|
||||
|
||||
if err := JSONDeepCopy(nsCtr.IDMappings(), &ctr.config.IDMappings); err != nil {
|
||||
return err
|
||||
}
|
||||
g := generate.Generator{Config: ctr.config.Spec}
|
||||
|
||||
g.ClearLinuxUIDMappings()
|
||||
|
@ -968,7 +969,6 @@ func WithUserNSFrom(nsCtr *Container) CtrCreateOption {
|
|||
for _, gidmap := range nsCtr.config.IDMappings.GIDMap {
|
||||
g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
|
||||
}
|
||||
ctr.config.IDMappings = nsCtr.config.IDMappings
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -2423,6 +2423,24 @@ func WithVolatile() CtrCreateOption {
|
|||
}
|
||||
|
||||
ctr.config.Volatile = true
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithPodUserns sets the userns for the infra container in a pod.
|
||||
func WithPodUserns(userns specgen.Namespace) PodCreateOption {
|
||||
return func(pod *Pod) error {
|
||||
if pod.valid {
|
||||
return define.ErrPodFinalized
|
||||
}
|
||||
|
||||
if !pod.config.InfraContainer.HasInfraContainer {
|
||||
return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod userns as no infra container is being created")
|
||||
}
|
||||
|
||||
pod.config.InfraContainer.Userns = userns
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ type InfraContainerConfig struct {
|
|||
Slirp4netns bool `json:"slirp4netns,omitempty"`
|
||||
NetworkOptions map[string][]string `json:"network_options,omitempty"`
|
||||
ResourceLimits *specs.LinuxResources `json:"resource_limits,omitempty"`
|
||||
Userns specgen.Namespace `json:"userns,omitempty"`
|
||||
}
|
||||
|
||||
// ID retrieves the pod's ID
|
||||
|
|
|
@ -593,6 +593,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
|
|||
infraConfig.CPUQuota = p.CPUQuota()
|
||||
infraConfig.CPUSetCPUs = p.ResourceLim().CPU.Cpus
|
||||
infraConfig.PidNS = p.PidMode()
|
||||
infraConfig.UserNS = p.config.InfraContainer.Userns.String()
|
||||
|
||||
if len(p.config.InfraContainer.DNSServer) > 0 {
|
||||
infraConfig.DNSServer = make([]string, 0, len(p.config.InfraContainer.DNSServer))
|
||||
|
|
|
@ -8,7 +8,9 @@ import (
|
|||
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/podman/v3/libpod/define"
|
||||
"github.com/containers/podman/v3/pkg/namespaces"
|
||||
"github.com/containers/podman/v3/pkg/rootless"
|
||||
"github.com/containers/podman/v3/pkg/specgen"
|
||||
"github.com/containers/podman/v3/pkg/util"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
|
@ -110,9 +112,7 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
|
|||
options = append(options, WithNetworkOptions(p.config.InfraContainer.NetworkOptions))
|
||||
}
|
||||
}
|
||||
// PostConfigureNetNS should not be set since user namespace sharing is not implemented
|
||||
// and rootless networking no longer supports post configuration setup
|
||||
options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, false, netmode, p.config.InfraContainer.Networks))
|
||||
options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, !p.config.InfraContainer.Userns.IsHost(), netmode, p.config.InfraContainer.Networks))
|
||||
}
|
||||
|
||||
// For each option in InfraContainerConfig - if set, pass into
|
||||
|
@ -158,11 +158,39 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
|
|||
g.Config.Linux.Namespaces = newNS
|
||||
}
|
||||
}
|
||||
|
||||
for _, ctl := range r.config.Containers.DefaultSysctls {
|
||||
sysctl := strings.SplitN(ctl, "=", 2)
|
||||
if len(sysctl) < 2 {
|
||||
return nil, errors.Errorf("invalid default sysctl %s", ctl)
|
||||
}
|
||||
|
||||
// Ignore net sysctls if --net=host
|
||||
if p.config.InfraContainer.HostNetwork && strings.HasPrefix(sysctl[0], "net.") {
|
||||
logrus.Infof("Sysctl %s=%s ignored in containers.conf, since Network Namespace set to host", sysctl[0], sysctl[1])
|
||||
continue
|
||||
}
|
||||
|
||||
g.AddLinuxSysctl(sysctl[0], sysctl[1])
|
||||
}
|
||||
|
||||
g.SetRootReadonly(true)
|
||||
g.SetProcessArgs(infraCtrCommand)
|
||||
|
||||
logrus.Debugf("Using %q as infra container command", infraCtrCommand)
|
||||
|
||||
mapopt, err := util.ParseIDMapping(namespaces.UsernsMode(p.config.InfraContainer.Userns.String()), []string{}, []string{}, "", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := specgen.SetupUserNS(mapopt, p.config.InfraContainer.Userns, &g)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user != "" {
|
||||
options = append(options, WithUser(user))
|
||||
}
|
||||
|
||||
g.RemoveMount("/dev/shm")
|
||||
if isRootless {
|
||||
g.RemoveMount("/dev/pts")
|
||||
|
@ -210,14 +238,15 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
|
|||
options = append(options, WithRootFSFromImage(imgID, imgName, rawImageName))
|
||||
options = append(options, WithName(containerName))
|
||||
options = append(options, withIsInfra())
|
||||
options = append(options, WithIDMappings(*mapopt))
|
||||
if len(p.config.InfraContainer.ConmonPidFile) > 0 {
|
||||
options = append(options, WithConmonPidFile(p.config.InfraContainer.ConmonPidFile))
|
||||
}
|
||||
newRes := new(spec.LinuxResources)
|
||||
newRes.CPU = new(spec.LinuxCPU)
|
||||
newRes.CPU = p.ResourceLim().CPU
|
||||
|
||||
g.Config.Linux.Resources.CPU = newRes.CPU
|
||||
|
||||
return r.newContainer(ctx, g.Config, options...)
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,12 @@ func PodCreate(w http.ResponseWriter, r *http.Request) {
|
|||
utils.Error(w, "failed to decode specgen", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen"))
|
||||
return
|
||||
}
|
||||
// parse userns so we get the valid default value of userns
|
||||
psg.Userns, err = specgen.ParseUserNamespace(psg.Userns.String())
|
||||
if err != nil {
|
||||
utils.Error(w, "failed to parse userns", http.StatusInternalServerError, errors.Wrap(err, "failed to parse userns"))
|
||||
return
|
||||
}
|
||||
pod, err := generate.MakePod(&psg, runtime)
|
||||
if err != nil {
|
||||
httpCode := http.StatusInternalServerError
|
||||
|
|
|
@ -122,6 +122,7 @@ type PodCreateOptions struct {
|
|||
Pid string
|
||||
Cpus float64
|
||||
CpusetCpus string
|
||||
Userns specgen.Namespace
|
||||
}
|
||||
|
||||
type PodCreateReport struct {
|
||||
|
@ -217,6 +218,7 @@ func (p *PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) error {
|
|||
s.CPUQuota = *cpuDat.Quota
|
||||
}
|
||||
}
|
||||
s.Userns = p.Userns
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -303,6 +303,8 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
|
|||
if opts.NetNSIsHost {
|
||||
s.NetNS.NSMode = specgen.Host
|
||||
}
|
||||
// Always set the userns to host since k8s doesn't have support for userns yet
|
||||
s.UserNS.NSMode = specgen.Host
|
||||
|
||||
// Add labels that come from kube
|
||||
if len(s.Labels) == 0 {
|
||||
|
|
|
@ -175,6 +175,11 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.
|
|||
if pod == nil || infraCtr == nil {
|
||||
return nil, errNoInfra
|
||||
}
|
||||
// Inherit the user from the infra container if it is set and --user has not
|
||||
// been set explicitly
|
||||
if infraCtr.User() != "" && s.User == "" {
|
||||
toReturn = append(toReturn, libpod.WithUser(infraCtr.User()))
|
||||
}
|
||||
toReturn = append(toReturn, libpod.WithUserNSFrom(infraCtr))
|
||||
case specgen.FromContainer:
|
||||
userCtr, err := rt.LookupContainer(s.UserNS.Value)
|
||||
|
@ -184,7 +189,10 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.
|
|||
toReturn = append(toReturn, libpod.WithUserNSFrom(userCtr))
|
||||
}
|
||||
|
||||
if s.IDMappings != nil {
|
||||
// This wipes the UserNS settings that get set from the infra container
|
||||
// when we are inheritting from the pod. So only apply this if the container
|
||||
// is not being created in a pod.
|
||||
if s.IDMappings != nil && pod == nil {
|
||||
toReturn = append(toReturn, libpod.WithIDMappings(*s.IDMappings))
|
||||
}
|
||||
if s.User != "" {
|
||||
|
@ -379,46 +387,8 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt
|
|||
}
|
||||
|
||||
// User
|
||||
switch s.UserNS.NSMode {
|
||||
case specgen.Path:
|
||||
if _, err := os.Stat(s.UserNS.Value); err != nil {
|
||||
return errors.Wrap(err, "cannot find specified user namespace path")
|
||||
}
|
||||
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), s.UserNS.Value); 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))
|
||||
case specgen.Host:
|
||||
if err := g.RemoveLinuxNamespace(string(spec.UserNamespace)); err != nil {
|
||||
return err
|
||||
}
|
||||
case specgen.KeepID:
|
||||
var (
|
||||
err error
|
||||
uid, gid int
|
||||
)
|
||||
s.IDMappings, uid, gid, err = util.GetKeepIDMapping()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.SetProcessUID(uint32(uid))
|
||||
g.SetProcessGID(uint32(gid))
|
||||
fallthrough
|
||||
case specgen.Private:
|
||||
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
|
||||
return err
|
||||
}
|
||||
if s.IDMappings == nil || (len(s.IDMappings.UIDMap) == 0 && len(s.IDMappings.GIDMap) == 0) {
|
||||
return errors.Errorf("must provide at least one UID or GID mapping to configure a user namespace")
|
||||
}
|
||||
for _, uidmap := range s.IDMappings.UIDMap {
|
||||
g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
|
||||
}
|
||||
for _, gidmap := range s.IDMappings.GIDMap {
|
||||
g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
|
||||
}
|
||||
if _, err := specgen.SetupUserNS(s.IDMappings, s.UserNS, g); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Cgroup
|
||||
|
@ -474,7 +444,7 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt
|
|||
// GetNamespaceOptions transforms a slice of kernel namespaces
|
||||
// into a slice of pod create options. Currently, not all
|
||||
// kernel namespaces are supported, and they will be returned in an error
|
||||
func GetNamespaceOptions(ns []string) ([]libpod.PodCreateOption, error) {
|
||||
func GetNamespaceOptions(ns []string, netnsIsHost bool) ([]libpod.PodCreateOption, error) {
|
||||
var options []libpod.PodCreateOption
|
||||
var erroredOptions []libpod.PodCreateOption
|
||||
if ns == nil {
|
||||
|
@ -486,7 +456,10 @@ func GetNamespaceOptions(ns []string) ([]libpod.PodCreateOption, error) {
|
|||
case "cgroup":
|
||||
options = append(options, libpod.WithPodCgroups())
|
||||
case "net":
|
||||
options = append(options, libpod.WithPodNet())
|
||||
// share the netns setting with other containers in the pod only when it is not set to host
|
||||
if !netnsIsHost {
|
||||
options = append(options, libpod.WithPodNet())
|
||||
}
|
||||
case "mnt":
|
||||
return erroredOptions, errors.Errorf("Mount sharing functionality not supported on pod level")
|
||||
case "pid":
|
||||
|
|
|
@ -27,11 +27,16 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod
|
|||
)
|
||||
if !p.NoInfra {
|
||||
options = append(options, libpod.WithInfraContainer())
|
||||
nsOptions, err := GetNamespaceOptions(p.SharedNamespaces)
|
||||
nsOptions, err := GetNamespaceOptions(p.SharedNamespaces, p.NetNS.IsHost())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options = append(options, nsOptions...)
|
||||
// Use pod user and infra userns only when --userns is not set to host
|
||||
if !p.Userns.IsHost() {
|
||||
options = append(options, libpod.WithPodUser())
|
||||
options = append(options, libpod.WithPodUserns(p.Userns))
|
||||
}
|
||||
|
||||
// Make our exit command
|
||||
storageConfig := rt.StorageConfig()
|
||||
|
@ -154,5 +159,6 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod
|
|||
if len(p.InfraConmonPidFile) > 0 {
|
||||
options = append(options, libpod.WithInfraConmonPidFile(p.InfraConmonPidFile))
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
package specgen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/podman/v3/pkg/cgroups"
|
||||
"github.com/containers/podman/v3/pkg/rootless"
|
||||
"github.com/containers/podman/v3/pkg/util"
|
||||
"github.com/containers/storage"
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/runtime-tools/generate"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -103,6 +109,13 @@ func (n *Namespace) IsKeepID() bool {
|
|||
return n.NSMode == KeepID
|
||||
}
|
||||
|
||||
func (n *Namespace) String() string {
|
||||
if n.Value != "" {
|
||||
return fmt.Sprintf("%s:%s", n.NSMode, n.Value)
|
||||
}
|
||||
return string(n.NSMode)
|
||||
}
|
||||
|
||||
func validateUserNS(n *Namespace) error {
|
||||
if n == nil {
|
||||
return nil
|
||||
|
@ -323,3 +336,48 @@ func ParseNetworkString(network string) (Namespace, []string, map[string][]strin
|
|||
}
|
||||
return ns, cniNets, networkOptions, nil
|
||||
}
|
||||
|
||||
func SetupUserNS(idmappings *storage.IDMappingOptions, userns Namespace, g *generate.Generator) (string, error) {
|
||||
// User
|
||||
var user string
|
||||
switch userns.NSMode {
|
||||
case Path:
|
||||
if _, err := os.Stat(userns.Value); err != nil {
|
||||
return user, errors.Wrap(err, "cannot find specified user namespace path")
|
||||
}
|
||||
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), userns.Value); err != nil {
|
||||
return user, 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))
|
||||
case Host:
|
||||
if err := g.RemoveLinuxNamespace(string(spec.UserNamespace)); err != nil {
|
||||
return user, err
|
||||
}
|
||||
case KeepID:
|
||||
mappings, uid, gid, err := util.GetKeepIDMapping()
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
idmappings = mappings
|
||||
g.SetProcessUID(uint32(uid))
|
||||
g.SetProcessGID(uint32(gid))
|
||||
user = fmt.Sprintf("%d:%d", uid, gid)
|
||||
fallthrough
|
||||
case Private:
|
||||
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
|
||||
return user, err
|
||||
}
|
||||
if idmappings == nil || (len(idmappings.UIDMap) == 0 && len(idmappings.GIDMap) == 0) {
|
||||
return user, errors.Errorf("must provide at least one UID or GID mapping to configure a user namespace")
|
||||
}
|
||||
for _, uidmap := range idmappings.UIDMap {
|
||||
g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
|
||||
}
|
||||
for _, gidmap := range idmappings.GIDMap {
|
||||
g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
|
||||
}
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
|
|
@ -67,6 +67,10 @@ type PodBasicConfig struct {
|
|||
// Optional (defaults to private if unset). This sets the PID namespace of the infra container
|
||||
// This configuration will then be shared with the entire pod if PID namespace sharing is enabled via --share
|
||||
Pid Namespace `json:"pid,omitempty:"`
|
||||
// Userns is used to indicate which kind of Usernamespace to enter.
|
||||
// Any containers created within the pod will inherit the pod's userns settings.
|
||||
// Optional
|
||||
Userns Namespace `json:"userns,omitempty"`
|
||||
}
|
||||
|
||||
// PodNetworkConfig contains networking configuration for a pod.
|
||||
|
|
|
@ -1114,7 +1114,7 @@ var _ = Describe("Podman play kube", func() {
|
|||
})
|
||||
|
||||
It("podman play kube should share ipc,net,uts when shareProcessNamespace is set", func() {
|
||||
SkipIfRootless("Requires root priviledges for sharing few namespaces")
|
||||
SkipIfRootless("Requires root privileges for sharing few namespaces")
|
||||
err := writeYaml(sharedNamespacePodYaml, kubeYaml)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -621,4 +622,223 @@ ENTRYPOINT ["sleep","99999"]
|
|||
Expect(podCreate).Should(ExitWithError())
|
||||
|
||||
})
|
||||
|
||||
It("podman pod create with --userns=keep-id", func() {
|
||||
if os.Geteuid() == 0 {
|
||||
Skip("Test only runs without root")
|
||||
}
|
||||
|
||||
podName := "testPod"
|
||||
podCreate := podmanTest.Podman([]string{"pod", "create", "--userns", "keep-id", "--name", podName})
|
||||
podCreate.WaitWithDefaultTimeout()
|
||||
Expect(podCreate).Should(Exit(0))
|
||||
|
||||
session := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "id", "-u"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
uid := fmt.Sprintf("%d", os.Geteuid())
|
||||
ok, _ := session.GrepString(uid)
|
||||
Expect(ok).To(BeTrue())
|
||||
|
||||
// Check passwd
|
||||
session = podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "id", "-un"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
u, err := user.Current()
|
||||
Expect(err).To(BeNil())
|
||||
ok, _ = session.GrepString(u.Name)
|
||||
Expect(ok).To(BeTrue())
|
||||
|
||||
// root owns /usr
|
||||
session = podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "stat", "-c%u", "/usr"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).To(Equal("0"))
|
||||
|
||||
// fail if --pod and --userns set together
|
||||
session = podmanTest.Podman([]string{"run", "--pod", podName, "--userns", "keep-id", ALPINE, "id", "-u"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(125))
|
||||
})
|
||||
|
||||
It("podman pod create with --userns=keep-id can add users", func() {
|
||||
if os.Geteuid() == 0 {
|
||||
Skip("Test only runs without root")
|
||||
}
|
||||
|
||||
podName := "testPod"
|
||||
podCreate := podmanTest.Podman([]string{"pod", "create", "--userns", "keep-id", "--name", podName})
|
||||
podCreate.WaitWithDefaultTimeout()
|
||||
Expect(podCreate).Should(Exit(0))
|
||||
|
||||
ctrName := "ctr-name"
|
||||
session := podmanTest.Podman([]string{"run", "--pod", podName, "-d", "--stop-signal", "9", "--name", ctrName, fedoraMinimal, "sleep", "600"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
|
||||
// container inside pod inherits user form infra container if --user is not set
|
||||
// etc/passwd entry will look like 1000:*:1000:1000:container user:/:/bin/sh
|
||||
exec1 := podmanTest.Podman([]string{"exec", ctrName, "cat", "/etc/passwd"})
|
||||
exec1.WaitWithDefaultTimeout()
|
||||
Expect(exec1).Should(Exit(0))
|
||||
Expect(exec1.OutputToString()).To(ContainSubstring("container"))
|
||||
|
||||
exec2 := podmanTest.Podman([]string{"exec", ctrName, "useradd", "testuser"})
|
||||
exec2.WaitWithDefaultTimeout()
|
||||
Expect(exec2).Should(Exit(0))
|
||||
|
||||
exec3 := podmanTest.Podman([]string{"exec", ctrName, "cat", "/etc/passwd"})
|
||||
exec3.WaitWithDefaultTimeout()
|
||||
Expect(exec3).Should(Exit(0))
|
||||
Expect(exec3.OutputToString()).To(ContainSubstring("testuser"))
|
||||
})
|
||||
|
||||
It("podman pod create with --userns=auto", func() {
|
||||
u, err := user.Current()
|
||||
Expect(err).To(BeNil())
|
||||
name := u.Name
|
||||
if name == "root" {
|
||||
name = "containers"
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile("/etc/subuid")
|
||||
if err != nil {
|
||||
Skip("cannot read /etc/subuid")
|
||||
}
|
||||
if !strings.Contains(string(content), name) {
|
||||
Skip("cannot find mappings for the current user")
|
||||
}
|
||||
|
||||
m := make(map[string]string)
|
||||
for i := 0; i < 5; i++ {
|
||||
podName := "testPod" + strconv.Itoa(i)
|
||||
podCreate := podmanTest.Podman([]string{"pod", "create", "--userns=auto", "--name", podName})
|
||||
podCreate.WaitWithDefaultTimeout()
|
||||
Expect(podCreate).Should(Exit(0))
|
||||
|
||||
session := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/uid_map"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
l := session.OutputToString()
|
||||
Expect(strings.Contains(l, "1024")).To(BeTrue())
|
||||
m[l] = l
|
||||
}
|
||||
// check for no duplicates
|
||||
Expect(len(m)).To(Equal(5))
|
||||
})
|
||||
|
||||
It("podman pod create --userns=auto:size=%d", func() {
|
||||
u, err := user.Current()
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
name := u.Name
|
||||
if name == "root" {
|
||||
name = "containers"
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile("/etc/subuid")
|
||||
if err != nil {
|
||||
Skip("cannot read /etc/subuid")
|
||||
}
|
||||
if !strings.Contains(string(content), name) {
|
||||
Skip("cannot find mappings for the current user")
|
||||
}
|
||||
|
||||
podName := "testPod"
|
||||
podCreate := podmanTest.Podman([]string{"pod", "create", "--userns=auto:size=500", "--name", podName})
|
||||
podCreate.WaitWithDefaultTimeout()
|
||||
Expect(podCreate).Should(Exit(0))
|
||||
session := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/uid_map"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
ok, _ := session.GrepString("500")
|
||||
|
||||
podName = "testPod-1"
|
||||
podCreate = podmanTest.Podman([]string{"pod", "create", "--userns=auto:size=3000", "--name", podName})
|
||||
podCreate.WaitWithDefaultTimeout()
|
||||
Expect(podCreate).Should(Exit(0))
|
||||
session = podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/uid_map"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
ok, _ = session.GrepString("3000")
|
||||
|
||||
Expect(ok).To(BeTrue())
|
||||
})
|
||||
|
||||
It("podman pod create --userns=auto:uidmapping=", func() {
|
||||
u, err := user.Current()
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
name := u.Name
|
||||
if name == "root" {
|
||||
name = "containers"
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile("/etc/subuid")
|
||||
if err != nil {
|
||||
Skip("cannot read /etc/subuid")
|
||||
}
|
||||
if !strings.Contains(string(content), name) {
|
||||
Skip("cannot find mappings for the current user")
|
||||
}
|
||||
|
||||
podName := "testPod"
|
||||
podCreate := podmanTest.Podman([]string{"pod", "create", "--userns=auto:uidmapping=0:0:1", "--name", podName})
|
||||
podCreate.WaitWithDefaultTimeout()
|
||||
Expect(podCreate).Should(Exit(0))
|
||||
session := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/uid_map"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
output := session.OutputToString()
|
||||
Expect(output).To(MatchRegexp("\\s0\\s0\\s1"))
|
||||
|
||||
podName = "testPod-1"
|
||||
podCreate = podmanTest.Podman([]string{"pod", "create", "--userns=auto:size=8192,uidmapping=0:0:1", "--name", podName})
|
||||
podCreate.WaitWithDefaultTimeout()
|
||||
Expect(podCreate).Should(Exit(0))
|
||||
session = podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/uid_map"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
ok, _ := session.GrepString("8191")
|
||||
Expect(ok).To(BeTrue())
|
||||
})
|
||||
|
||||
It("podman pod create --userns=auto:gidmapping=", func() {
|
||||
u, err := user.Current()
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
name := u.Name
|
||||
if name == "root" {
|
||||
name = "containers"
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile("/etc/subuid")
|
||||
if err != nil {
|
||||
Skip("cannot read /etc/subuid")
|
||||
}
|
||||
if !strings.Contains(string(content), name) {
|
||||
Skip("cannot find mappings for the current user")
|
||||
}
|
||||
|
||||
podName := "testPod"
|
||||
podCreate := podmanTest.Podman([]string{"pod", "create", "--userns=auto:gidmapping=0:0:1", "--name", podName})
|
||||
podCreate.WaitWithDefaultTimeout()
|
||||
Expect(podCreate).Should(Exit(0))
|
||||
session := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/gid_map"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
output := session.OutputToString()
|
||||
Expect(output).To(MatchRegexp("\\s0\\s0\\s1"))
|
||||
|
||||
podName = "testPod-1"
|
||||
podCreate = podmanTest.Podman([]string{"pod", "create", "--userns=auto:size=8192,gidmapping=0:0:1", "--name", podName})
|
||||
podCreate.WaitWithDefaultTimeout()
|
||||
Expect(podCreate).Should(Exit(0))
|
||||
session = podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/gid_map"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
ok, _ := session.GrepString("8191")
|
||||
Expect(ok).To(BeTrue())
|
||||
})
|
||||
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue