Skip to content

Commit

Permalink
podman: add uid and gid options to keep-id
Browse files Browse the repository at this point in the history
add two new options to the keep-id user namespace option:

- uid: allow to override the UID used inside the container.
- gid: allow to override the GID used inside the container.

For example, the following command will map the rootless user (that
has UID=0 inside the rootless user namespace) to the UID=11 inside the
container user namespace:

$ podman run --userns=keep-id:uid=11 --rm -ti  fedora cat /proc/self/uid_map
         0          1         11
        11          0          1
        12         12      65525

Closes: containers#15294

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
  • Loading branch information
giuseppe committed Aug 19, 2022
1 parent df37d73 commit 3521582
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 6 deletions.
5 changes: 5 additions & 0 deletions docs/source/markdown/podman-create.1.md.in
Expand Up @@ -770,6 +770,11 @@ 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 not allowed for containers created by the root user.

Valid `keep-id` options:

- *uid*=UID: override the UID inside the container that will be used to map the current rootless user to.
- *gid*=GID: override the GID inside the container that will be used to map the current rootless user to.

**nomap**: creates a user namespace where the current rootless user's UID:GID are not mapped into the container. This option is not allowed for containers created by the root user.

**ns:**_namespace_: run the container in the given existing user namespace.
Expand Down
5 changes: 5 additions & 0 deletions docs/source/markdown/podman-kube-play.1.md.in
Expand Up @@ -272,6 +272,11 @@ 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 not allowed for containers created by the root user.

Valid `keep-id` options:

- *uid*=UID: override the UID inside the container that will be used to map the current rootless user to.
- *gid*=GID: override the GID inside the container that will be used to map the current rootless user to.

**nomap**: creates a user namespace where the current rootless user's UID:GID are not mapped into the container. This option is not allowed for containers created by the root user.

**ns:**_namespace_: run the pod in the given existing user namespace.
Expand Down
5 changes: 5 additions & 0 deletions docs/source/markdown/podman-run.1.md.in
Expand Up @@ -824,6 +824,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 not allowed for containers created by the root user.

Valid `keep-id` options:

- *uid*=UID: override the UID inside the container that will be used to map the current rootless user to.
- *gid*=GID: override the GID inside the container that will be used to map the current rootless user to.

**nomap**: creates a user namespace where the current rootless user's UID:GID are not mapped into the container. This option is not allowed for containers created by the root user.

**ns:**_namespace_: run the container in the given existing user namespace.
Expand Down
49 changes: 48 additions & 1 deletion pkg/namespaces/namespaces.go
Expand Up @@ -21,6 +21,14 @@ const (
slirpType = "slirp4netns"
)

// KeepIDUserNsOptions defines how to keepIDmatically create a user namespace.
type KeepIDUserNsOptions struct {
// Uid is the target uid in the user namespace.
Uid *uint32
// Gid is the target uid in the user namespace.
Gid *uint32
}

// CgroupMode represents cgroup mode in the container.
type CgroupMode string

Expand Down Expand Up @@ -93,7 +101,8 @@ func (n UsernsMode) IsHost() bool {

// IsKeepID indicates whether container uses a mapping where the (uid, gid) on the host is kept inside of the namespace.
func (n UsernsMode) IsKeepID() bool {
return n == "keep-id"
parts := strings.Split(string(n), ":")
return parts[0] == "keep-id"
}

// IsNoMap indicates whether container uses a mapping where the (uid, gid) on the host is not present in the namespace.
Expand Down Expand Up @@ -154,6 +163,44 @@ func (n UsernsMode) GetAutoOptions() (*types.AutoUserNsOptions, error) {
return &options, nil
}

// GetKeepIDOptions returns a KeepIDUserNsOptions with the settings to keepIDmatically set up
// a user namespace.
func (n UsernsMode) GetKeepIDOptions() (*KeepIDUserNsOptions, error) {
parts := strings.SplitN(string(n), ":", 2)
if parts[0] != "keep-id" {
return nil, fmt.Errorf("wrong user namespace mode")
}
options := KeepIDUserNsOptions{}
if len(parts) == 1 {
return &options, nil
}
for _, o := range strings.Split(parts[1], ",") {
v := strings.SplitN(o, "=", 2)
if len(v) != 2 {
return nil, fmt.Errorf("invalid option specified: %q", o)
}
switch v[0] {
case "uid":
s, err := strconv.ParseUint(v[1], 10, 32)
if err != nil {
return nil, err
}
v := uint32(s)
options.Uid = &v
case "gid":
s, err := strconv.ParseUint(v[1], 10, 32)
if err != nil {
return nil, err
}
v := uint32(s)
options.Gid = &v
default:
return nil, fmt.Errorf("unknown option specified: %q", v[0])
}
}
return &options, nil
}

// IsPrivate indicates whether the container uses the a private userns.
func (n UsernsMode) IsPrivate() bool {
return !(n.IsHost() || n.IsContainer())
Expand Down
11 changes: 9 additions & 2 deletions pkg/specgen/generate/namespaces.go
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/libpod"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/namespaces"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/podman/v4/pkg/specgen"
"github.com/containers/podman/v4/pkg/util"
Expand Down Expand Up @@ -198,12 +199,18 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.
if !rootless.IsRootless() {
return nil, errors.New("keep-id is only supported in rootless mode")
}
toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry())
opts, err := namespaces.UsernsMode(s.UserNS.String()).GetKeepIDOptions()
if err != nil {
return nil, err
}
if opts.Uid == nil && opts.Gid == nil {
toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry())
}

