mirror of
https://github.com/containers/podman.git
synced 2025-05-29 06:03:25 +08:00
Begin wiring in USERNS Support into podman
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com> Closes: #690 Approved by: mheon
This commit is contained in:

committed by
Atomic Bot

parent
1f5debd438
commit
b51d737998
@ -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 [])",
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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?
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Reference in New Issue
Block a user