mirror of
https://github.com/containers/podman.git
synced 2025-05-17 23:26:08 +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
|
// -> same as AutocompleteNamespace with "auto", "keep-id" added
|
||||||
func AutocompleteUserNamespace(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
func AutocompleteUserNamespace(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
results, directive := AutocompleteNamespace(cmd, args, toComplete)
|
results, directive := AutocompleteNamespace(cmd, args, toComplete)
|
||||||
results = append(results, "auto", "keep-id")
|
results = append(results, "auto", "keep-id", "nomap")
|
||||||
return results, directive
|
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.
|
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:
|
Valid _mode_ values are:
|
||||||
|
|
||||||
**auto**[:_OPTIONS,..._]: automatically create a unique user namespace.
|
**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.
|
**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.
|
**ns:**_namespace_: run the container in the given existing user namespace.
|
||||||
|
|
||||||
**private**: create a new namespace for the container.
|
**private**: create a new namespace for the container.
|
||||||
|
@ -308,14 +308,30 @@ 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.
|
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:
|
Valid _mode_ values are:
|
||||||
|
|
||||||
- *auto[:*_OPTIONS,..._*]*: automatically create a namespace. It is possible to specify these options to `auto`:
|
- *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.
|
- *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.
|
- *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.
|
- *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.
|
- *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]]*]
|
#### **--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*]
|
||||||
|
|
||||||
|
@ -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.
|
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:
|
Valid _mode_ values are:
|
||||||
|
|
||||||
**auto**[:_OPTIONS,..._]: automatically create a unique user namespace.
|
**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`.
|
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.
|
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`.
|
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:
|
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.
|
**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.
|
**ns:**_namespace_: run the container in the given existing user namespace.
|
||||||
|
|
||||||
**private**: create a new namespace for the container.
|
**private**: create a new namespace for the container.
|
||||||
|
|
||||||
This option is incompatible with **--gidmap**, **--uidmap**, **--subuidname** and **--subgidname**.
|
This option is incompatible with **--gidmap**, **--uidmap**, **--subuidname** and **--subgidname**.
|
||||||
|
|
||||||
#### **--uts**=*mode*
|
#### **--uts**=*mode*
|
||||||
|
@ -276,46 +276,47 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin
|
|||||||
if len(subUIDMap) > 0 || len(subGIDMap) > 0 {
|
if len(subUIDMap) > 0 || len(subGIDMap) > 0 {
|
||||||
return nil, errors.New("cannot specify subuidmap or subgidmap with --userns=keep-id")
|
return nil, errors.New("cannot specify subuidmap or subgidmap with --userns=keep-id")
|
||||||
}
|
}
|
||||||
if rootless.IsRootless() {
|
if !rootless.IsRootless() {
|
||||||
min := func(a, b int) int {
|
return nil, errors.New("keep-id is only supported in rootless mode")
|
||||||
if a < b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
uid := rootless.GetRootlessUID()
|
|
||||||
gid := rootless.GetRootlessGID()
|
|
||||||
|
|
||||||
uids, gids, err := rootless.GetConfiguredMappings()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "cannot read mappings")
|
|
||||||
}
|
|
||||||
maxUID, maxGID := 0, 0
|
|
||||||
for _, u := range uids {
|
|
||||||
maxUID += u.Size
|
|
||||||
}
|
|
||||||
for _, g := range gids {
|
|
||||||
maxGID += g.Size
|
|
||||||
}
|
|
||||||
|
|
||||||
options.UIDMap, options.GIDMap = nil, nil
|
|
||||||
|
|
||||||
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)})
|
|
||||||
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1})
|
|
||||||
if maxUID > uid {
|
|
||||||
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid + 1, HostID: uid + 1, Size: maxUID - uid})
|
|
||||||
}
|
|
||||||
|
|
||||||
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)})
|
|
||||||
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1})
|
|
||||||
if maxGID > gid {
|
|
||||||
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid + 1, HostID: gid + 1, Size: maxGID - gid})
|
|
||||||
}
|
|
||||||
|
|
||||||
options.HostUIDMapping = false
|
|
||||||
options.HostGIDMapping = false
|
|
||||||
}
|
}
|
||||||
|
min := func(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := rootless.GetRootlessUID()
|
||||||
|
gid := rootless.GetRootlessGID()
|
||||||
|
|
||||||
|
uids, gids, err := rootless.GetConfiguredMappings()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "cannot read mappings")
|
||||||
|
}
|
||||||
|
maxUID, maxGID := 0, 0
|
||||||
|
for _, u := range uids {
|
||||||
|
maxUID += u.Size
|
||||||
|
}
|
||||||
|
for _, g := range gids {
|
||||||
|
maxGID += g.Size
|
||||||
|
}
|
||||||
|
|
||||||
|
options.UIDMap, options.GIDMap = nil, nil
|
||||||
|
|
||||||
|
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)})
|
||||||
|
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1})
|
||||||
|
if maxUID > uid {
|
||||||
|
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid + 1, HostID: uid + 1, Size: maxUID - uid})
|
||||||
|
}
|
||||||
|
|
||||||
|
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)})
|
||||||
|
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1})
|
||||||
|
if maxGID > gid {
|
||||||
|
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
|
// Simply ignore the setting and do not setup an inner namespace for root as it is a no-op
|
||||||
return &options, nil
|
return &options, nil
|
||||||
}
|
}
|
||||||
|
@ -96,6 +96,11 @@ func (n UsernsMode) IsKeepID() bool {
|
|||||||
return n == "keep-id"
|
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.
|
// IsAuto indicates whether container uses the "auto" userns mode.
|
||||||
func (n UsernsMode) IsAuto() bool {
|
func (n UsernsMode) IsAuto() bool {
|
||||||
parts := strings.Split(string(n), ":")
|
parts := strings.Split(string(n), ":")
|
||||||
@ -158,7 +163,7 @@ func (n UsernsMode) IsPrivate() bool {
|
|||||||
func (n UsernsMode) Valid() bool {
|
func (n UsernsMode) Valid() bool {
|
||||||
parts := strings.Split(string(n), ":")
|
parts := strings.Split(string(n), ":")
|
||||||
switch mode := parts[0]; mode {
|
switch mode := parts[0]; mode {
|
||||||
case "", privateType, hostType, "keep-id", nsType, "auto":
|
case "", privateType, hostType, "keep-id", nsType, "auto", "nomap":
|
||||||
case containerType:
|
case containerType:
|
||||||
if len(parts) != 2 || parts[1] == "" {
|
if len(parts) != 2 || parts[1] == "" {
|
||||||
return false
|
return false
|
||||||
|
@ -165,21 +165,19 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.
|
|||||||
// User
|
// User
|
||||||
switch s.UserNS.NSMode {
|
switch s.UserNS.NSMode {
|
||||||
case specgen.KeepID:
|
case specgen.KeepID:
|
||||||
if rootless.IsRootless() {
|
if !rootless.IsRootless() {
|
||||||
toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry())
|
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
|
// If user is not overridden, set user in the container
|
||||||
// to user running Podman.
|
// to user running Podman.
|
||||||
if s.User == "" {
|
if s.User == "" {
|
||||||
_, uid, gid, err := util.GetKeepIDMapping()
|
_, uid, gid, err := util.GetKeepIDMapping()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
|
||||||
toReturn = append(toReturn, libpod.WithUser(fmt.Sprintf("%d:%d", uid, gid)))
|
|
||||||
}
|
}
|
||||||
} else {
|
toReturn = append(toReturn, libpod.WithUser(fmt.Sprintf("%d:%d", uid, gid)))
|
||||||
// keep-id as root doesn't need a user namespace
|
|
||||||
s.UserNS.NSMode = specgen.Host
|
|
||||||
}
|
}
|
||||||
case specgen.FromPod:
|
case specgen.FromPod:
|
||||||
if pod == nil || infraCtr == nil {
|
if pod == nil || infraCtr == nil {
|
||||||
|
@ -55,6 +55,10 @@ const (
|
|||||||
// of the namespace itself.
|
// of the namespace itself.
|
||||||
// Only used with the user namespace, invalid otherwise.
|
// Only used with the user namespace, invalid otherwise.
|
||||||
KeepID NamespaceMode = "keep-id"
|
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.
|
// Auto indicates to automatically create a user namespace.
|
||||||
// Only used with the user namespace, invalid otherwise.
|
// Only used with the user namespace, invalid otherwise.
|
||||||
Auto NamespaceMode = "auto"
|
Auto NamespaceMode = "auto"
|
||||||
@ -121,6 +125,11 @@ func (n *Namespace) IsKeepID() bool {
|
|||||||
return n.NSMode == KeepID
|
return n.NSMode == KeepID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNoMap indicates the namespace is NoMap
|
||||||
|
func (n *Namespace) IsNoMap() bool {
|
||||||
|
return n.NSMode == NoMap
|
||||||
|
}
|
||||||
|
|
||||||
func (n *Namespace) String() string {
|
func (n *Namespace) String() string {
|
||||||
if n.Value != "" {
|
if n.Value != "" {
|
||||||
return fmt.Sprintf("%s:%s", n.NSMode, n.Value)
|
return fmt.Sprintf("%s:%s", n.NSMode, n.Value)
|
||||||
@ -133,7 +142,7 @@ func validateUserNS(n *Namespace) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
switch n.NSMode {
|
switch n.NSMode {
|
||||||
case Auto, KeepID:
|
case Auto, KeepID, NoMap:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return n.validate()
|
return n.validate()
|
||||||
@ -299,6 +308,9 @@ func ParseUserNamespace(ns string) (Namespace, error) {
|
|||||||
case ns == "keep-id":
|
case ns == "keep-id":
|
||||||
toReturn.NSMode = KeepID
|
toReturn.NSMode = KeepID
|
||||||
return toReturn, nil
|
return toReturn, nil
|
||||||
|
case ns == "nomap":
|
||||||
|
toReturn.NSMode = NoMap
|
||||||
|
return toReturn, nil
|
||||||
case ns == "":
|
case ns == "":
|
||||||
toReturn.NSMode = Host
|
toReturn.NSMode = Host
|
||||||
return toReturn, nil
|
return toReturn, nil
|
||||||
@ -548,20 +560,41 @@ func SetupUserNS(idmappings *storage.IDMappingOptions, userns Namespace, g *gene
|
|||||||
g.SetProcessUID(uint32(uid))
|
g.SetProcessUID(uint32(uid))
|
||||||
g.SetProcessGID(uint32(gid))
|
g.SetProcessGID(uint32(gid))
|
||||||
user = fmt.Sprintf("%d:%d", uid, gid)
|
user = fmt.Sprintf("%d:%d", uid, gid)
|
||||||
fallthrough
|
if err := privateUserNamespace(idmappings, g); err != nil {
|
||||||
case Private:
|
|
||||||
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
|
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
if idmappings == nil || (len(idmappings.UIDMap) == 0 && len(idmappings.GIDMap) == 0) {
|
case NoMap:
|
||||||
return user, errors.Errorf("must provide at least one UID or GID mapping to configure a user namespace")
|
mappings, uid, gid, err := util.GetNoMapMapping()
|
||||||
|
if err != nil {
|
||||||
|
return user, err
|
||||||
}
|
}
|
||||||
for _, uidmap := range idmappings.UIDMap {
|
idmappings = mappings
|
||||||
g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
|
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
|
||||||
}
|
}
|
||||||
for _, gidmap := range idmappings.GIDMap {
|
case Private:
|
||||||
g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
|
if err := privateUserNamespace(idmappings, g); err != nil {
|
||||||
|
return user, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return user, nil
|
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 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 nil
|
||||||
|
}
|
||||||
|
@ -347,55 +347,84 @@ func ParseSignal(rawSignal string) (syscall.Signal, error) {
|
|||||||
|
|
||||||
// GetKeepIDMapping returns the mappings and the user to use when keep-id is used
|
// GetKeepIDMapping returns the mappings and the user to use when keep-id is used
|
||||||
func GetKeepIDMapping() (*stypes.IDMappingOptions, int, int, error) {
|
func GetKeepIDMapping() (*stypes.IDMappingOptions, int, int, error) {
|
||||||
|
if !rootless.IsRootless() {
|
||||||
|
return nil, -1, -1, errors.New("keep-id is only supported in rootless mode")
|
||||||
|
}
|
||||||
options := stypes.IDMappingOptions{
|
options := stypes.IDMappingOptions{
|
||||||
HostUIDMapping: true,
|
HostUIDMapping: false,
|
||||||
HostGIDMapping: true,
|
HostGIDMapping: false,
|
||||||
}
|
}
|
||||||
uid, gid := 0, 0
|
min := func(a, b int) int {
|
||||||
if rootless.IsRootless() {
|
if a < b {
|
||||||
min := func(a, b int) int {
|
return a
|
||||||
if a < b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
}
|
||||||
|
return b
|
||||||
uid = rootless.GetRootlessUID()
|
|
||||||
gid = rootless.GetRootlessGID()
|
|
||||||
|
|
||||||
uids, gids, err := rootless.GetConfiguredMappings()
|
|
||||||
if err != nil {
|
|
||||||
return nil, -1, -1, errors.Wrapf(err, "cannot read mappings")
|
|
||||||
}
|
|
||||||
maxUID, maxGID := 0, 0
|
|
||||||
for _, u := range uids {
|
|
||||||
maxUID += u.Size
|
|
||||||
}
|
|
||||||
for _, g := range gids {
|
|
||||||
maxGID += g.Size
|
|
||||||
}
|
|
||||||
|
|
||||||
options.UIDMap, options.GIDMap = nil, nil
|
|
||||||
|
|
||||||
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)})
|
|
||||||
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1})
|
|
||||||
if maxUID > uid {
|
|
||||||
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid + 1, HostID: uid + 1, Size: maxUID - uid})
|
|
||||||
}
|
|
||||||
|
|
||||||
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)})
|
|
||||||
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1})
|
|
||||||
if maxGID > gid {
|
|
||||||
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
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
for _, g := range gids {
|
||||||
|
maxGID += g.Size
|
||||||
|
}
|
||||||
|
|
||||||
|
options.UIDMap, options.GIDMap = nil, nil
|
||||||
|
|
||||||
|
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)})
|
||||||
|
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1})
|
||||||
|
if maxUID > uid {
|
||||||
|
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid + 1, HostID: uid + 1, Size: maxUID - uid})
|
||||||
|
}
|
||||||
|
|
||||||
|
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)})
|
||||||
|
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1})
|
||||||
|
if maxGID > gid {
|
||||||
|
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid + 1, HostID: gid + 1, Size: maxGID - gid})
|
||||||
|
}
|
||||||
|
|
||||||
return &options, uid, gid, nil
|
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
|
// 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) {
|
func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []string, subUIDMap, subGIDMap string) (*stypes.IDMappingOptions, error) {
|
||||||
options := stypes.IDMappingOptions{
|
options := stypes.IDMappingOptions{
|
||||||
@ -415,7 +444,7 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin
|
|||||||
options.AutoUserNsOpts = *opts
|
options.AutoUserNsOpts = *opts
|
||||||
return &options, nil
|
return &options, nil
|
||||||
}
|
}
|
||||||
if mode.IsKeepID() {
|
if mode.IsKeepID() || mode.IsNoMap() {
|
||||||
options.HostUIDMapping = false
|
options.HostUIDMapping = false
|
||||||
options.HostGIDMapping = false
|
options.HostGIDMapping = false
|
||||||
return &options, nil
|
return &options, nil
|
||||||
|
@ -78,12 +78,18 @@ var _ = Describe("Podman UserNS support", func() {
|
|||||||
It("podman --userns=keep-id", func() {
|
It("podman --userns=keep-id", func() {
|
||||||
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-u"})
|
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-u"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
|
if os.Geteuid() == 0 {
|
||||||
|
Expect(session).Should(Exit(125))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Expect(session).Should(Exit(0))
|
Expect(session).Should(Exit(0))
|
||||||
uid := fmt.Sprintf("%d", os.Geteuid())
|
uid := fmt.Sprintf("%d", os.Geteuid())
|
||||||
Expect(session.OutputToString()).To(ContainSubstring(uid))
|
Expect(session.OutputToString()).To(ContainSubstring(uid))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman --userns=keep-id check passwd", func() {
|
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 := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-un"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session).Should(Exit(0))
|
Expect(session).Should(Exit(0))
|
||||||
@ -93,6 +99,7 @@ var _ = Describe("Podman UserNS support", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("podman --userns=keep-id root owns /usr", 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 := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "stat", "-c%u", "/usr"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session).Should(Exit(0))
|
Expect(session).Should(Exit(0))
|
||||||
@ -100,6 +107,7 @@ var _ = Describe("Podman UserNS support", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("podman --userns=keep-id --user root:root", 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 := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", "alpine", "id", "-u"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session).Should(Exit(0))
|
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() {
|
It("podman run --userns=keep-id can add users", func() {
|
||||||
if os.Geteuid() == 0 {
|
SkipIfNotRootless("keep-id only works in rootless mode")
|
||||||
Skip("Test only runs without root")
|
|
||||||
}
|
|
||||||
|
|
||||||
userName := os.Getenv("USER")
|
userName := os.Getenv("USER")
|
||||||
if userName == "" {
|
if userName == "" {
|
||||||
Skip("Can't complete test if no username available")
|
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() {
|
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,
|
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", ALPINE,
|
||||||
"id"})
|
"id"})
|
||||||
session.WaitWithDefaultTimeout()
|
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() {
|
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 session *PodmanSessionIntegration
|
||||||
var err error
|
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() {
|
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 session *PodmanSessionIntegration
|
||||||
|
|
||||||
var username string = "testuser"
|
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() {
|
It("podman create --userns=keep-id + podman exec - adding group with groupadd", func() {
|
||||||
|
SkipIfNotRootless("only meaningful when run rootless")
|
||||||
var session *PodmanSessionIntegration
|
var session *PodmanSessionIntegration
|
||||||
|
|
||||||
var groupName string = "testgroup"
|
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() {
|
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 session *PodmanSessionIntegration
|
||||||
var badHomeDir string = "/home/badtestuser"
|
var badHomeDir string = "/home/badtestuser"
|
||||||
var badShell string = "/bin/sh"
|
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() {
|
It("podman run --privileged --userns=keep-id --user root:root - entrypoint - (bind)mounting", func() {
|
||||||
|
SkipIfNotRootless("only meaningful when run rootless")
|
||||||
var session *PodmanSessionIntegration
|
var session *PodmanSessionIntegration
|
||||||
|
|
||||||
session = podmanTest.Podman([]string{"run", "--privileged", "--userns=keep-id", "--user", "root:root", ALPINE,
|
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() {
|
It("podman create + start - with all needed switches for create - sleep as entry-point", func() {
|
||||||
|
SkipIfNotRootless("only meaningful when run rootless")
|
||||||
var session *PodmanSessionIntegration
|
var session *PodmanSessionIntegration
|
||||||
|
|
||||||
// These should be most of the switches that Toolbox uses to create a "toolbox" container
|
// 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() {
|
It("podman run --userns=keep-id check $HOME", func() {
|
||||||
|
SkipIfNotRootless("only meaningful when run rootless")
|
||||||
var session *PodmanSessionIntegration
|
var session *PodmanSessionIntegration
|
||||||
|
|
||||||
currentUser, err := user.Current()
|
currentUser, err := user.Current()
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
|
|
||||||
|
@ -273,9 +273,11 @@ echo $rand | 0 | $rand
|
|||||||
# symptom only manifests on a fedora container image -- we have no
|
# symptom only manifests on a fedora container image -- we have no
|
||||||
# reproducer on alpine. Checking directory ownership is good enough.
|
# reproducer on alpine. Checking directory ownership is good enough.
|
||||||
@test "podman run : user namespace preserved root ownership" {
|
@test "podman run : user namespace preserved root ownership" {
|
||||||
|
keep="--userns=keep-id"
|
||||||
|
is_rootless || keep=""
|
||||||
for priv in "" "--privileged"; do
|
for priv in "" "--privileged"; do
|
||||||
for user in "--user=0" "--user=100"; 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"
|
opts="$priv $user $keepid"
|
||||||
|
|
||||||
for dir in /etc /usr;do
|
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
|
# #6829 : add username to /etc/passwd inside container if --userns=keep-id
|
||||||
@test "podman run : add username to /etc/passwd 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
|
# Default: always run as root
|
||||||
run_podman run --rm $IMAGE id -un
|
run_podman run --rm $IMAGE id -un
|
||||||
is "$output" "root" "id -un on regular container"
|
is "$output" "root" "id -un on regular container"
|
||||||
@ -340,6 +343,7 @@ echo $rand | 0 | $rand
|
|||||||
|
|
||||||
# #6991 : /etc/passwd is modifiable
|
# #6991 : /etc/passwd is modifiable
|
||||||
@test "podman run : --userns=keep-id: passwd file 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'
|
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"
|
cid="$output"
|
||||||
|
|
||||||
@ -824,6 +828,9 @@ EOF
|
|||||||
|
|
||||||
# CVE-2022-1227 : podman top joins container mount NS and uses nsenter from image
|
# CVE-2022-1227 : podman top joins container mount NS and uses nsenter from image
|
||||||
@test "podman top does not use nsenter from image" {
|
@test "podman top does not use nsenter from image" {
|
||||||
|
keepid="--userns=keep-id"
|
||||||
|
is_rootless || keepid=""
|
||||||
|
|
||||||
tmpdir=$PODMAN_TMPDIR/build-test
|
tmpdir=$PODMAN_TMPDIR/build-test
|
||||||
mkdir -p $tmpdir
|
mkdir -p $tmpdir
|
||||||
tmpbuilddir=$tmpdir/build
|
tmpbuilddir=$tmpdir/build
|
||||||
@ -838,7 +845,7 @@ EOF
|
|||||||
|
|
||||||
test_image="cve_2022_1227_test"
|
test_image="cve_2022_1227_test"
|
||||||
run_podman build -t $test_image $tmpbuilddir
|
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"
|
ctr="$output"
|
||||||
run_podman top $ctr huser,user
|
run_podman top $ctr huser,user
|
||||||
run_podman rm -f -t0 $ctr
|
run_podman rm -f -t0 $ctr
|
||||||
|
@ -119,7 +119,9 @@ load helpers
|
|||||||
echo "content" > $srcdir/hostfile
|
echo "content" > $srcdir/hostfile
|
||||||
userid=$(id -u)
|
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 cp $srcdir/hostfile cpcontainer:/tmp/hostfile
|
||||||
run_podman exec cpcontainer stat -c "%u" /tmp/hostfile
|
run_podman exec cpcontainer stat -c "%u" /tmp/hostfile
|
||||||
is "$output" "$userid" "copied file is chowned to the container user"
|
is "$output" "$userid" "copied file is chowned to the container user"
|
||||||
@ -138,7 +140,9 @@ load helpers
|
|||||||
|
|
||||||
userid=$(id -u)
|
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 cp -a=false - cpcontainer:/tmp/ < "${tmpdir}/a.tar"
|
||||||
run_podman exec cpcontainer stat -c "%u:%g" /tmp/a.txt
|
run_podman exec cpcontainer stat -c "%u:%g" /tmp/a.txt
|
||||||
is "$output" "1042:1043" "copied file retains uid/gid from the tar"
|
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
|
# #6829 : add username to /etc/passwd inside container if --userns=keep-id
|
||||||
@test "podman exec - with 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)
|
# Multiple --userns options confirm command-line override (last one wins)
|
||||||
run_podman run -d --userns=private --userns=keep-id $IMAGE sh -c \
|
run_podman run -d --userns=private --userns=keep-id $IMAGE sh -c \
|
||||||
"echo READY;while [ ! -f /tmp/stop ]; do sleep 1; done"
|
"echo READY;while [ ! -f /tmp/stop ]; do sleep 1; done"
|
||||||
|
@ -182,13 +182,14 @@ EOF
|
|||||||
|
|
||||||
run_podman volume rm $myvol
|
run_podman volume rm $myvol
|
||||||
|
|
||||||
# Autocreated volumes should also work with keep-id
|
if is_rootless; then
|
||||||
# All we do here is check status; podman 1.9.1 would fail with EPERM
|
# Autocreated volumes should also work with keep-id
|
||||||
myvol=myvol$(random_string)
|
# All we do here is check status; podman 1.9.1 would fail with EPERM
|
||||||
run_podman run --rm -v $myvol:/myvol:z --userns=keep-id $IMAGE \
|
myvol=myvol$(random_string)
|
||||||
|
run_podman run --rm -v $myvol:/myvol:z --userns=keep-id $IMAGE \
|
||||||
touch /myvol/myfile
|
touch /myvol/myfile
|
||||||
|
run_podman volume rm $myvol
|
||||||
run_podman volume rm $myvol
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,3 +94,17 @@ EOF
|
|||||||
is ${output} ${secret_content} "Secrets should work with user namespace"
|
is ${output} ${secret_content} "Secrets should work with user namespace"
|
||||||
run_podman secret rm ${test_name}
|
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
|
# Issue #5466 - port-forwarding doesn't work with this option and -d
|
||||||
@test "podman networking: port with --userns=keep-id" {
|
@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
|
for cidr in "" "$(random_rfc1918_subnet).0/24"; do
|
||||||
myport=$(random_free_port 52000-52999)
|
myport=$(random_free_port 52000-52999)
|
||||||
if [[ -z $cidr ]]; then
|
if [[ -z $cidr ]]; then
|
||||||
|
Reference in New Issue
Block a user