mirror of
https://github.com/containers/podman.git
synced 2025-05-17 15:18:43 +08:00
podman: add uid and gid options to keep-id
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: https://github.com/containers/podman/issues/15294 Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
@ -687,6 +687,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.
|
**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.
|
**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.
|
**ns:**_namespace_: run the container in the given existing user namespace.
|
||||||
|
@ -260,6 +260,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.
|
**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.
|
**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.
|
**ns:**_namespace_: run the pod in the given existing user namespace.
|
||||||
|
@ -745,6 +745,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.
|
**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.
|
**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.
|
**ns:**_namespace_: run the container in the given existing user namespace.
|
||||||
|
@ -21,6 +21,14 @@ const (
|
|||||||
slirpType = "slirp4netns"
|
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.
|
// CgroupMode represents cgroup mode in the container.
|
||||||
type CgroupMode string
|
type CgroupMode string
|
||||||
|
|
||||||
@ -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.
|
// 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 {
|
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.
|
// IsNoMap indicates whether container uses a mapping where the (uid, gid) on the host is not present in the namespace.
|
||||||
@ -154,6 +163,44 @@ func (n UsernsMode) GetAutoOptions() (*types.AutoUserNsOptions, error) {
|
|||||||
return &options, nil
|
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.
|
// IsPrivate indicates whether the container uses the a private userns.
|
||||||
func (n UsernsMode) IsPrivate() bool {
|
func (n UsernsMode) IsPrivate() bool {
|
||||||
return !(n.IsHost() || n.IsContainer())
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/containers/common/pkg/config"
|
"github.com/containers/common/pkg/config"
|
||||||
"github.com/containers/podman/v4/libpod"
|
"github.com/containers/podman/v4/libpod"
|
||||||
"github.com/containers/podman/v4/libpod/define"
|
"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/rootless"
|
||||||
"github.com/containers/podman/v4/pkg/specgen"
|
"github.com/containers/podman/v4/pkg/specgen"
|
||||||
"github.com/containers/podman/v4/pkg/util"
|
"github.com/containers/podman/v4/pkg/util"
|
||||||
@ -198,12 +199,18 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.
|
|||||||
if !rootless.IsRootless() {
|
if !rootless.IsRootless() {
|
||||||
return nil, errors.New("keep-id is only supported in rootless mode")
|
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
|
// 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(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/containers/common/pkg/cgroups"
|
"github.com/containers/common/pkg/cgroups"
|
||||||
cutil "github.com/containers/common/pkg/util"
|
cutil "github.com/containers/common/pkg/util"
|
||||||
"github.com/containers/podman/v4/libpod/define"
|
"github.com/containers/podman/v4/libpod/define"
|
||||||
|
"github.com/containers/podman/v4/pkg/namespaces"
|
||||||
"github.com/containers/podman/v4/pkg/util"
|
"github.com/containers/podman/v4/pkg/util"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
@ -308,6 +309,14 @@ 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 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":
|
case ns == "nomap":
|
||||||
toReturn.NSMode = NoMap
|
toReturn.NSMode = NoMap
|
||||||
return toReturn, nil
|
return toReturn, nil
|
||||||
@ -490,7 +499,11 @@ func SetupUserNS(idmappings *storage.IDMappingOptions, userns Namespace, g *gene
|
|||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
case KeepID:
|
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 {
|
if err != nil {
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
@ -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
|
// 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() {
|
if !rootless.IsRootless() {
|
||||||
return nil, -1, -1, errors.New("keep-id is only supported in rootless mode")
|
return nil, -1, -1, errors.New("keep-id is only supported in rootless mode")
|
||||||
}
|
}
|
||||||
@ -359,6 +359,12 @@ func GetKeepIDMapping() (*stypes.IDMappingOptions, int, int, error) {
|
|||||||
|
|
||||||
uid := rootless.GetRootlessUID()
|
uid := rootless.GetRootlessUID()
|
||||||
gid := rootless.GetRootlessGID()
|
gid := rootless.GetRootlessGID()
|
||||||
|
if opts.UID != nil {
|
||||||
|
uid = int(*opts.UID)
|
||||||
|
}
|
||||||
|
if opts.GID != nil {
|
||||||
|
gid = int(*opts.GID)
|
||||||
|
}
|
||||||
|
|
||||||
uids, gids, err := rootless.GetConfiguredMappings()
|
uids, gids, err := rootless.GetConfiguredMappings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -113,6 +113,16 @@ var _ = Describe("Podman UserNS support", func() {
|
|||||||
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))
|
||||||
|
|
||||||
|
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() {
|
It("podman --userns=keep-id check passwd", func() {
|
||||||
|
Reference in New Issue
Block a user