mirror of
https://github.com/containers/podman.git
synced 2025-05-17 15:18:43 +08:00
Add support for --userns=nomap
From a security point of view, it would be nice to be able to map a rootless usernamespace that does not use your own UID within the container. This would add protection against a hostile process escapping the container and reading content in your homedir. Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
@ -756,7 +756,7 @@ func AutocompleteNamespace(cmd *cobra.Command, args []string, toComplete string)
|
||||
// -> same as AutocompleteNamespace with "auto", "keep-id" added
|
||||
func AutocompleteUserNamespace(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
results, directive := AutocompleteNamespace(cmd, args, toComplete)
|
||||
results = append(results, "auto", "keep-id")
|
||||
results = append(results, "auto", "keep-id", "nomap")
|
||||
return results, directive
|
||||
}
|
||||
|
||||
|
@ -1225,6 +1225,15 @@ Without this argument the command will be run as root in the container.
|
||||
|
||||
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.
|
||||
@ -1247,6 +1256,8 @@ Podman allocates unique ranges of UIDs and GIDs from the `containers` subordinat
|
||||
|
||||
**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 container in the given existing user namespace.
|
||||
|
||||
**private**: create a new namespace for the container.
|
||||
|
@ -308,15 +308,31 @@ several times to map different ranges.
|
||||
|
||||
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.
|
||||
|
||||
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 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.
|
||||
|
||||
- *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.
|
||||
|
||||
#### **--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*]
|
||||
|
||||
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, Podman
|
||||
|
@ -1290,6 +1290,15 @@ When a user namespace is not in use, the UID and GID used within the container a
|
||||
|
||||
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.
|
||||
@ -1299,6 +1308,7 @@ The `--userns=auto` flag, requires that the user name `containers` and a range o
|
||||
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 rootless option `--userns=keep-id` uses all the subuids and subgids of the user. Using `--userns=auto` when starting new containers will not work as long as any containers exist that were started with `--userns=keep-id`.
|
||||
|
||||
Valid `auto` options:
|
||||
@ -1313,10 +1323,11 @@ The rootless option `--userns=keep-id` uses all the subuids and subgids of the u
|
||||
|
||||
**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 container in the given existing user namespace.
|
||||
|
||||
**private**: create a new namespace for the container.
|
||||
|
||||
This option is incompatible with **--gidmap**, **--uidmap**, **--subuidname** and **--subgidname**.
|
||||
|
||||
#### **--uts**=*mode*
|
||||
|
@ -276,7 +276,9 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin
|
||||
if len(subUIDMap) > 0 || len(subGIDMap) > 0 {
|
||||
return nil, errors.New("cannot specify subuidmap or subgidmap with --userns=keep-id")
|
||||
}
|
||||
if rootless.IsRootless() {
|
||||
if !rootless.IsRootless() {
|
||||
return nil, errors.New("keep-id is only supported in rootless mode")
|
||||
}
|
||||
min := func(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
@ -315,7 +317,6 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin
|
||||
|
||||
options.HostUIDMapping = false
|
||||
options.HostGIDMapping = false
|
||||
}
|
||||
// Simply ignore the setting and do not setup an inner namespace for root as it is a no-op
|
||||
return &options, nil
|
||||
}
|
||||
|
@ -96,6 +96,11 @@ func (n UsernsMode) IsKeepID() bool {
|
||||
return n == "keep-id"
|
||||
}
|
||||
|
||||
// IsNoMap indicates whether container uses a mapping where the (uid, gid) on the host is not present in the namespace.
|
||||
func (n UsernsMode) IsNoMap() bool {
|
||||
return n == "nomap"
|
||||
}
|
||||
|
||||
// IsAuto indicates whether container uses the "auto" userns mode.
|
||||
func (n UsernsMode) IsAuto() bool {
|
||||
parts := strings.Split(string(n), ":")
|
||||
@ -158,7 +163,7 @@ func (n UsernsMode) IsPrivate() bool {
|
||||
func (n UsernsMode) Valid() bool {
|
||||
parts := strings.Split(string(n), ":")
|
||||
switch mode := parts[0]; mode {
|
||||
case "", privateType, hostType, "keep-id", nsType, "auto":
|
||||
case "", privateType, hostType, "keep-id", nsType, "auto", "nomap":
|
||||
case containerType:
|
||||
if len(parts) != 2 || parts[1] == "" {
|
||||
return false
|
||||
|
@ -165,7 +165,9 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.
|
||||
// User
|
||||
switch s.UserNS.NSMode {
|
||||
case specgen.KeepID:
|
||||
if rootless.IsRootless() {
|
||||
if !rootless.IsRootless() {
|
||||
return nil, errors.New("keep-id is only supported in rootless mode")
|
||||
}
|
||||
toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry())
|
||||
|
||||
// If user is not overridden, set user in the container
|
||||
@ -177,10 +179,6 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.
|
||||
}
|
||||
toReturn = append(toReturn, libpod.WithUser(fmt.Sprintf("%d:%d", uid, gid)))
|
||||
}
|
||||
} else {
|
||||
// keep-id as root doesn't need a user namespace
|
||||
s.UserNS.NSMode = specgen.Host
|
||||
}
|
||||
case specgen.FromPod:
|
||||
if pod == nil || infraCtr == nil {
|
||||
return nil, errNoInfra
|
||||
|
@ -55,6 +55,10 @@ const (
|
||||
// of the namespace itself.
|
||||
// Only used with the user namespace, invalid otherwise.
|
||||
KeepID NamespaceMode = "keep-id"
|
||||
// NoMap indicates a user namespace to keep the owner uid out
|
||||
// of the namespace itself.
|
||||
// Only used with the user namespace, invalid otherwise.
|
||||
NoMap NamespaceMode = "no-map"
|
||||
// Auto indicates to automatically create a user namespace.
|
||||
// Only used with the user namespace, invalid otherwise.
|
||||
Auto NamespaceMode = "auto"
|
||||
@ -121,6 +125,11 @@ func (n *Namespace) IsKeepID() bool {
|
||||
return n.NSMode == KeepID
|
||||
}
|
||||
|
||||
// IsNoMap indicates the namespace is NoMap
|
||||
func (n *Namespace) IsNoMap() bool {
|
||||
return n.NSMode == NoMap
|
||||
}
|
||||
|
||||
func (n *Namespace) String() string {
|
||||
if n.Value != "" {
|
||||
return fmt.Sprintf("%s:%s", n.NSMode, n.Value)
|
||||
@ -133,7 +142,7 @@ func validateUserNS(n *Namespace) error {
|
||||
return nil
|
||||
}
|
||||
switch n.NSMode {
|
||||
case Auto, KeepID:
|
||||
case Auto, KeepID, NoMap:
|
||||
return nil
|
||||
}
|
||||
return n.validate()
|
||||
@ -299,6 +308,9 @@ func ParseUserNamespace(ns string) (Namespace, error) {
|
||||
case ns == "keep-id":
|
||||
toReturn.NSMode = KeepID
|
||||
return toReturn, nil
|
||||
case ns == "nomap":
|
||||
toReturn.NSMode = NoMap
|
||||
return toReturn, nil
|
||||
case ns == "":
|
||||
toReturn.NSMode = Host
|
||||
return toReturn, nil
|
||||
@ -548,13 +560,35 @@ func SetupUserNS(idmappings *storage.IDMappingOptions, userns Namespace, g *gene
|
||||
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 {
|
||||
if err := privateUserNamespace(idmappings, g); err != nil {
|
||||
return user, err
|
||||
}
|
||||
case NoMap:
|
||||
mappings, uid, gid, err := util.GetNoMapMapping()
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
idmappings = mappings
|
||||
g.SetProcessUID(uint32(uid))
|
||||
g.SetProcessGID(uint32(gid))
|
||||
user = fmt.Sprintf("%d:%d", uid, gid)
|
||||
if err := privateUserNamespace(idmappings, g); err != nil {
|
||||
return user, err
|
||||
}
|
||||
case Private:
|
||||
if err := privateUserNamespace(idmappings, g); err != nil {
|
||||
return user, err
|
||||
}
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func privateUserNamespace(idmappings *storage.IDMappingOptions, g *generate.Generator) error {
|
||||
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
|
||||
return 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")
|
||||
return 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))
|
||||
@ -562,6 +596,5 @@ func SetupUserNS(idmappings *storage.IDMappingOptions, userns Namespace, g *gene
|
||||
for _, gidmap := range idmappings.GIDMap {
|
||||
g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
|
||||
}
|
||||
}
|
||||
return user, nil
|
||||
return nil
|
||||
}
|
||||
|
@ -347,12 +347,13 @@ func ParseSignal(rawSignal string) (syscall.Signal, error) {
|
||||
|
||||
// GetKeepIDMapping returns the mappings and the user to use when keep-id is used
|
||||
func GetKeepIDMapping() (*stypes.IDMappingOptions, int, int, error) {
|
||||
options := stypes.IDMappingOptions{
|
||||
HostUIDMapping: true,
|
||||
HostGIDMapping: true,
|
||||
if !rootless.IsRootless() {
|
||||
return nil, -1, -1, errors.New("keep-id is only supported in rootless mode")
|
||||
}
|
||||
options := stypes.IDMappingOptions{
|
||||
HostUIDMapping: false,
|
||||
HostGIDMapping: false,
|
||||
}
|
||||
uid, gid := 0, 0
|
||||
if rootless.IsRootless() {
|
||||
min := func(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
@ -360,13 +361,16 @@ func GetKeepIDMapping() (*stypes.IDMappingOptions, int, int, error) {
|
||||
return b
|
||||
}
|
||||
|
||||
uid = rootless.GetRootlessUID()
|
||||
gid = rootless.GetRootlessGID()
|
||||
uid := rootless.GetRootlessUID()
|
||||
gid := rootless.GetRootlessGID()
|
||||
|
||||
uids, gids, err := rootless.GetConfiguredMappings()
|
||||
if err != nil {
|
||||
return nil, -1, -1, errors.Wrapf(err, "cannot read mappings")
|
||||
}
|
||||
if len(uids) == 0 || len(gids) == 0 {
|
||||
return nil, -1, -1, errors.Wrapf(err, "keep-id requires additional UIDs or GIDs defined in /etc/subuid and /etc/subgid to function correctly")
|
||||
}
|
||||
maxUID, maxGID := 0, 0
|
||||
for _, u := range uids {
|
||||
maxUID += u.Size
|
||||
@ -389,13 +393,38 @@ func GetKeepIDMapping() (*stypes.IDMappingOptions, int, int, error) {
|
||||
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid + 1, HostID: gid + 1, Size: maxGID - gid})
|
||||
}
|
||||
|
||||
options.HostUIDMapping = false
|
||||
options.HostGIDMapping = false
|
||||
}
|
||||
// Simply ignore the setting and do not setup an inner namespace for root as it is a no-op
|
||||
return &options, uid, gid, nil
|
||||
}
|
||||
|
||||
// GetNoMapMapping returns the mappings and the user to use when nomap is used
|
||||
func GetNoMapMapping() (*stypes.IDMappingOptions, int, int, error) {
|
||||
if !rootless.IsRootless() {
|
||||
return nil, -1, -1, errors.New("nomap is only supported in rootless mode")
|
||||
}
|
||||
options := stypes.IDMappingOptions{
|
||||
HostUIDMapping: false,
|
||||
HostGIDMapping: false,
|
||||
}
|
||||
uids, gids, err := rootless.GetConfiguredMappings()
|
||||
if err != nil {
|
||||
return nil, -1, -1, errors.Wrapf(err, "cannot read mappings")
|
||||
}
|
||||
if len(uids) == 0 || len(gids) == 0 {
|
||||
return nil, -1, -1, errors.Wrapf(err, "nomap requires additional UIDs or GIDs defined in /etc/subuid and /etc/subgid to function correctly")
|
||||
}
|
||||
options.UIDMap, options.GIDMap = nil, nil
|
||||
uid, gid := 0, 0
|
||||
for _, u := range uids {
|
||||
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: uid + 1, Size: u.Size})
|
||||
uid += u.Size
|
||||
}
|
||||
for _, g := range gids {
|
||||
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: gid + 1, Size: g.Size})
|
||||
gid += g.Size
|
||||
}
|
||||
return &options, 0, 0, nil
|
||||
}
|
||||
|
||||
// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
|
||||
func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []string, subUIDMap, subGIDMap string) (*stypes.IDMappingOptions, error) {
|
||||
options := stypes.IDMappingOptions{
|
||||
@ -415,7 +444,7 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin
|
||||
options.AutoUserNsOpts = *opts
|
||||
return &options, nil
|
||||
}
|
||||
if mode.IsKeepID() {
|
||||
if mode.IsKeepID() || mode.IsNoMap() {
|
||||
options.HostUIDMapping = false
|
||||
options.HostGIDMapping = false
|
||||
return &options, nil
|
||||
|
@ -78,12 +78,18 @@ var _ = Describe("Podman UserNS support", func() {
|
||||
It("podman --userns=keep-id", func() {
|
||||
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-u"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
if os.Geteuid() == 0 {
|
||||
Expect(session).Should(Exit(125))
|
||||
return
|
||||
}
|
||||
|
||||
Expect(session).Should(Exit(0))
|
||||
uid := fmt.Sprintf("%d", os.Geteuid())
|
||||
Expect(session.OutputToString()).To(ContainSubstring(uid))
|
||||
})
|
||||
|
||||
It("podman --userns=keep-id check passwd", func() {
|
||||
SkipIfNotRootless("keep-id only works in rootless mode")
|
||||
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-un"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
@ -93,6 +99,7 @@ var _ = Describe("Podman UserNS support", func() {
|
||||
})
|
||||
|
||||
It("podman --userns=keep-id root owns /usr", func() {
|
||||
SkipIfNotRootless("keep-id only works in rootless mode")
|
||||
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "stat", "-c%u", "/usr"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
@ -100,6 +107,7 @@ var _ = Describe("Podman UserNS support", func() {
|
||||
})
|
||||
|
||||
It("podman --userns=keep-id --user root:root", func() {
|
||||
SkipIfNotRootless("keep-id only works in rootless mode")
|
||||
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", "alpine", "id", "-u"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
@ -107,10 +115,7 @@ var _ = Describe("Podman UserNS support", func() {
|
||||
})
|
||||
|
||||
It("podman run --userns=keep-id can add users", func() {
|
||||
if os.Geteuid() == 0 {
|
||||
Skip("Test only runs without root")
|
||||
}
|
||||
|
||||
SkipIfNotRootless("keep-id only works in rootless mode")
|
||||
userName := os.Getenv("USER")
|
||||
if userName == "" {
|
||||
Skip("Can't complete test if no username available")
|
||||
|
@ -160,6 +160,7 @@ var _ = Describe("Toolbox-specific testing", func() {
|
||||
})
|
||||
|
||||
It("podman create --userns=keep-id --user root:root - entrypoint - entrypoint is executed as root", func() {
|
||||
SkipIfNotRootless("only meaningful when run rootless")
|
||||
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", ALPINE,
|
||||
"id"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
@ -168,6 +169,7 @@ var _ = Describe("Toolbox-specific testing", func() {
|
||||
})
|
||||
|
||||
It("podman create --userns=keep-id + podman exec - correct names of user and group", func() {
|
||||
SkipIfNotRootless("only meaningful when run rootless")
|
||||
var session *PodmanSessionIntegration
|
||||
var err error
|
||||
|
||||
@ -199,6 +201,7 @@ var _ = Describe("Toolbox-specific testing", func() {
|
||||
})
|
||||
|
||||
It("podman create --userns=keep-id - entrypoint - adding user with useradd and then removing their password", func() {
|
||||
SkipIfNotRootless("only meaningful when run rootless")
|
||||
var session *PodmanSessionIntegration
|
||||
|
||||
var username string = "testuser"
|
||||
@ -238,6 +241,7 @@ var _ = Describe("Toolbox-specific testing", func() {
|
||||
})
|
||||
|
||||
It("podman create --userns=keep-id + podman exec - adding group with groupadd", func() {
|
||||
SkipIfNotRootless("only meaningful when run rootless")
|
||||
var session *PodmanSessionIntegration
|
||||
|
||||
var groupName string = "testgroup"
|
||||
@ -268,6 +272,7 @@ var _ = Describe("Toolbox-specific testing", func() {
|
||||
})
|
||||
|
||||
It("podman create --userns=keep-id - entrypoint - modifying existing user with usermod - add to new group, change home/shell/uid", func() {
|
||||
SkipIfNotRootless("only meaningful when run rootless")
|
||||
var session *PodmanSessionIntegration
|
||||
var badHomeDir string = "/home/badtestuser"
|
||||
var badShell string = "/bin/sh"
|
||||
@ -315,6 +320,7 @@ var _ = Describe("Toolbox-specific testing", func() {
|
||||
})
|
||||
|
||||
It("podman run --privileged --userns=keep-id --user root:root - entrypoint - (bind)mounting", func() {
|
||||
SkipIfNotRootless("only meaningful when run rootless")
|
||||
var session *PodmanSessionIntegration
|
||||
|
||||
session = podmanTest.Podman([]string{"run", "--privileged", "--userns=keep-id", "--user", "root:root", ALPINE,
|
||||
@ -329,6 +335,7 @@ var _ = Describe("Toolbox-specific testing", func() {
|
||||
})
|
||||
|
||||
It("podman create + start - with all needed switches for create - sleep as entry-point", func() {
|
||||
SkipIfNotRootless("only meaningful when run rootless")
|
||||
var session *PodmanSessionIntegration
|
||||
|
||||
// These should be most of the switches that Toolbox uses to create a "toolbox" container
|
||||
@ -365,8 +372,8 @@ var _ = Describe("Toolbox-specific testing", func() {
|
||||
})
|
||||
|
||||
It("podman run --userns=keep-id check $HOME", func() {
|
||||
SkipIfNotRootless("only meaningful when run rootless")
|
||||
var session *PodmanSessionIntegration
|
||||
|
||||
currentUser, err := user.Current()
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
|
@ -273,9 +273,11 @@ echo $rand | 0 | $rand
|
||||
# symptom only manifests on a fedora container image -- we have no
|
||||
# reproducer on alpine. Checking directory ownership is good enough.
|
||||
@test "podman run : user namespace preserved root ownership" {
|
||||
keep="--userns=keep-id"
|
||||
is_rootless || keep=""
|
||||
for priv in "" "--privileged"; do
|
||||
for user in "--user=0" "--user=100"; do
|
||||
for keepid in "" "--userns=keep-id"; do
|
||||
for keepid in "" ${keep}; do
|
||||
opts="$priv $user $keepid"
|
||||
|
||||
for dir in /etc /usr;do
|
||||
@ -290,6 +292,7 @@ echo $rand | 0 | $rand
|
||||
|
||||
# #6829 : add username to /etc/passwd inside container if --userns=keep-id
|
||||
@test "podman run : add username to /etc/passwd if --userns=keep-id" {
|
||||
skip_if_not_rootless "--userns=keep-id only works in rootless mode"
|
||||
# Default: always run as root
|
||||
run_podman run --rm $IMAGE id -un
|
||||
is "$output" "root" "id -un on regular container"
|
||||
@ -340,6 +343,7 @@ echo $rand | 0 | $rand
|
||||
|
||||
# #6991 : /etc/passwd is modifiable
|
||||
@test "podman run : --userns=keep-id: passwd file is modifiable" {
|
||||
skip_if_not_rootless "--userns=keep-id only works in rootless mode"
|
||||
run_podman run -d --userns=keep-id --cap-add=dac_override $IMAGE sh -c 'while ! test -e /tmp/stop; do sleep 0.1; done'
|
||||
cid="$output"
|
||||
|
||||
@ -824,6 +828,9 @@ EOF
|
||||
|
||||
# CVE-2022-1227 : podman top joins container mount NS and uses nsenter from image
|
||||
@test "podman top does not use nsenter from image" {
|
||||
keepid="--userns=keep-id"
|
||||
is_rootless || keepid=""
|
||||
|
||||
tmpdir=$PODMAN_TMPDIR/build-test
|
||||
mkdir -p $tmpdir
|
||||
tmpbuilddir=$tmpdir/build
|
||||
@ -838,7 +845,7 @@ EOF
|
||||
|
||||
test_image="cve_2022_1227_test"
|
||||
run_podman build -t $test_image $tmpbuilddir
|
||||
run_podman run -d --userns=keep-id $test_image top
|
||||
run_podman run -d ${keepid} $test_image top
|
||||
ctr="$output"
|
||||
run_podman top $ctr huser,user
|
||||
run_podman rm -f -t0 $ctr
|
||||
|
@ -119,7 +119,9 @@ load helpers
|
||||
echo "content" > $srcdir/hostfile
|
||||
userid=$(id -u)
|
||||
|
||||
run_podman run --user=$userid --userns=keep-id -d --name cpcontainer $IMAGE sleep infinity
|
||||
keepid="--userns=keep-id"
|
||||
is_rootless || keepid=""
|
||||
run_podman run --user=$userid ${keepid} -d --name cpcontainer $IMAGE sleep infinity
|
||||
run_podman cp $srcdir/hostfile cpcontainer:/tmp/hostfile
|
||||
run_podman exec cpcontainer stat -c "%u" /tmp/hostfile
|
||||
is "$output" "$userid" "copied file is chowned to the container user"
|
||||
@ -138,7 +140,9 @@ load helpers
|
||||
|
||||
userid=$(id -u)
|
||||
|
||||
run_podman run --user="$userid" --userns=keep-id -d --name cpcontainer $IMAGE sleep infinity
|
||||
keepid="--userns=keep-id"
|
||||
is_rootless || keepid=""
|
||||
run_podman run --user=$userid ${keepid} -d --name cpcontainer $IMAGE sleep infinity
|
||||
run_podman cp -a=false - cpcontainer:/tmp/ < "${tmpdir}/a.tar"
|
||||
run_podman exec cpcontainer stat -c "%u:%g" /tmp/a.txt
|
||||
is "$output" "1042:1043" "copied file retains uid/gid from the tar"
|
||||
|
@ -87,6 +87,7 @@ load helpers
|
||||
|
||||
# #6829 : add username to /etc/passwd inside container if --userns=keep-id
|
||||
@test "podman exec - with keep-id" {
|
||||
skip_if_not_rootless "--userns=keep-id only works in rootless mode"
|
||||
# Multiple --userns options confirm command-line override (last one wins)
|
||||
run_podman run -d --userns=private --userns=keep-id $IMAGE sh -c \
|
||||
"echo READY;while [ ! -f /tmp/stop ]; do sleep 1; done"
|
||||
|
@ -182,13 +182,14 @@ EOF
|
||||
|
||||
run_podman volume rm $myvol
|
||||
|
||||
if is_rootless; then
|
||||
# Autocreated volumes should also work with keep-id
|
||||
# All we do here is check status; podman 1.9.1 would fail with EPERM
|
||||
myvol=myvol$(random_string)
|
||||
run_podman run --rm -v $myvol:/myvol:z --userns=keep-id $IMAGE \
|
||||
touch /myvol/myfile
|
||||
|
||||
run_podman volume rm $myvol
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
@ -94,3 +94,17 @@ EOF
|
||||
is ${output} ${secret_content} "Secrets should work with user namespace"
|
||||
run_podman secret rm ${test_name}
|
||||
}
|
||||
|
||||
@test "podman userns=nomap" {
|
||||
skip_if_not_rootless "--userns=nomap only works in rootless mode"
|
||||
ns_user=$(id -un)
|
||||
baseuid=$(egrep "${ns_user}:" /etc/subuid | cut -f2 -d:)
|
||||
test ! -z ${baseuid} || skip "no IDs allocated for user ${ns_user}"
|
||||
|
||||
test_name="test_$(random_string 12)"
|
||||
run_podman run -d --userns=nomap $IMAGE sleep 100
|
||||
cid=${output}
|
||||
run_podman top ${cid} huser
|
||||
is "${output}" "HUSER.*${baseuid}" "Container should start with baseuid from /etc/subuid not user UID"
|
||||
run_podman rm -t 0 --force ${cid}
|
||||
}
|
||||
|
@ -88,6 +88,7 @@ load helpers
|
||||
|
||||
# Issue #5466 - port-forwarding doesn't work with this option and -d
|
||||
@test "podman networking: port with --userns=keep-id" {
|
||||
skip_if_not_rootless "--userns=keep-id only works in rootless mode"
|
||||
for cidr in "" "$(random_rfc1918_subnet).0/24"; do
|
||||
myport=$(random_free_port 52000-52999)
|
||||
if [[ -z $cidr ]]; then
|
||||
|
Reference in New Issue
Block a user