mirror of
https://github.com/containers/podman.git
synced 2025-06-27 05:26:50 +08:00
Merge pull request #14140 from giuseppe/play-kube-userns
kube: add support for --userns=[auto|host]
This commit is contained in:
@ -98,6 +98,12 @@ func init() {
|
||||
)
|
||||
_ = kubeCmd.RegisterFlagCompletionFunc(logOptFlagName, common.AutocompleteLogOpt)
|
||||
|
||||
usernsFlagName := "userns"
|
||||
flags.StringVar(&kubeOptions.Userns, usernsFlagName, os.Getenv("PODMAN_USERNS"),
|
||||
"User namespace to use",
|
||||
)
|
||||
_ = kubeCmd.RegisterFlagCompletionFunc(usernsFlagName, common.AutocompleteUserNamespace)
|
||||
|
||||
flags.BoolVar(&kubeOptions.NoHosts, "no-hosts", false, "Do not create /etc/hosts within the pod's containers, instead use the version from the image")
|
||||
flags.BoolVarP(&kubeOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
|
||||
flags.BoolVar(&kubeOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
|
||||
|
@ -243,6 +243,45 @@ Require HTTPS and verify certificates when contacting registries (default: true)
|
||||
then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified,
|
||||
TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf.
|
||||
|
||||
#### **--userns**=*mode*
|
||||
|
||||
Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value ("") means user namespaces are disabled unless an explicit mapping is set with the **--uidmap** and **--gidmap** options.
|
||||
|
||||
Rootless user --userns=Key mappings:
|
||||
|
||||
Key | Host User | Container User
|
||||
----------|---------------|---------------------
|
||||
"" |$UID |0 (Default User account mapped to root user in container.)
|
||||
keep-id |$UID |$UID (Map user account to same UID within container.)
|
||||
auto |$UID | nil (Host User UID is not mapped into container.)
|
||||
nomap |$UID | nil (Host User UID is not mapped into container.)
|
||||
|
||||
Valid _mode_ values are:
|
||||
|
||||
**auto**[:_OPTIONS,..._]: automatically create a unique user namespace.
|
||||
|
||||
The `--userns=auto` flag, requires that the user name `containers` and a range of subordinate user ids that the Podman container is allowed to use be specified in the /etc/subuid and /etc/subgid files.
|
||||
|
||||
Example: `containers:2147483647:2147483648`.
|
||||
|
||||
Podman allocates unique ranges of UIDs and GIDs from the `containers` subordinate user ids. The size of the ranges is based on the number of UIDs required in the image. The number of UIDs and GIDs can be overridden with the `size` option. The `auto` options currently does not work in rootless mode
|
||||
|
||||
Valid `auto` options:
|
||||
|
||||
- *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.
|
||||
|
||||
**container:**_id_: join the user namespace of the specified container.
|
||||
|
||||
**host**: create a new namespace for the container.
|
||||
|
||||
**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.
|
||||
|
||||
**nomap**: creates a user namespace where the current rootless user's UID:GID are not mapped into the container. This option is ignored for containers created by the root user.
|
||||
|
||||
**ns:**_namespace_: run the pod in the given existing user namespace.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
Recreate the pod and containers as described in a file called `demo.yml`
|
||||
|
@ -43,4 +43,6 @@ type KubeOptions struct {
|
||||
LogOptions *[]string
|
||||
// Start - don't start the pod if false
|
||||
Start *bool
|
||||
// Userns - define the user namespace to use.
|
||||
Userns *string
|
||||
}
|
||||
|
@ -272,3 +272,18 @@ func (o *KubeOptions) GetStart() bool {
|
||||
}
|
||||
return *o.Start
|
||||
}
|
||||
|
||||
// WithUserns set field Userns to given value
|
||||
func (o *KubeOptions) WithUserns(value string) *KubeOptions {
|
||||
o.Userns = &value
|
||||
return o
|
||||
}
|
||||
|
||||
// GetUserns returns value of field Userns
|
||||
func (o *KubeOptions) GetUserns() string {
|
||||
if o.Userns == nil {
|
||||
var z string
|
||||
return z
|
||||
}
|
||||
return *o.Userns
|
||||
}
|
||||
|
@ -54,6 +54,8 @@ type PlayKubeOptions struct {
|
||||
LogOptions []string
|
||||
// Start - don't start the pod if false
|
||||
Start types.OptionalBool
|
||||
// Userns - define the user namespace to use.
|
||||
Userns string
|
||||
}
|
||||
|
||||
// PlayKubePod represents a single pod and associated containers created by play kube
|
||||
|
@ -222,6 +222,16 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
||||
podOpt.Net.NetworkOptions = netOpts
|
||||
}
|
||||
|
||||
if options.Userns == "" {
|
||||
options.Userns = "host"
|
||||
}
|
||||
|
||||
// Validate the userns modes supported.
|
||||
podOpt.Userns, err = specgen.ParseUserNamespace(options.Userns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// FIXME This is very hard to support properly with a good ux
|
||||
if len(options.StaticIPs) > *ipIndex {
|
||||
if !podOpt.Net.Network.IsBridge() {
|
||||
@ -352,6 +362,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
||||
infraImage := util.DefaultContainerConfig().Engine.InfraImage
|
||||
infraOptions := entities.NewInfraContainerCreateOptions()
|
||||
infraOptions.Hostname = podSpec.PodSpecGen.PodBasicConfig.Hostname
|
||||
infraOptions.UserNS = options.Userns
|
||||
podSpec.PodSpecGen.InfraImage = infraImage
|
||||
podSpec.PodSpecGen.NoInfra = false
|
||||
podSpec.PodSpecGen.InfraContainerSpec = specgen.NewSpecGenerator(infraImage, false)
|
||||
@ -428,6 +439,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
||||
RestartPolicy: ctrRestartPolicy,
|
||||
SeccompPaths: seccompPaths,
|
||||
SecretsManager: secretsManager,
|
||||
UserNSIsHost: p.Userns.IsHost(),
|
||||
Volumes: volumes,
|
||||
}
|
||||
specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
|
||||
@ -476,6 +488,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
||||
RestartPolicy: ctrRestartPolicy,
|
||||
SeccompPaths: seccompPaths,
|
||||
SecretsManager: secretsManager,
|
||||
UserNSIsHost: p.Userns.IsHost(),
|
||||
Volumes: volumes,
|
||||
}
|
||||
specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
|
||||
|
@ -20,7 +20,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, opts en
|
||||
if opts.Annotations != nil {
|
||||
options.WithAnnotations(opts.Annotations)
|
||||
}
|
||||
options.WithNoHosts(opts.NoHosts)
|
||||
options.WithNoHosts(opts.NoHosts).WithUserns(opts.Userns)
|
||||
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
|
||||
options.WithSkipTLSVerify(s == types.OptionalBoolTrue)
|
||||
}
|
||||
|
@ -120,6 +120,8 @@ type CtrSpecGenOptions struct {
|
||||
RestartPolicy string
|
||||
// NetNSIsHost tells the container to use the host netns
|
||||
NetNSIsHost bool
|
||||
// UserNSIsHost tells the container to use the host userns
|
||||
UserNSIsHost bool
|
||||
// SecretManager to access the secrets
|
||||
SecretsManager *secrets.SecretsManager
|
||||
// LogDriver which should be used for the container
|
||||
@ -389,8 +391,9 @@ 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
|
||||
if opts.UserNSIsHost {
|
||||
s.UserNS.NSMode = specgen.Host
|
||||
}
|
||||
|
||||
// Add labels that come from kube
|
||||
if len(s.Labels) == 0 {
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -3633,6 +3634,55 @@ ENV OPENJ9_JAVA_OPTIONS=%q
|
||||
inspect.WaitWithDefaultTimeout()
|
||||
Expect(start).Should(Exit(0))
|
||||
Expect((inspect.InspectContainerToJSON()[0]).HostConfig.LogConfig.Tag).To(Equal("{{.ImageName}}"))
|
||||
})
|
||||
|
||||
// Check that --userns=auto creates a user namespace
|
||||
It("podman play kube --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")
|
||||
}
|
||||
|
||||
initialUsernsConfig, err := ioutil.ReadFile("/proc/self/uid_map")
|
||||
Expect(err).To(BeNil())
|
||||
if os.Geteuid() != 0 {
|
||||
unshare := podmanTest.Podman([]string{"unshare", "cat", "/proc/self/uid_map"})
|
||||
unshare.WaitWithDefaultTimeout()
|
||||
Expect(unshare).Should(Exit(0))
|
||||
initialUsernsConfig = unshare.Out.Contents()
|
||||
}
|
||||
|
||||
pod := getPod()
|
||||
err = generateKubeYaml("pod", pod, kubeYaml)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
|
||||
kube.WaitWithDefaultTimeout()
|
||||
Expect(kube).Should(Exit(0))
|
||||
|
||||
usernsInCtr := podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "cat", "/proc/self/uid_map"})
|
||||
usernsInCtr.WaitWithDefaultTimeout()
|
||||
Expect(usernsInCtr).Should(Exit(0))
|
||||
// the conversion to string is needed for better error messages
|
||||
Expect(string(usernsInCtr.Out.Contents())).To(Equal(string(initialUsernsConfig)))
|
||||
|
||||
// PodmanNoCache is a workaround for https://github.com/containers/storage/issues/1232
|
||||
kube = podmanTest.PodmanNoCache([]string{"play", "kube", "--replace", "--userns=auto", kubeYaml})
|
||||
kube.WaitWithDefaultTimeout()
|
||||
Expect(kube).Should(Exit(0))
|
||||
|
||||
usernsInCtr = podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "cat", "/proc/self/uid_map"})
|
||||
usernsInCtr.WaitWithDefaultTimeout()
|
||||
Expect(usernsInCtr).Should(Exit(0))
|
||||
Expect(string(usernsInCtr.Out.Contents())).To(Not(Equal(string(initialUsernsConfig))))
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user