// If user is not overridden, set user in the container
// to user running Podman.
if s.User == "" {
_, uid, gid, err := util.GetKeepIDMapping()
_, uid, gid, err := util.GetKeepIDMapping(opts)
if err != nil {
return nil, err
}
Expand Down
14 changes: 12 additions & 2 deletions pkg/specgen/namespaces.go
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/containers/common/pkg/cgroups"
cutil "github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/namespaces"
"github.com/containers/podman/v4/pkg/util"
"github.com/containers/storage"
spec "github.com/opencontainers/runtime-spec/specs-go"
Expand Down Expand Up @@ -305,8 +306,13 @@ func ParseUserNamespace(ns string) (Namespace, error) {
toReturn.NSMode = Auto
toReturn.Value = split[1]
return toReturn, nil
case ns == "keep-id":
case strings.HasPrefix(ns, "keep-id:"):
split := strings.SplitN(ns, ":", 2)
if len(split) != 2 {
return toReturn, errors.New("invalid setting for keep-id: mode")
}
toReturn.NSMode = KeepID
toReturn.Value = split[1]
return toReturn, nil
case ns == "nomap":
toReturn.NSMode = NoMap
Expand Down Expand Up @@ -490,7 +496,11 @@ func SetupUserNS(idmappings *storage.IDMappingOptions, userns Namespace, g *gene
return user, err
}
case KeepID:
mappings, uid, gid, err := util.GetKeepIDMapping()
opts, err := namespaces.UsernsMode(userns.String()).GetKeepIDOptions()
if err != nil {
return user, err
}
mappings, uid, gid, err := util.GetKeepIDMapping(opts)
if err != nil {
return user, err
}
Expand Down
8 changes: 7 additions & 1 deletion pkg/util/utils.go
Expand Up @@ -342,7 +342,7 @@ 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) {
func GetKeepIDMapping(opts *namespaces.KeepIDUserNsOptions) (*stypes.IDMappingOptions, int, int, error) {
if !rootless.IsRootless() {
return nil, -1, -1, errors.New("keep-id is only supported in rootless mode")
}
Expand All @@ -359,6 +359,12 @@ func GetKeepIDMapping() (*stypes.IDMappingOptions, int, int, error) {

uid := rootless.GetRootlessUID()
gid := rootless.GetRootlessGID()
if opts.Uid != nil {
uid = int(*opts.Uid)
}
if opts.Gid != nil {
gid = int(*opts.Gid)
}

uids, gids, err := rootless.GetConfiguredMappings()
if err != nil {
Expand Down
10 changes: 10 additions & 0 deletions test/e2e/run_userns_test.go
Expand Up @@ -85,6 +85,16 @@ var _ = Describe("Podman UserNS support", func() {
Expect(session).Should(Exit(0))
uid := fmt.Sprintf("%d", os.Geteuid())
Expect(session.OutputToString()).To(ContainSubstring(uid))

session = podmanTest.Podman([]string{"run", "--userns=keep-id:uid=10,gid=12", "alpine", "sh", "-c", "echo $(id -u):$(id -g)"})
session.WaitWithDefaultTimeout()
if os.Geteuid() == 0 {
Expect(session).Should(Exit(125))
return
}

Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(ContainSubstring("10:12"))
})

It("podman --userns=keep-id check passwd", func() {
Expand Down

0 comments on commit 3521582

Please sign in to comment.