From b51d7379987581da82902027fe91cdf298047bc0 Mon Sep 17 00:00:00 2001
From: Daniel J Walsh <dwalsh@redhat.com>
Date: Mon, 23 Apr 2018 20:42:53 -0400
Subject: [PATCH] Begin wiring in USERNS Support into podman

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>

Closes: #690
Approved by: mheon
---
 cmd/podman/common.go                | 17 +++++++
 cmd/podman/create.go                | 71 ++++++++++++++++++---------
 cmd/podman/inspect.go               |  2 +-
 cmd/podman/libpodruntime/runtime.go | 37 +++++++-------
 cmd/podman/run.go                   | 16 ++++--
 cmd/podman/run_test.go              |  3 +-
 cmd/podman/spec.go                  | 17 +++++++
 completions/bash/podman             |  4 ++
 docs/podman-create.1.md             | 38 +++++++++++++--
 docs/podman-run.1.md                | 38 +++++++++++++--
 docs/podman.1.md                    |  1 -
 libpod/container.go                 | 28 ++++++++++-
 libpod/container_internal.go        | 16 ++++--
 libpod/options.go                   | 12 +++++
 libpod/storage.go                   | 16 +++---
 pkg/secrets/secrets.go              | 23 +++++++--
 pkg/util/utils.go                   | 76 +++++++++++++++++++++++++++++
 17 files changed, 345 insertions(+), 70 deletions(-)

diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index 68d22b6298..635869609f 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -199,6 +199,10 @@ var createFlags = []cli.Flag{
 		Name:  "expose",
 		Usage: "Expose a port or a range of ports (default [])",
 	},
