summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2019-05-25 12:32:08 +0200
committerGitHub <noreply@github.com>2019-05-25 12:32:08 +0200
commitb1d590b4412fa7508390c8733c549049d6d3a75b (patch)
tree878e704e129d29e4c869717a97abde3ef5891e56 /pkg
parent3c85122faa8f5697d41cb704c76468349cf97cb8 (diff)
parent5eb321ac372f5c29f65769a4554ff224186ffb21 (diff)
downloadpodman-b1d590b4412fa7508390c8733c549049d6d3a75b.tar.gz
podman-b1d590b4412fa7508390c8733c549049d6d3a75b.tar.bz2
podman-b1d590b4412fa7508390c8733c549049d6d3a75b.zip
Merge pull request #3196 from giuseppe/keep-id
userns: add new option --userns=keep-id
Diffstat (limited to 'pkg')
-rw-r--r--pkg/namespaces/namespaces.go7
-rw-r--r--pkg/rootless/rootless_linux.c16
-rw-r--r--pkg/rootless/rootless_linux.go20
-rw-r--r--pkg/rootless/rootless_unsupported.go5
-rw-r--r--pkg/util/utils.go53
5 files changed, 99 insertions, 2 deletions
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/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c
index a08cfd36a..098ca7830 100644
--- a/pkg/rootless/rootless_linux.c
+++ b/pkg/rootless/rootless_linux.c
@@ -40,6 +40,7 @@ static const char *_unprivileged_user_namespaces = "/proc/sys/kernel/unprivilege
static int open_files_max_fd;
fd_set open_files_set;
static uid_t rootless_uid_init;
+static gid_t rootless_gid_init;
static int
syscall_setresuid (uid_t ruid, uid_t euid, uid_t suid)
@@ -59,6 +60,12 @@ rootless_uid ()
return rootless_uid_init;
}
+uid_t
+rootless_gid ()
+{
+ return rootless_gid_init;
+}
+
static void
do_pause ()
{
@@ -224,6 +231,7 @@ static void __attribute__((constructor)) init()
long pid;
char buf[12];
uid_t uid;
+ gid_t gid;
char path[PATH_MAX];
const char *const suffix = "/libpod/pause.pid";
char *cwd = getcwd (NULL, 0);
@@ -263,6 +271,7 @@ static void __attribute__((constructor)) init()
}
uid = geteuid ();
+ gid = getegid ();
sprintf (path, "/proc/%d/ns/user", pid);
fd = open (path, O_RDONLY);
@@ -310,6 +319,7 @@ static void __attribute__((constructor)) init()
free (cwd);
rootless_uid_init = uid;
+ rootless_gid_init = gid;
}
}
@@ -440,6 +450,7 @@ reexec_userns_join (int userns, int mountns, char *pause_pid_file_path)
{
pid_t ppid = getpid ();
char uid[16];
+ char gid[16];
char **argv;
int pid;
char *cwd = getcwd (NULL, 0);
@@ -451,6 +462,7 @@ reexec_userns_join (int userns, int mountns, char *pause_pid_file_path)
}
sprintf (uid, "%d", geteuid ());
+ sprintf (gid, "%d", getegid ());
argv = get_cmd_line_args (ppid);
if (argv == NULL)
@@ -477,6 +489,7 @@ reexec_userns_join (int userns, int mountns, char *pause_pid_file_path)
setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1);
setenv ("_CONTAINERS_ROOTLESS_UID", uid, 1);
+ setenv ("_CONTAINERS_ROOTLESS_GID", gid, 1);
if (prctl (PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0) < 0)
{
@@ -556,6 +569,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path)
pid_t ppid = getpid ();
char **argv;
char uid[16];
+ char gid[16];
char *listen_fds = NULL;
char *listen_pid = NULL;
bool do_socket_activation = false;
@@ -577,6 +591,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path)
}
sprintf (uid, "%d", geteuid ());
+ sprintf (gid, "%d", getegid ());
pid = syscall_clone (CLONE_NEWUSER|CLONE_NEWNS|SIGCHLD, NULL);
if (pid < 0)
@@ -621,6 +636,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path)
setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1);
setenv ("_CONTAINERS_ROOTLESS_UID", uid, 1);
+ setenv ("_CONTAINERS_ROOTLESS_GID", gid, 1);
do
ret = read (ready, &b, 1) < 0;
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index ddf881368..9132c0fe5 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -25,6 +25,7 @@ import (
#cgo remoteclient CFLAGS: -DDISABLE_JOIN_SHORTCUT
#include <stdlib.h>
extern uid_t rootless_uid();
+extern uid_t rootless_gid();
extern int reexec_in_user_namespace(int ready, char *pause_pid_file_path);
extern int reexec_in_user_namespace_wait(int pid);
extern int reexec_userns_join(int userns, int mountns, char *pause_pid_file_path);
@@ -49,10 +50,12 @@ var (
func IsRootless() bool {
isRootlessOnce.Do(func() {
rootlessUIDInit := int(C.rootless_uid())
+ rootlessGIDInit := int(C.rootless_gid())
if rootlessUIDInit != 0 {
// This happens if we joined the user+mount namespace as part of
os.Setenv("_CONTAINERS_USERNS_CONFIGURED", "done")
os.Setenv("_CONTAINERS_ROOTLESS_UID", fmt.Sprintf("%d", rootlessUIDInit))
+ os.Setenv("_CONTAINERS_ROOTLESS_GID", fmt.Sprintf("%d", rootlessGIDInit))
}
isRootless = os.Geteuid() != 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != ""
})
@@ -69,6 +72,23 @@ func GetRootlessUID() int {
return os.Geteuid()
}
+// GetRootlessGID returns the GID of the user in the parent userNS
+func GetRootlessGID() int {
+ gidEnv := os.Getenv("_CONTAINERS_ROOTLESS_GID")
+ if gidEnv != "" {
+ u, _ := strconv.Atoi(gidEnv)
+ return u
+ }
+
+ /* If the _CONTAINERS_ROOTLESS_UID is set, assume the gid==uid. */
+ uidEnv := os.Getenv("_CONTAINERS_ROOTLESS_UID")
+ if uidEnv != "" {
+ u, _ := strconv.Atoi(uidEnv)
+ return u
+ }
+ return os.Getegid()
+}
+
func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap) error {
path, err := exec.LookPath(tool)
if err != nil {
diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go
index 42f8f3aec..221baff97 100644
--- a/pkg/rootless/rootless_unsupported.go
+++ b/pkg/rootless/rootless_unsupported.go
@@ -24,6 +24,11 @@ func GetRootlessUID() int {
return -1
}
+// GetRootlessGID returns the GID of the user in the parent userNS
+func GetRootlessGID() int {
+ return -1
+}
+
// JoinUserAndMountNS re-exec podman in a new userNS and join the user and mount
// namespace of the specified PID without looking up its parent. Useful to join directly
// the conmon process. It is a convenience function for JoinUserAndMountNSWithOpts
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
}