mirror of
https://github.com/containers/podman.git
synced 2025-06-27 21:50:18 +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)
|
_ = 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.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.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")
|
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,
|
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.
|
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
|
## EXAMPLES
|
||||||
|
|
||||||
Recreate the pod and containers as described in a file called `demo.yml`
|
Recreate the pod and containers as described in a file called `demo.yml`
|
||||||
|
@ -43,4 +43,6 @@ type KubeOptions struct {
|
|||||||
LogOptions *[]string
|
LogOptions *[]string
|
||||||
// Start - don't start the pod if false
|
// Start - don't start the pod if false
|
||||||
Start *bool
|
Start *bool
|
||||||
|
// Userns - define the user namespace to use.
|
||||||
|
Userns *string
|
||||||
}
|
}
|
||||||
|
@ -272,3 +272,18 @@ func (o *KubeOptions) GetStart() bool {
|
|||||||
}
|
}
|
||||||
return *o.Start
|
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
|
LogOptions []string
|
||||||
// Start - don't start the pod if false
|
// Start - don't start the pod if false
|
||||||
Start types.OptionalBool
|
Start types.OptionalBool
|
||||||
|
// Userns - define the user namespace to use.
|
||||||
|
Userns string
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlayKubePod represents a single pod and associated containers created by play kube
|
// 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
|
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
|
// FIXME This is very hard to support properly with a good ux
|
||||||
if len(options.StaticIPs) > *ipIndex {
|
if len(options.StaticIPs) > *ipIndex {
|
||||||
if !podOpt.Net.Network.IsBridge() {
|
if !podOpt.Net.Network.IsBridge() {
|
||||||
@ -352,6 +362,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
|||||||
infraImage := util.DefaultContainerConfig().Engine.InfraImage
|
infraImage := util.DefaultContainerConfig().Engine.InfraImage
|
||||||
infraOptions := entities.NewInfraContainerCreateOptions()
|
infraOptions := entities.NewInfraContainerCreateOptions()
|
||||||
infraOptions.Hostname = podSpec.PodSpecGen.PodBasicConfig.Hostname
|
infraOptions.Hostname = podSpec.PodSpecGen.PodBasicConfig.Hostname
|
||||||
|
infraOptions.UserNS = options.Userns
|
||||||
podSpec.PodSpecGen.InfraImage = infraImage
|
podSpec.PodSpecGen.InfraImage = infraImage
|
||||||
podSpec.PodSpecGen.NoInfra = false
|
podSpec.PodSpecGen.NoInfra = false
|
||||||
podSpec.PodSpecGen.InfraContainerSpec = specgen.NewSpecGenerator(infraImage, false)
|
podSpec.PodSpecGen.InfraContainerSpec = specgen.NewSpecGenerator(infraImage, false)
|
||||||
@ -428,6 +439,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
|||||||
RestartPolicy: ctrRestartPolicy,
|
RestartPolicy: ctrRestartPolicy,
|
||||||
SeccompPaths: seccompPaths,
|
SeccompPaths: seccompPaths,
|
||||||
SecretsManager: secretsManager,
|
SecretsManager: secretsManager,
|
||||||
|
UserNSIsHost: p.Userns.IsHost(),
|
||||||
Volumes: volumes,
|
Volumes: volumes,
|
||||||
}
|
}
|
||||||
specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
|
specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
|
||||||
@ -476,6 +488,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
|||||||
RestartPolicy: ctrRestartPolicy,
|
RestartPolicy: ctrRestartPolicy,
|
||||||
SeccompPaths: seccompPaths,
|
SeccompPaths: seccompPaths,
|
||||||
SecretsManager: secretsManager,
|
SecretsManager: secretsManager,
|
||||||
|
UserNSIsHost: p.Userns.IsHost(),
|
||||||
Volumes: volumes,
|
Volumes: volumes,
|
||||||
}
|
}
|
||||||
specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
|
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 {
|
if opts.Annotations != nil {
|
||||||
options.WithAnnotations(opts.Annotations)
|
options.WithAnnotations(opts.Annotations)
|
||||||
}
|
}
|
||||||
options.WithNoHosts(opts.NoHosts)
|
options.WithNoHosts(opts.NoHosts).WithUserns(opts.Userns)
|
||||||
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
|
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
|
||||||
options.WithSkipTLSVerify(s == types.OptionalBoolTrue)
|
options.WithSkipTLSVerify(s == types.OptionalBoolTrue)
|
||||||
}
|
}
|
||||||
|
@ -120,6 +120,8 @@ type CtrSpecGenOptions struct {
|
|||||||
RestartPolicy string
|
RestartPolicy string
|
||||||
// NetNSIsHost tells the container to use the host netns
|
// NetNSIsHost tells the container to use the host netns
|
||||||
NetNSIsHost bool
|
NetNSIsHost bool
|
||||||
|
// UserNSIsHost tells the container to use the host userns
|
||||||
|
UserNSIsHost bool
|
||||||
// SecretManager to access the secrets
|
// SecretManager to access the secrets
|
||||||
SecretsManager *secrets.SecretsManager
|
SecretsManager *secrets.SecretsManager
|
||||||
// LogDriver which should be used for the container
|
// LogDriver which should be used for the container
|
||||||
@ -389,8 +391,9 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
|
|||||||
if opts.NetNSIsHost {
|
if opts.NetNSIsHost {
|
||||||
s.NetNS.NSMode = specgen.Host
|
s.NetNS.NSMode = specgen.Host
|
||||||
}
|
}
|
||||||
// Always set the userns to host since k8s doesn't have support for userns yet
|
if opts.UserNSIsHost {
|
||||||
s.UserNS.NSMode = specgen.Host
|
s.UserNS.NSMode = specgen.Host
|
||||||
|
}
|
||||||
|
|
||||||
// Add labels that come from kube
|
// Add labels that come from kube
|
||||||
if len(s.Labels) == 0 {
|
if len(s.Labels) == 0 {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -3633,6 +3634,55 @@ ENV OPENJ9_JAVA_OPTIONS=%q
|
|||||||
inspect.WaitWithDefaultTimeout()
|
inspect.WaitWithDefaultTimeout()
|
||||||
Expect(start).Should(Exit(0))
|
Expect(start).Should(Exit(0))
|
||||||
Expect((inspect.InspectContainerToJSON()[0]).HostConfig.LogConfig.Tag).To(Equal("{{.ImageName}}"))
|
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