+	cli.StringSliceFlag{
+		Name:  "gidmap",
+		Usage: "GID map to use for the user namespace",
+	},
 	cli.StringSliceFlag{
 		Name:  "group-add",
 		Usage: "Add additional groups to join (default [])",
@@ -341,6 +345,15 @@ var createFlags = []cli.Flag{
 		Name:  "storage-opt",
 		Usage: "Storage driver options per container (default [])",
 	},
+	cli.StringFlag{
+		Name:  "subgidname",
+		Usage: "Name of range listed in /etc/subgid for use in user namespace",
+	},
+	cli.StringFlag{
+		Name:  "subuidname",
+		Usage: "Name of range listed in /etc/subuid for use in user namespace",
+	},
+
 	cli.StringSliceFlag{
 		Name:  "sysctl",
 		Usage: "Sysctl options (default [])",
@@ -353,6 +366,10 @@ var createFlags = []cli.Flag{
 		Name:  "tty, t",
 		Usage: "Allocate a pseudo-TTY for container",
 	},
+	cli.StringSliceFlag{
+		Name:  "uidmap",
+		Usage: "UID map to use for the user namespace",
+	},
 	cli.StringSliceFlag{
 		Name:  "ulimit",
 		Usage: "Ulimit options (default [])",
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index 54a542ee52..7740da8e18 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
 	"net"
@@ -9,6 +10,7 @@ import (
 	"strings"
 	"syscall"
 
+	"github.com/containers/storage"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/go-connections/nat"
@@ -92,7 +94,8 @@ type createConfig struct {
 	Hostname           string   //hostname
 	Image              string
 	ImageID            string
-	BuiltinImgVolumes  map[string]struct{}   // volumes defined in the image config
+	BuiltinImgVolumes  map[string]struct{} // volumes defined in the image config
+	IDMappings         *storage.IDMappingOptions
 	ImageVolumeType    string                // how to handle the image volume, either bind, tmpfs, or ignore
 	Interactive        bool                  //interactive
 	IpcMode            container.IpcMode     //ipc
@@ -108,8 +111,7 @@ type createConfig struct {
 	Network            string                //network
 	NetworkAlias       []string              //network-alias
 	PidMode            container.PidMode     //pid
-	NsUser             string
-	Pod                string //pod
+	Pod                string                //pod
 	PortBindings       nat.PortMap
 	Privileged         bool     //privileged
 	Publish            []string //publish
@@ -119,20 +121,21 @@ type createConfig struct {
 	Resources          createResourceConfig
 	Rm                 bool //rm
 	ShmDir             string
-	StopSignal         syscall.Signal    // stop-signal
-	StopTimeout        uint              // stop-timeout
-	Sysctl             map[string]string //sysctl
-	Tmpfs              []string          // tmpfs
-	Tty                bool              //tty
-	User               string            //user
-	UtsMode            container.UTSMode //uts
-	Volumes            []string          //volume
-	WorkDir            string            //workdir
-	MountLabel         string            //SecurityOpts
-	ProcessLabel       string            //SecurityOpts
-	NoNewPrivs         bool              //SecurityOpts
-	ApparmorProfile    string            //SecurityOpts
-	SeccompProfilePath string            //SecurityOpts
+	StopSignal         syscall.Signal       // stop-signal
+	StopTimeout        uint                 // stop-timeout
+	Sysctl             map[string]string    //sysctl
+	Tmpfs              []string             // tmpfs
+	Tty                bool                 //tty
+	UsernsMode         container.UsernsMode //userns
+	User               string               //user
+	UtsMode            container.UTSMode    //uts
+	Volumes            []string             //volume
+	WorkDir            string               //workdir
+	MountLabel         string               //SecurityOpts
+	ProcessLabel       string               //SecurityOpts
+	NoNewPrivs         bool                 //SecurityOpts
+	ApparmorProfile    string               //SecurityOpts
+	SeccompProfilePath string               //SecurityOpts
 	SecurityOpts       []string
 }
 
@@ -174,7 +177,15 @@ func createCmd(c *cli.Context) error {
 		return errors.Errorf("image name or ID is required")
 	}
 
-	runtime, err := libpodruntime.GetRuntime(c)
+	mappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidmap"), c.String("subgidmap"))
+	if err != nil {
+		return err
+	}
+	storageOpts := storage.DefaultStoreOptions
+	storageOpts.UIDMap = mappings.UIDMap
+	storageOpts.GIDMap = mappings.GIDMap
+
+	runtime, err := libpodruntime.GetRuntimeWithStorageOpts(c, &storageOpts)
 	if err != nil {
 		return errors.Wrapf(err, "error creating libpod runtime")
 	}
@@ -188,7 +199,7 @@ func createCmd(c *cli.Context) error {
 		return err
 	}
 	data, err := newImage.Inspect(ctx)
-	createConfig, err := parseCreateOpts(c, runtime, newImage.Names()[0], data)
+	createConfig, err := parseCreateOpts(ctx, c, runtime, newImage.Names()[0], data)
 	if err != nil {
 		return err
 	}
@@ -211,6 +222,7 @@ func createCmd(c *cli.Context) error {
 	options = append(options, libpod.WithShmDir(createConfig.ShmDir))
 	options = append(options, libpod.WithShmSize(createConfig.Resources.ShmSize))
 	options = append(options, libpod.WithGroups(createConfig.GroupAdd))
+	options = append(options, libpod.WithIDMappings(*createConfig.IDMappings))
 	ctr, err := runtime.NewContainer(ctx, runtimeSpec, options...)
 	if err != nil {
 		return err
@@ -414,10 +426,16 @@ func getRandomPort() (int, error) {
 
 // Parses CLI options related to container creation into a config which can be
 // parsed into an OCI runtime spec
-func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime, imageName string, data *inspect.ImageData) (*createConfig, error) {
-	var inputCommand, command []string
-	var memoryLimit, memoryReservation, memorySwap, memoryKernel int64
-	var blkioWeight uint16
+func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtime, imageName string, data *inspect.ImageData) (*createConfig, error) {
+	var (
+		inputCommand, command                                    []string
+		memoryLimit, memoryReservation, memorySwap, memoryKernel int64
+		blkioWeight                                              uint16
+	)
+	idmappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidname"), c.String("subgidname"))
+	if err != nil {
+		return nil, err
+	}
 
 	imageID := data.ID
 
@@ -473,6 +491,11 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime, imageName string,
 		return nil, errors.Errorf("--pid %q is not valid", c.String("pid"))
 	}
 
+	usernsMode := container.UsernsMode(c.String("userns"))
+	if !usernsMode.Valid() {
+		return nil, errors.Errorf("--userns %q is not valid", c.String("userns"))
+	}
+
 	if c.Bool("detach") && c.Bool("rm") {
 		return nil, errors.Errorf("--rm and --detach can not be specified together")
 	}
@@ -653,6 +676,7 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime, imageName string,
 		GroupAdd:       c.StringSlice("group-add"),
 		Hostname:       c.String("hostname"),
 		HostAdd:        c.StringSlice("add-host"),
+		IDMappings:     idmappings,
 		Image:          imageName,
 		ImageID:        imageID,
 		Interactive:    c.Bool("interactive"),
@@ -712,6 +736,7 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime, imageName string,
 		Tmpfs:       c.StringSlice("tmpfs"),
 		Tty:         tty,
 		User:        user,
+		UsernsMode:  usernsMode,
 		Volumes:     c.StringSlice("volume"),
 		WorkDir:     workDir,
 	}
diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go
index f54eb6d105..0fd1760a94 100644
--- a/cmd/podman/inspect.go
+++ b/cmd/podman/inspect.go
@@ -225,7 +225,7 @@ func getCtrInspectInfo(ctr *libpod.Container, ctrInspectData *inspect.ContainerI
 			IpcMode:              string(createArtifact.IpcMode),
 			Cgroup:               cgroup,
 			UTSMode:              string(createArtifact.UtsMode),
-			UsernsMode:           createArtifact.NsUser,
+			UsernsMode:           string(createArtifact.UsernsMode),
 			GroupAdd:             spec.Process.User.AdditionalGids,
 			ContainerIDFile:      createArtifact.CidFile,
 			AutoRemove:           createArtifact.Rm,
diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go
index f9c14f6e75..d1657325d6 100644
--- a/cmd/podman/libpodruntime/runtime.go
+++ b/cmd/podman/libpodruntime/runtime.go
@@ -8,27 +8,28 @@ import (
 
 // GetRuntime generates a new libpod runtime configured by command line options
 func GetRuntime(c *cli.Context) (*libpod.Runtime, error) {
+	storageOpts := storage.DefaultStoreOptions
+	return GetRuntimeWithStorageOpts(c, &storageOpts)
+}
+
+// GetRuntime generates a new libpod runtime configured by command line options
+func GetRuntimeWithStorageOpts(c *cli.Context, storageOpts *storage.StoreOptions) (*libpod.Runtime, error) {
 	options := []libpod.RuntimeOption{}
 
-	if c.GlobalIsSet("root") || c.GlobalIsSet("runroot") ||
-		c.GlobalIsSet("storage-opt") || c.GlobalIsSet("storage-driver") {
-		storageOpts := storage.DefaultStoreOptions
-
-		if c.GlobalIsSet("root") {
-			storageOpts.GraphRoot = c.GlobalString("root")
-		}
-		if c.GlobalIsSet("runroot") {
-			storageOpts.RunRoot = c.GlobalString("runroot")
-		}
-		if c.GlobalIsSet("storage-driver") {
-			storageOpts.GraphDriverName = c.GlobalString("storage-driver")
-		}
-		if c.GlobalIsSet("storage-opt") {
-			storageOpts.GraphDriverOptions = c.GlobalStringSlice("storage-opt")
-		}
-
-		options = append(options, libpod.WithStorageConfig(storageOpts))
+	if c.GlobalIsSet("root") {
+		storageOpts.GraphRoot = c.GlobalString("root")
 	}
+	if c.GlobalIsSet("runroot") {
+		storageOpts.RunRoot = c.GlobalString("runroot")
+	}
+	if c.GlobalIsSet("storage-driver") {
+		storageOpts.GraphDriverName = c.GlobalString("storage-driver")
+	}
+	if c.GlobalIsSet("storage-opt") {
+		storageOpts.GraphDriverOptions = c.GlobalStringSlice("storage-opt")
+	}
+
+	options = append(options, libpod.WithStorageConfig(*storageOpts))
 
 	// TODO CLI flags for image config?
 	// TODO CLI flag for signature policy?
diff --git a/cmd/podman/run.go b/cmd/podman/run.go
index 5f0034e90e..06bc0e9df8 100644
--- a/cmd/podman/run.go
+++ b/cmd/podman/run.go
@@ -9,10 +9,12 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/containers/storage"
 	"github.com/pkg/errors"
 	"github.com/projectatomic/libpod/cmd/podman/libpodruntime"
 	"github.com/projectatomic/libpod/libpod"
 	"github.com/projectatomic/libpod/libpod/image"
+	"github.com/projectatomic/libpod/pkg/util"
 	"github.com/sirupsen/logrus"
 	"github.com/urfave/cli"
 )
@@ -50,7 +52,15 @@ func runCmd(c *cli.Context) error {
 		}
 	}
 
-	runtime, err := libpodruntime.GetRuntime(c)
+	storageOpts := storage.DefaultStoreOptions
+	mappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidmap"), c.String("subgidmap"))
+	if err != nil {
+		return err
+	}
+	storageOpts.UIDMap = mappings.UIDMap
+	storageOpts.GIDMap = mappings.GIDMap
+
+	runtime, err := libpodruntime.GetRuntimeWithStorageOpts(c, &storageOpts)
 	if err != nil {
 		return errors.Wrapf(err, "error creating libpod runtime")
 	}
@@ -60,7 +70,6 @@ func runCmd(c *cli.Context) error {
 	}
 
 	ctx := getContext()
-
 	rtc := runtime.GetConfig()
 	newImage, err := runtime.ImageRuntime().New(ctx, c.Args()[0], rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}, false, false)
 	if err != nil {
@@ -76,7 +85,7 @@ func runCmd(c *cli.Context) error {
 	} else {
 		imageName = newImage.Names()[0]
 	}
-	createConfig, err := parseCreateOpts(c, runtime, imageName, data)
+	createConfig, err := parseCreateOpts(ctx, c, runtime, imageName, data)
 	if err != nil {
 		return err
 	}
@@ -101,6 +110,7 @@ func runCmd(c *cli.Context) error {
 	options = append(options, libpod.WithShmDir(createConfig.ShmDir))
 	options = append(options, libpod.WithShmSize(createConfig.Resources.ShmSize))
 	options = append(options, libpod.WithGroups(createConfig.GroupAdd))
+	options = append(options, libpod.WithIDMappings(*createConfig.IDMappings))
 
 	// Default used if not overridden on command line
 
diff --git a/cmd/podman/run_test.go b/cmd/podman/run_test.go
index 3baee46156..bbcdcc60a1 100644
--- a/cmd/podman/run_test.go
+++ b/cmd/podman/run_test.go
@@ -75,7 +75,8 @@ func getRuntimeSpec(c *cli.Context) (*spec.Spec, error) {
 		}
 		createConfig, err := parseCreateOpts(c, runtime, "alpine", generateAlpineImageData())
 	*/
-	createConfig, err := parseCreateOpts(c, nil, "alpine", generateAlpineImageData())
+	ctx := getContext()
+	createConfig, err := parseCreateOpts(ctx, c, nil, "alpine", generateAlpineImageData())
 	if err != nil {
 		return nil, err
 	}
diff --git a/cmd/podman/spec.go b/cmd/podman/spec.go
index fc2ab267df..15dab6c4d8 100644
--- a/cmd/podman/spec.go
+++ b/cmd/podman/spec.go
@@ -66,6 +66,13 @@ func addPidNS(config *createConfig, g *generate.Generator) error {
 	return nil
 }
 
+func addUserNS(config *createConfig, g *generate.Generator) error {
+	if (len(config.IDMappings.UIDMap) > 0 || len(config.IDMappings.GIDMap) > 0) && !config.UsernsMode.IsHost() {
+		g.AddOrReplaceLinuxNamespace(spec.UserNamespace, "")
+	}
+	return nil
+}
+
 func addNetNS(config *createConfig, g *generate.Generator) error {
 	netMode := config.NetMode
 	if netMode.IsHost() {
@@ -257,6 +264,12 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
 		}
 	}
 
+	for _, uidmap := range config.IDMappings.UIDMap {
+		g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
+	}
+	for _, gidmap := range config.IDMappings.GIDMap {
+		g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
+	}
 	// SECURITY OPTS
 	g.SetProcessNoNewPrivileges(config.NoNewPrivs)
 	g.SetProcessApparmorProfile(config.ApparmorProfile)
@@ -300,6 +313,10 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
 		return nil, err
 	}
 
+	if err := addUserNS(config, &g); err != nil {
+		return nil, err
+	}
+
 	if err := addNetNS(config, &g); err != nil {
 		return nil, err
 	}
diff --git a/completions/bash/podman b/completions/bash/podman
index cafb4d8c8b..45e5c9a154 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1072,6 +1072,7 @@ _podman_container_run() {
 		--env -e
 		--env-file
 		--expose
+		--gidmap
 		--group-add
 		--hostname -h
 		--image-volume
@@ -1099,7 +1100,10 @@ _podman_container_run() {
 		--stop-signal
 		--stop-timeout
 		--tmpfs
+		--subgidname
+		--subuidname
 		--sysctl
+		--uidmap
 		--ulimit
 		--user -u
 		--userns
diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md
index 457f54eddb..1291f39c67 100644
--- a/docs/podman-create.1.md
+++ b/docs/podman-create.1.md
@@ -209,6 +209,11 @@ inside of the container.
    Expose a port, or a range of ports (e.g. --expose=3300-3310) to set up port redirection
    on the host system.
 
+**--gidmap**=map
+   GID map for the user namespace.  Using this flag will run the container with user namespace enabled.  It conflicts with the `--userns` and `--subgidname` flags.
+
+   The following example maps uids 0-2000 in the container to the uids 30000-31999 on the host and gids 0-2000 in the container to the gids 30000-31999 on the host.
+
 **--group-add**=[]
    Add additional groups to run as
 
@@ -223,9 +228,9 @@ inside of the container.
 **--image-volume**, **builtin-volume**=*bind*|*tmpfs*|*ignore*
     Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore' (default 'bind').
     bind: A directory is created inside the container state directory and bind mounted into
-        the container for the volumes.
+	the container for the volumes.
     tmpfs: The volume is mounted onto the container as a tmpfs, which allows the users to create
-        content that disappears when the container is stopped.
+	content that disappears when the container is stopped.
     ignore: All volumes are just ignored and no action is taken.
 
 **-i**, **--interactive**=*true*|*false*
@@ -424,6 +429,12 @@ its root filesystem mounted as read only prohibiting any writes.
 **--stop-timeout**=*10*
   Timeout (in seconds) to stop a container. Default is 10.
 
+**--subgidname**=name
+   Name for GID map from the `/etc/subgid` file.  Using this flag will run the container with user namespace enabled.  This flag conflicts with `--userns` and `--gidmap`.
+
+**--subuidname**=name
+   Name for UID map from the `/etc/subuid` file.  Using this flag will run the container with user namespace enabled.  This flag conflicts with `--userns` and `--uidmap`.
+
 **--sysctl**=SYSCTL
   Configure namespaced kernel parameters at runtime
 
@@ -460,6 +471,11 @@ interactive shell. The default is false.
 Note: The **-t** option is incompatible with a redirection of the podman client
 standard input.
 
+**--uidmap**=map
+   UID map for the user namespace.  Using this flag will run the container with user namespace enabled.  It conflicts with the `--userns` and `--subuidname` flags.
+
+   The following example maps uids 0-2000 in the container to the uids 30000-31999 on the host and gids 0-2000 in the container to the gids 30000-31999 on the host.
+
 **--ulimit**=[]
    Ulimit options
 
@@ -472,7 +488,8 @@ standard input.
    Without this argument the command will be run as root in the container.
 
 **--userns**=""
-   Set the usernamespace mode for the container when `userns-remap` option is enabled.
+   Set the usernamespace mode for the container. The use of userns is disabled by default.
+
      **host**: use the host usernamespace and enable all privileged options (e.g., `pid=host` or `--privileged`).
 
 **--uts**=*host*
@@ -556,6 +573,21 @@ can override the working directory by using the **-w** option.
 
 ## EXAMPLES
 
+### Set UID/GID mapping in a new user namespace
+
+If you want to run the container in a new user namespace and define the mapping of
+the uid and gid from the host.
+
+    # podman create --uidmap 0:30000:7000 --gidmap 0:30000:7000 fedora echo hello
+
+## FILES
+
+**/etc/subuid**
+**/etc/subgid**
+
+## SEE ALSO
+SUBGID(5), SUBUID(5),
+
 ## HISTORY
 August 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
 September 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index a198fd9d4d..df79114f6a 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -214,6 +214,11 @@ inside of the container.
    Expose a port, or a range of ports (e.g. --expose=3300-3310) to set up port redirection
    on the host system.
 
+**--gidmap**=map
+   GID map for the user namespace.  Using this flag will run the container with user namespace enabled.  It conflicts with the `--userns` and `--subgidname` flags.
+
+   The following example maps uids 0-2000 in the container to the uids 30000-31999 on the host and gids 0-2000 in the container to the gids 30000-31999 on the host.
+
 **--group-add**=[]
    Add additional groups to run as
 
@@ -228,9 +233,9 @@ inside of the container.
 **--image-volume**, **builtin-volume**=*bind*|*tmpfs*|*ignore*
     Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore' (default 'bind')
     bind: A directory is created inside the container state directory and bind mounted into
-        the container for the volumes.
+	the container for the volumes.
     tmpfs: The volume is mounted onto the container as a tmpfs, which allows the users to create
-        content that disappears when the container is stopped.
+	content that disappears when the container is stopped.
     ignore: All volumes are just ignored and no action is taken.
 
 **-i**, **--interactive**=*true*|*false*
@@ -435,6 +440,12 @@ its root filesystem mounted as read only prohibiting any writes.
 **--stop-timeout**=*10*
   Timeout (in seconds) to stop a container. Default is 10.
 
+**--subgidname**=name
+   Name for GID map from the `/etc/subgid` file.  Using this flag will run the container with user namespace enabled.  This flag conflicts with `--userns` and `--gidmap`.
+
+**--subuidname**=name
+   Name for UID map from the `/etc/subuid` file.  Using this flag will run the container with user namespace enabled.  This flag conflicts with `--userns` and `--uidmap`.
+
 **--sysctl**=SYSCTL
   Configure namespaced kernel parameters at runtime
 
@@ -471,6 +482,11 @@ interactive shell. The default is false.
 Note: The **-t** option is incompatible with a redirection of the podman client
 standard input.
 
+**--uidmap**=map
+   UID map for the user namespace.  Using this flag will run the container with user namespace enabled.  It conflicts with the `--userns` and `--subuidname` flags.
+
+   The following example maps uids 0-2000 in the container to the uids 30000-31999 on the host and gids 0-2000 in the container to the gids 30000-31999 on the host.
+
 **--ulimit**=[]
     Ulimit options
 
@@ -483,7 +499,8 @@ standard input.
    Without this argument the command will be run as root in the container.
 
 **--userns**=""
-   Set the usernamespace mode for the container when `userns-remap` option is enabled.
+   Set the usernamespace mode for the container. The use of userns is disabled by default.
+
      **host**: use the host usernamespace and enable all privileged options (e.g., `pid=host` or `--privileged`).
 
 **--uts**=*host*
@@ -793,6 +810,21 @@ evolves we expect to see more sysctls become namespaced.
 See the definition of the `--sysctl` option above for the current list of
 supported sysctls.
 
+### Set UID/GID mapping in a new user namespace
+
+If you want to run the container in a new user namespace and define the mapping of
+the uid and gid from the host.
+
+    # podman run --uidmap 0:30000:7000 --gidmap 0:30000:7000 fedora echo hello
+
+## FILES
+
+**/etc/subuid**
+**/etc/subgid**
+
+## SEE ALSO
+SUBGID(5), SUBUID(5),
+
 ## HISTORY
 April 2014, Originally compiled by William Henry (whenry at redhat dot com)
 based on docker.com source material and internal work.
diff --git a/docs/podman.1.md b/docs/podman.1.md
index 1ea6603a77..3e0c59232a 100644
--- a/docs/podman.1.md
+++ b/docs/podman.1.md
@@ -96,7 +96,6 @@ has the capability to debug pods/images created by crio.
 
 ## FILES
 
-
 **libpod.conf** (`/etc/containers/libpod.conf`)
 
 	libpod.conf is the configuration file for all tools using libpod to manage containers
diff --git a/libpod/container.go b/libpod/container.go
index e7fe77498b..fb1f83c29b 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -173,7 +173,8 @@ type ContainerConfig struct {
 
 	// TODO consider breaking these subsections up into smaller structs
 
-	// Storage Config
+	// UID/GID mappings used by the storage
+	IDMappings storage.IDMappingOptions `json:"idMappingsOptions,omitempty"`
 
 	// Information on the image used for the root filesystem/
 	RootfsImageID   string `json:"rootfsImageID,omitempty"`
@@ -863,3 +864,28 @@ func (c *Container) RWSize() (int64, error) {
 	}
 	return c.rwSize()
 }
+
+// IDMappings returns the UID/GID mapping used for the container
+func (c *Container) IDMappings() (storage.IDMappingOptions, error) {
+	return c.config.IDMappings, nil
+}
+
+// RootUID returns the root user mapping from container
+func (c *Container) RootUID() int {
+	for _, uidmap := range c.config.IDMappings.UIDMap {
+		if uidmap.ContainerID == 0 {
+			return uidmap.HostID
+		}
+	}
+	return 0
+}
+
+// RootGID returns the root user mapping from container
+func (c *Container) RootGID() int {
+	for _, gidmap := range c.config.IDMappings.GIDMap {
+		if gidmap.ContainerID == 0 {
+			return gidmap.HostID
+		}
+	}
+	return 0
+}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 614c6aca09..73095316e5 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -190,7 +190,8 @@ func (c *Container) setupStorage(ctx context.Context) error {
 		return errors.Wrapf(ErrInvalidArg, "must provide image ID and image name to use an image")
 	}
 
-	containerInfo, err := c.runtime.storageService.CreateContainerStorage(ctx, c.runtime.imageContext, c.config.RootfsImageName, c.config.RootfsImageID, c.config.Name, c.config.ID, c.config.MountLabel)
+	options := storage.ContainerOptions{IDMappingOptions: c.config.IDMappings}
+	containerInfo, err := c.runtime.storageService.CreateContainerStorage(ctx, c.runtime.imageContext, c.config.RootfsImageName, c.config.RootfsImageID, c.config.Name, c.config.ID, c.config.MountLabel, &options)
 	if err != nil {
 		return errors.Wrapf(err, "error creating container storage")
 	}
@@ -591,6 +592,9 @@ func (c *Container) mountStorage() (err error) {
 			label.FormatMountLabel(shmOptions, c.config.MountLabel)); err != nil {
 			return errors.Wrapf(err, "failed to mount shm tmpfs %q", c.config.ShmDir)
 		}
+		if err := os.Chown(c.config.ShmDir, c.RootUID(), c.RootGID()); err != nil {
+			return err
+		}
 	}
 
 	mountPoint, err := c.runtime.storageService.MountContainerImage(c.ID())
@@ -755,7 +759,7 @@ func (c *Container) makeBindMounts() error {
 	}
 
 	// Add Secret Mounts
-	secretMounts := secrets.SecretMounts(c.config.MountLabel, c.state.RunDir, c.runtime.config.DefaultMountsFile)
+	secretMounts := secrets.SecretMountsWithUIDGID(c.config.MountLabel, c.state.RunDir, c.runtime.config.DefaultMountsFile, c.RootUID(), c.RootGID())
 	for _, mount := range secretMounts {
 		if _, ok := c.state.BindMounts[mount.Destination]; !ok {
 			c.state.BindMounts[mount.Destination] = mount.Source
@@ -772,10 +776,12 @@ func (c *Container) writeStringToRundir(destFile, output string) (string, error)
 	if err != nil {
 		return "", errors.Wrapf(err, "unable to create %s", destFileName)
 	}
-
 	defer f.Close()
-	_, err = f.WriteString(output)
-	if err != nil {
+	if err := f.Chown(c.RootUID(), c.RootGID()); err != nil {
+		return "", err
+	}
+
+	if _, err := f.WriteString(output); err != nil {
 		return "", errors.Wrapf(err, "unable to write %s", destFileName)
 	}
 	// Relabel runDirResolv for the container
diff --git a/libpod/options.go b/libpod/options.go
index 101ff98335..eaca70afc5 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -459,6 +459,18 @@ func WithStopTimeout(timeout uint) CtrCreateOption {
 	}
 }
 
+// WithIDMappings sets the idmappsings for the container
+func WithIDMappings(idmappings storage.IDMappingOptions) CtrCreateOption {
+	return func(ctr *Container) error {
+		if ctr.valid {
+			return ErrCtrFinalized
+		}
+
+		ctr.config.IDMappings = idmappings
+		return nil
+	}
+}
+
 // WithIPCNSFrom indicates the the container should join the IPC namespace of
 // the given container.
 // If the container has joined a pod, it can only join the namespaces of
diff --git a/libpod/storage.go b/libpod/storage.go
index 910db1970f..c0391326cf 100644
--- a/libpod/storage.go
+++ b/libpod/storage.go
@@ -59,7 +59,7 @@ func (metadata *RuntimeContainerMetadata) SetMountLabel(mountLabel string) {
 
 // CreateContainerStorage creates the storage end of things.  We already have the container spec created
 // TO-DO We should be passing in an Image object in the future.
-func (r *storageService) CreateContainerStorage(ctx context.Context, systemContext *types.SystemContext, imageName, imageID, containerName, containerID, mountLabel string) (ContainerInfo, error) {
+func (r *storageService) CreateContainerStorage(ctx context.Context, systemContext *types.SystemContext, imageName, imageID, containerName, containerID, mountLabel string, options *storage.ContainerOptions) (ContainerInfo, error) {
 	var ref types.ImageReference
 	if imageName == "" && imageID == "" {
 		return ContainerInfo{}, ErrEmptyID
@@ -111,13 +111,15 @@ func (r *storageService) CreateContainerStorage(ctx context.Context, systemConte
 	// Build the container.
 	names := []string{containerName}
 
-	options := storage.ContainerOptions{
-		IDMappingOptions: storage.IDMappingOptions{
-			HostUIDMapping: true,
-			HostGIDMapping: true,
-		},
+	if options == nil {
+		options = &storage.ContainerOptions{
+			IDMappingOptions: storage.IDMappingOptions{
+				HostUIDMapping: true,
+				HostGIDMapping: true,
+			},
+		}
 	}
-	container, err := r.store.CreateContainer(containerID, names, img.ID, "", string(mdata), &options)
+	container, err := r.store.CreateContainer(containerID, names, img.ID, "", string(mdata), options)
 	if err != nil {
 		logrus.Debugf("failed to create container %s(%s): %v", metadata.ContainerName, containerID, err)
 
diff --git a/pkg/secrets/secrets.go b/pkg/secrets/secrets.go
index 04890c06a4..29ccd4592c 100644
--- a/pkg/secrets/secrets.go
+++ b/pkg/secrets/secrets.go
@@ -127,7 +127,12 @@ func getMountsMap(path string) (string, string, error) {
 }
 
 // SecretMounts copies, adds, and mounts the secrets to the container root filesystem
-func SecretMounts(mountLabel, containerWorkingDir string, mountFile string) []rspec.Mount {
+func SecretMounts(mountLabel, containerWorkingDir, mountFile string) []rspec.Mount {
+	return SecretMountsWithUIDGID(mountLabel, containerWorkingDir, mountFile, 0, 0)
+}
+
+// SecretMountsWithUIDGID specifies the uid/gid of the owner
+func SecretMountsWithUIDGID(mountLabel, containerWorkingDir, mountFile string, uid, gid int) []rspec.Mount {
 	var (
 		secretMounts []rspec.Mount
 		mountFiles   []string
@@ -141,7 +146,7 @@ func SecretMounts(mountLabel, containerWorkingDir string, mountFile string) []rs
 		mountFiles = append(mountFiles, mountFile)
 	}
 	for _, file := range mountFiles {
-		mounts, err := addSecretsFromMountsFile(file, mountLabel, containerWorkingDir)
+		mounts, err := addSecretsFromMountsFile(file, mountLabel, containerWorkingDir, uid, gid)
 		if err != nil {
 			logrus.Warnf("error mounting secrets, skipping: %v", err)
 		}
@@ -162,9 +167,15 @@ func SecretMounts(mountLabel, containerWorkingDir string, mountFile string) []rs
 	return secretMounts
 }
 
+func rchown(chowndir string, uid, gid int) error {
+	return filepath.Walk(chowndir, func(filePath string, f os.FileInfo, err error) error {
+		return os.Lchown(filePath, uid, gid)
+	})
+}
+
 // addSecretsFromMountsFile copies the contents of host directory to container directory
 // and returns a list of mounts
-func addSecretsFromMountsFile(filePath, mountLabel, containerWorkingDir string) ([]rspec.Mount, error) {
+func addSecretsFromMountsFile(filePath, mountLabel, containerWorkingDir string, uid, gid int) ([]rspec.Mount, error) {
 	var mounts []rspec.Mount
 	defaultMountsPaths := getMounts(filePath)
 	for _, path := range defaultMountsPaths {
@@ -186,7 +197,6 @@ func addSecretsFromMountsFile(filePath, mountLabel, containerWorkingDir string)
 			if err = os.MkdirAll(ctrDirOnHost, 0755); err != nil {
 				return nil, errors.Wrapf(err, "making container directory failed")
 			}
-
 			hostDir, err = resolveSymbolicLink(hostDir)
 			if err != nil {
 				return nil, err
@@ -206,6 +216,11 @@ func addSecretsFromMountsFile(filePath, mountLabel, containerWorkingDir string)
 			if err != nil {
 				return nil, errors.Wrap(err, "error applying correct labels")
 			}
+			if uid != 0 || gid != 0 {
+				if err := rchown(ctrDirOnHost, uid, gid); err != nil {
+					return nil, err
+				}
+			}
 		} else if err != nil {
 			return nil, errors.Wrapf(err, "error getting status of %q", ctrDirOnHost)
 		}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 1bbfe30d38..a29a1ee60e 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -2,9 +2,12 @@ package util
 
 import (
 	"fmt"
+	"strconv"
 	"strings"
 
 	"github.com/containers/image/types"
+	"github.com/containers/storage"
+	"github.com/containers/storage/pkg/idtools"
 	"github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
 	"golang.org/x/crypto/ssh/terminal"
@@ -120,3 +123,76 @@ func GetImageConfig(changes []string) (v1.ImageConfig, error) {
 		StopSignal:   stopSignal,
 	}, nil
 }
+
+// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
+func ParseIDMapping(UIDMapSlice, GIDMapSlice []string, subUIDMap, subGIDMap string) (*storage.IDMappingOptions, error) {
+	options := storage.IDMappingOptions{
+		HostUIDMapping: true,
+		HostGIDMapping: true,
+	}
+	if subGIDMap == "" && subUIDMap != "" {
+		subGIDMap = subUIDMap
+	}
+	if subUIDMap == "" && subGIDMap != "" {
+		subUIDMap = subGIDMap
+	}
+	parseTriple := func(spec []string) (container, host, size int, err error) {
+		cid, err := strconv.ParseUint(spec[0], 10, 32)
+		if err != nil {
+			return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[0], err)
+		}
+		hid, err := strconv.ParseUint(spec[1], 10, 32)
+		if err != nil {
+			return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[1], err)
+		}
+		sz, err := strconv.ParseUint(spec[2], 10, 32)
+		if err != nil {
+			return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[2], err)
+		}
+		return int(cid), int(hid), int(sz), nil
+	}
+	parseIDMap := func(spec []string) (idmap []idtools.IDMap, err error) {
+		for _, uid := range spec {
+			splitmap := strings.SplitN(uid, ":", 3)
+			if len(splitmap) < 3 {
+				return nil, fmt.Errorf("invalid mapping requires 3 fields: %q", uid)
+			}
+			cid, hid, size, err := parseTriple(splitmap)
+			if err != nil {
+				return nil, err
+			}
+			pmap := idtools.IDMap{
+				ContainerID: cid,
+				HostID:      hid,
+				Size:        size,
+			}
+			idmap = append(idmap, pmap)
+		}
+		return idmap, nil
+	}
+	if subUIDMap != "" && subGIDMap != "" {
+		mappings, err := idtools.NewIDMappings(subUIDMap, subGIDMap)
+		if err != nil {
+			return nil, err
+		}
+		options.UIDMap = mappings.UIDs()
+		options.GIDMap = mappings.GIDs()
+	}
+	parsedUIDMap, err := parseIDMap(UIDMapSlice)
+	if err != nil {
+		return nil, err
+	}
+	parsedGIDMap, err := parseIDMap(GIDMapSlice)
+	if err != nil {
+		return nil, err
+	}
+	options.UIDMap = append(options.UIDMap, parsedUIDMap...)
+	options.GIDMap = append(options.GIDMap, parsedGIDMap...)
+	if len(options.UIDMap) > 0 {
+		options.HostUIDMapping = false
+	}
+	if len(options.GIDMap) > 0 {
+		options.HostGIDMapping = false
+	}
+	return &options, nil
+}