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:
Daniel J Walsh
2018-04-23 20:42:53 -04:00
committed by Atomic Bot
parent 1f5debd438
commit b51d737998
17 changed files with 345 additions and 70 deletions

View File

@ -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 [])",

View File

@ -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,
}

View File

@ -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,

View File

@ -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?

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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>

View File

@ -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.

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)
}

View File

@ -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
}