summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiuseppe Scrivano <gscrivan@redhat.com>2019-05-23 22:28:59 +0200
committerGiuseppe Scrivano <gscrivan@redhat.com>2019-05-24 17:34:12 +0200
commitf09370c68b8b514aca80bfaa34f98fbc5b97d318 (patch)
tree703f156c0ead88361527575f6fd2ccfb2a13893e
parent6df320c3910da5600a611f8aed783a499430a75c (diff)
downloadpodman-f09370c68b8b514aca80bfaa34f98fbc5b97d318.tar.gz
podman-f09370c68b8b514aca80bfaa34f98fbc5b97d318.tar.bz2
podman-f09370c68b8b514aca80bfaa34f98fbc5b97d318.zip
userns: add new option --userns=keep-id
it creates a namespace where the current UID:GID on the host is mapped to the same UID:GID in the container. Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
-rw-r--r--cmd/podman/libpodruntime/runtime.go4
-rw-r--r--cmd/podman/shared/create.go7
-rw-r--r--docs/podman-create.1.md2
-rw-r--r--docs/podman-run.1.md2
-rw-r--r--pkg/namespaces/namespaces.go7
-rw-r--r--pkg/util/utils.go53
-rw-r--r--test/e2e/run_userns_test.go9
7 files changed, 79 insertions, 5 deletions
diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go
index b8d77602d..898c81515 100644
--- a/cmd/podman/libpodruntime/runtime.go
+++ b/cmd/podman/libpodruntime/runtime.go
@@ -5,6 +5,7 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/namespaces"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
@@ -37,11 +38,12 @@ func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber bool,
subgidname := c.Flags().Lookup("subgidname")
if (uidmapFlag != nil && gidmapFlag != nil && subuidname != nil && subgidname != nil) &&
(uidmapFlag.Changed || gidmapFlag.Changed || subuidname.Changed || subgidname.Changed) {
+ userns, _ := c.Flags().GetString("userns")
uidmapVal, _ := c.Flags().GetStringSlice("uidmap")
gidmapVal, _ := c.Flags().GetStringSlice("gidmap")
subuidVal, _ := c.Flags().GetString("subuidname")
subgidVal, _ := c.Flags().GetString("subgidname")
- mappings, err := util.ParseIDMapping(uidmapVal, gidmapVal, subuidVal, subgidVal)
+ mappings, err := util.ParseIDMapping(namespaces.UsernsMode(userns), uidmapVal, gidmapVal, subuidVal, subgidVal)
if err != nil {
return nil, err
}
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index d1f704374..3c9b17804 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -19,6 +19,7 @@ import (
ann "github.com/containers/libpod/pkg/annotations"
"github.com/containers/libpod/pkg/inspect"
ns "github.com/containers/libpod/pkg/namespaces"
+ "github.com/containers/libpod/pkg/rootless"
cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/util"
"github.com/docker/docker/pkg/signal"
@@ -283,7 +284,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
namespaces map[string]string
)
- idmappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidname"), c.String("subgidname"))
+ idmappings, err := util.ParseIDMapping(ns.UsernsMode(c.String("userns")), c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidname"), c.String("subgidname"))
if err != nil {
return nil, err
}
@@ -451,7 +452,9 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
// USER
user := c.String("user")
if user == "" {
- if data == nil {
+ if usernsMode.IsKeepID() {
+ user = fmt.Sprintf("%d:%d", rootless.GetRootlessUID(), rootless.GetRootlessGID())
+ } else if data == nil {
user = "0"
} else {
user = data.Config.User
diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md
index 58e579605..53f4c8fed 100644
--- a/docs/podman-create.1.md
+++ b/docs/podman-create.1.md
@@ -727,11 +727,13 @@ The followings examples are all valid:
Without this argument the command will be run as root in the container.
**--userns**=host
+**--userns**=keep-id
**--userns**=ns:my_namespace
Set the user namespace mode for the container. The use of userns is disabled by default.
- `host`: run in the user namespace of the caller. This is the default if no user namespace options are set. The processes running in the container will have the same privileges on the host as any other process launched by the calling 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.
- `ns`: run the container in the given existing user namespace.
This option is incompatible with --gidmap, --uidmap, --subuid and --subgid
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index a9484a517..95e99cb7e 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -763,11 +763,13 @@ The followings examples are all valid:
Without this argument the command will be run as root in the container.
**--userns**=host
+**--userns**=keep-id
**--userns**=ns:my_namespace
Set the user namespace mode for the container. The use of userns is disabled by default.
- `host`: run in the user namespace of the caller. This is the default if no user namespace options are set. The processes running in the container will have the same privileges on the host as any other process launched by the calling 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.
- `ns`: run the container in the given existing user namespace.
This option is incompatible with --gidmap, --uidmap, --subuid and --subgid
diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go
index fde6118af..ec9276344 100644
--- a/pkg/namespaces/namespaces.go
+++ b/pkg/namespaces/namespaces.go
@@ -12,6 +12,11 @@ func (n UsernsMode) IsHost() bool {
return n == "host"
}
+// IsKeepID indicates whether container uses a mapping where the (uid, gid) on the host is lept inside of the namespace.
+func (n UsernsMode) IsKeepID() bool {
+ return n == "keep-id"
+}
+
// IsPrivate indicates whether the container uses the a private userns.
func (n UsernsMode) IsPrivate() bool {
return !(n.IsHost())
@@ -21,7 +26,7 @@ func (n UsernsMode) IsPrivate() bool {
func (n UsernsMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
- case "", "host":
+ case "", "host", "keep-id":
default:
return false
}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 2a52e5129..a074f276c 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -3,6 +3,7 @@ package util
import (
"fmt"
"os"
+ ouser "os/user"
"path/filepath"
"strings"
"sync"
@@ -11,6 +12,8 @@ import (
"github.com/BurntSushi/toml"
"github.com/containers/image/types"
"github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/pkg/namespaces"
+ "github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
"github.com/opencontainers/image-spec/specs-go/v1"
@@ -131,11 +134,59 @@ func GetImageConfig(changes []string) (v1.ImageConfig, error) {
}
// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
-func ParseIDMapping(UIDMapSlice, GIDMapSlice []string, subUIDMap, subGIDMap string) (*storage.IDMappingOptions, error) {
+func ParseIDMapping(mode namespaces.UsernsMode, UIDMapSlice, GIDMapSlice []string, subUIDMap, subGIDMap string) (*storage.IDMappingOptions, error) {
options := storage.IDMappingOptions{
HostUIDMapping: true,
HostGIDMapping: true,
}
+
+ if mode.IsKeepID() {
+ if len(UIDMapSlice) > 0 || len(GIDMapSlice) > 0 {
+ return nil, errors.New("cannot specify custom mappings with --userns=keep-id")
+ }
+ if len(subUIDMap) > 0 || len(subGIDMap) > 0 {
+ return nil, errors.New("cannot specify subuidmap or subgidmap with --userns=keep-id")
+ }
+ if rootless.IsRootless() {
+ uid := rootless.GetRootlessUID()
+ gid := rootless.GetRootlessGID()
+
+ username := os.Getenv("USER")
+ if username == "" {
+ user, err := ouser.LookupId(fmt.Sprintf("%d", uid))
+ if err == nil {
+ username = user.Username
+ }
+ }
+ mappings, err := idtools.NewIDMappings(username, username)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot find mappings for user %s", username)
+ }
+ maxUID, maxGID := 0, 0
+ for _, u := range mappings.UIDs() {
+ maxUID += u.Size
+ }
+ for _, g := range mappings.GIDs() {
+ maxGID += g.Size
+ }
+
+ options.UIDMap, options.GIDMap = nil, nil
+
+ options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: uid})
+ options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1})
+ 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: gid})
+ options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1})
+ 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, nil
+ }
+
if subGIDMap == "" && subUIDMap != "" {
subGIDMap = subUIDMap
}
diff --git a/test/e2e/run_userns_test.go b/test/e2e/run_userns_test.go
index f7f0e1c9a..ce6971cd1 100644
--- a/test/e2e/run_userns_test.go
+++ b/test/e2e/run_userns_test.go
@@ -3,6 +3,7 @@
package integration
import (
+ "fmt"
"os"
. "github.com/containers/libpod/test/utils"
@@ -76,4 +77,12 @@ var _ = Describe("Podman UserNS support", func() {
Expect(ok).To(BeTrue())
})
+ It("podman --userns=keep-id", func() {
+ session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-u"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ uid := fmt.Sprintf("%d", os.Geteuid())
+ ok, _ := session.GrepString(uid)
+ Expect(ok).To(BeTrue())
+ })
})