mirror of
https://github.com/opencontainers/runc.git
synced 2025-09-26 19:41:35 +08:00
mount: add support for ridmap and idmap
ridmap indicates that the id mapping should be applied recursively (only
really relevant for rbind mount entries), and idmap indicates that it
should not be applied recursively (the default). If no mappings are
specified for the mount, we use the userns configuration of the
container. This matches the behaviour in the currently-unreleased
runtime-spec.
This includes a minor change to the state.json serialisation format, but
because there has been no released version of runc with commit
fbf183c6f8
("Add uid and gid mappings to mounts"), we can safely make
this change without affecting running containers. Doing it this way
makes it much easier to handle m.IsIDMapped() and indicating that a
mapping has been specified.
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
This commit is contained in:
@@ -3,5 +3,5 @@ package configs
|
||||
const (
|
||||
// EXT_COPYUP is a directive to copy up the contents of a directory when
|
||||
// a tmpfs is mounted over it.
|
||||
EXT_COPYUP = 1 << iota //nolint:golint // ignore "don't use ALL_CAPS" warning
|
||||
EXT_COPYUP = 1 << iota //nolint:golint,revive // ignore "don't use ALL_CAPS" warning
|
||||
)
|
||||
|
@@ -2,6 +2,24 @@ package configs
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
type MountIDMapping struct {
|
||||
// Recursive indicates if the mapping needs to be recursive.
|
||||
Recursive bool `json:"recursive"`
|
||||
|
||||
// UserNSPath is a path to a user namespace that indicates the necessary
|
||||
// id-mappings for MOUNT_ATTR_IDMAP. If set to non-"", UIDMappings and
|
||||
// GIDMappings must be set to nil.
|
||||
UserNSPath string `json:"userns_path,omitempty"`
|
||||
|
||||
// UIDMappings is the uid mapping set for this mount, to be used with
|
||||
// MOUNT_ATTR_IDMAP.
|
||||
UIDMappings []IDMap `json:"uid_mappings,omitempty"`
|
||||
|
||||
// GIDMappings is the gid mapping set for this mount, to be used with
|
||||
// MOUNT_ATTR_IDMAP.
|
||||
GIDMappings []IDMap `json:"gid_mappings,omitempty"`
|
||||
}
|
||||
|
||||
type Mount struct {
|
||||
// Source path for the mount.
|
||||
Source string `json:"source"`
|
||||
@@ -34,17 +52,9 @@ type Mount struct {
|
||||
// Extensions are additional flags that are specific to runc.
|
||||
Extensions int `json:"extensions"`
|
||||
|
||||
// UIDMappings is used to changing file user owners w/o calling chown.
|
||||
// Note that, the underlying filesystem should support this feature to be
|
||||
// used.
|
||||
// Every mount point could have its own mapping.
|
||||
UIDMappings []IDMap `json:"uid_mappings,omitempty"`
|
||||
|
||||
// GIDMappings is used to changing file group owners w/o calling chown.
|
||||
// Note that, the underlying filesystem should support this feature to be
|
||||
// used.
|
||||
// Every mount point could have its own mapping.
|
||||
GIDMappings []IDMap `json:"gid_mappings,omitempty"`
|
||||
// Mapping is the MOUNT_ATTR_IDMAP configuration for the mount. If non-nil,
|
||||
// the mount is configured to use MOUNT_ATTR_IDMAP-style id mappings.
|
||||
IDMapping *MountIDMapping `json:"id_mapping,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Mount) IsBind() bool {
|
||||
@@ -52,5 +62,5 @@ func (m *Mount) IsBind() bool {
|
||||
}
|
||||
|
||||
func (m *Mount) IsIDMapped() bool {
|
||||
return len(m.UIDMappings) > 0 || len(m.GIDMappings) > 0
|
||||
return m.IDMapping != nil
|
||||
}
|
||||
|
@@ -309,6 +309,13 @@ func checkBindOptions(m *configs.Mount) error {
|
||||
}
|
||||
|
||||
func checkIDMapMounts(config *configs.Config, m *configs.Mount) error {
|
||||
// Make sure MOUNT_ATTR_IDMAP is not set on any of our mounts. This
|
||||
// attribute is handled differently to all other attributes (through
|
||||
// m.IDMapping), so make sure we never store it in the actual config. This
|
||||
// really shouldn't ever happen.
|
||||
if m.RecAttr != nil && (m.RecAttr.Attr_set|m.RecAttr.Attr_clr)&unix.MOUNT_ATTR_IDMAP != 0 {
|
||||
return errors.New("mount configuration cannot contain recAttr for MOUNT_ATTR_IDMAP")
|
||||
}
|
||||
if !m.IsIDMapped() {
|
||||
return nil
|
||||
}
|
||||
@@ -318,6 +325,16 @@ func checkIDMapMounts(config *configs.Config, m *configs.Mount) error {
|
||||
if config.RootlessEUID {
|
||||
return errors.New("id-mapped mounts are not supported for rootless containers")
|
||||
}
|
||||
if m.IDMapping.UserNSPath == "" {
|
||||
if len(m.IDMapping.UIDMappings) == 0 || len(m.IDMapping.GIDMappings) == 0 {
|
||||
return errors.New("id-mapped mounts must have both uid and gid mappings specified")
|
||||
}
|
||||
} else {
|
||||
if m.IDMapping.UIDMappings != nil || m.IDMapping.GIDMappings != nil {
|
||||
// should never happen
|
||||
return errors.New("[internal error] id-mapped mounts cannot have both userns_path and uid and gid mappings specified")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -504,17 +504,82 @@ func TestValidateIDMapMounts(t *testing.T) {
|
||||
config *configs.Config
|
||||
}{
|
||||
{
|
||||
name: "idmap mount without bind opt specified",
|
||||
name: "idmap non-bind mount",
|
||||
isErr: true,
|
||||
config: &configs.Config{
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
Mounts: []*configs.Mount{
|
||||
{
|
||||
Source: "/dev/sda1",
|
||||
Destination: "/abs/path/",
|
||||
Device: "ext4",
|
||||
IDMapping: &configs.MountIDMapping{
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "idmap option non-bind mount",
|
||||
isErr: true,
|
||||
config: &configs.Config{
|
||||
Mounts: []*configs.Mount{
|
||||
{
|
||||
Source: "/dev/sda1",
|
||||
Destination: "/abs/path/",
|
||||
Device: "ext4",
|
||||
IDMapping: &configs.MountIDMapping{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ridmap option non-bind mount",
|
||||
isErr: true,
|
||||
config: &configs.Config{
|
||||
Mounts: []*configs.Mount{
|
||||
{
|
||||
Source: "/dev/sda1",
|
||||
Destination: "/abs/path/",
|
||||
Device: "ext4",
|
||||
IDMapping: &configs.MountIDMapping{
|
||||
Recursive: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "idmap mount no uid mapping",
|
||||
isErr: true,
|
||||
config: &configs.Config{
|
||||
Mounts: []*configs.Mount{
|
||||
{
|
||||
Source: "/abs/path/",
|
||||
Destination: "/abs/path/",
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
Flags: unix.MS_BIND,
|
||||
IDMapping: &configs.MountIDMapping{
|
||||
GIDMappings: mapping,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "idmap mount no gid mapping",
|
||||
isErr: true,
|
||||
config: &configs.Config{
|
||||
Mounts: []*configs.Mount{
|
||||
{
|
||||
Source: "/abs/path/",
|
||||
Destination: "/abs/path/",
|
||||
Flags: unix.MS_BIND,
|
||||
IDMapping: &configs.MountIDMapping{
|
||||
UIDMappings: mapping,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -531,8 +596,10 @@ func TestValidateIDMapMounts(t *testing.T) {
|
||||
Source: "/abs/path/",
|
||||
Destination: "/abs/path/",
|
||||
Flags: unix.MS_BIND,
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
IDMapping: &configs.MountIDMapping{
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -547,8 +614,10 @@ func TestValidateIDMapMounts(t *testing.T) {
|
||||
Source: "./rel/path/",
|
||||
Destination: "/abs/path/",
|
||||
Flags: unix.MS_BIND,
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
IDMapping: &configs.MountIDMapping{
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -563,8 +632,10 @@ func TestValidateIDMapMounts(t *testing.T) {
|
||||
Source: "/abs/path/",
|
||||
Destination: "./rel/path/",
|
||||
Flags: unix.MS_BIND,
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
IDMapping: &configs.MountIDMapping{
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -579,8 +650,10 @@ func TestValidateIDMapMounts(t *testing.T) {
|
||||
Source: "/another-abs/path/",
|
||||
Destination: "/abs/path/",
|
||||
Flags: unix.MS_BIND,
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
IDMapping: &configs.MountIDMapping{
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -595,8 +668,10 @@ func TestValidateIDMapMounts(t *testing.T) {
|
||||
Source: "/another-abs/path/",
|
||||
Destination: "/abs/path/",
|
||||
Flags: unix.MS_BIND | unix.MS_RDONLY,
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
IDMapping: &configs.MountIDMapping{
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -609,8 +684,10 @@ func TestValidateIDMapMounts(t *testing.T) {
|
||||
Source: "/abs/path/",
|
||||
Destination: "/abs/path/",
|
||||
Flags: unix.MS_BIND,
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
IDMapping: &configs.MountIDMapping{
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -625,35 +702,69 @@ func TestValidateIDMapMounts(t *testing.T) {
|
||||
Source: "/abs/path/",
|
||||
Destination: "/abs/path/",
|
||||
Flags: unix.MS_BIND,
|
||||
UIDMappings: []configs.IDMap{
|
||||
{
|
||||
ContainerID: 10,
|
||||
HostID: 10,
|
||||
Size: 1,
|
||||
IDMapping: &configs.MountIDMapping{
|
||||
UIDMappings: []configs.IDMap{
|
||||
{
|
||||
ContainerID: 10,
|
||||
HostID: 10,
|
||||
Size: 1,
|
||||
},
|
||||
},
|
||||
GIDMappings: mapping,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "idmap mounts with different userns and mount mappings",
|
||||
config: &configs.Config{
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
Mounts: []*configs.Mount{
|
||||
{
|
||||
Source: "/abs/path/",
|
||||
Destination: "/abs/path/",
|
||||
Flags: unix.MS_BIND,
|
||||
IDMapping: &configs.MountIDMapping{
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: []configs.IDMap{
|
||||
{
|
||||
ContainerID: 10,
|
||||
HostID: 10,
|
||||
Size: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
GIDMappings: mapping,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "idmap mounts with different userns and mount mappings",
|
||||
name: "mount with 'idmap' option but no mappings",
|
||||
isErr: true,
|
||||
config: &configs.Config{
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: mapping,
|
||||
Mounts: []*configs.Mount{
|
||||
{
|
||||
Source: "/abs/path/",
|
||||
Destination: "/abs/path/",
|
||||
Flags: unix.MS_BIND,
|
||||
UIDMappings: mapping,
|
||||
GIDMappings: []configs.IDMap{
|
||||
{
|
||||
ContainerID: 10,
|
||||
HostID: 10,
|
||||
Size: 1,
|
||||
},
|
||||
IDMapping: &configs.MountIDMapping{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mount with 'ridmap' option but no mappings",
|
||||
isErr: true,
|
||||
config: &configs.Config{
|
||||
Mounts: []*configs.Mount{
|
||||
{
|
||||
Source: "/abs/path/",
|
||||
Destination: "/abs/path/",
|
||||
Flags: unix.MS_BIND,
|
||||
IDMapping: &configs.MountIDMapping{
|
||||
Recursive: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -229,16 +229,32 @@ func mountFd(nsHandles *userns.Handles, m *configs.Mount) (*mountSource, error)
|
||||
sourceType = mountSourceOpenTree
|
||||
|
||||
// Configure the id mapping.
|
||||
usernsFile, err := nsHandles.Get(userns.Mapping{
|
||||
UIDMappings: m.UIDMappings,
|
||||
GIDMappings: m.GIDMappings,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create userns for %s id-mapping: %w", m.Source, err)
|
||||
var usernsFile *os.File
|
||||
if m.IDMapping.UserNSPath == "" {
|
||||
usernsFile, err = nsHandles.Get(userns.Mapping{
|
||||
UIDMappings: m.IDMapping.UIDMappings,
|
||||
GIDMappings: m.IDMapping.GIDMappings,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create userns for %s id-mapping: %w", m.Source, err)
|
||||
}
|
||||
} else {
|
||||
usernsFile, err = os.Open(m.IDMapping.UserNSPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open existing userns for %s id-mapping: %w", m.Source, err)
|
||||
}
|
||||
}
|
||||
defer usernsFile.Close()
|
||||
|
||||
if err := unix.MountSetattr(int(mountFile.Fd()), "", unix.AT_EMPTY_PATH, &unix.MountAttr{
|
||||
setAttrFlags := uint(unix.AT_EMPTY_PATH)
|
||||
// If the mount has "ridmap" set, we apply the configuration
|
||||
// recursively. This allows you to create "rbind" mounts where only
|
||||
// the top-level mount has an idmapping. I'm not sure why you'd
|
||||
// want that, but still...
|
||||
if m.IDMapping.Recursive {
|
||||
setAttrFlags |= unix.AT_RECURSIVE
|
||||
}
|
||||
if err := unix.MountSetattr(int(mountFile.Fd()), "", setAttrFlags, &unix.MountAttr{
|
||||
Attr_set: unix.MOUNT_ATTR_IDMAP,
|
||||
Userns_fd: uint64(usernsFile.Fd()),
|
||||
}); err != nil {
|
||||
|
@@ -38,6 +38,7 @@ var (
|
||||
clear bool
|
||||
flag int
|
||||
}
|
||||
complexFlags map[string]func(*configs.Mount)
|
||||
)
|
||||
|
||||
func initMaps() {
|
||||
@@ -126,7 +127,6 @@ func initMaps() {
|
||||
"rnostrictatime": {true, unix.MOUNT_ATTR_STRICTATIME},
|
||||
"rnosymfollow": {false, unix.MOUNT_ATTR_NOSYMFOLLOW}, // since kernel 5.14
|
||||
"rsymfollow": {true, unix.MOUNT_ATTR_NOSYMFOLLOW}, // since kernel 5.14
|
||||
// No support for MOUNT_ATTR_IDMAP yet (needs UserNS FD)
|
||||
}
|
||||
|
||||
extensionFlags = map[string]struct {
|
||||
@@ -135,6 +135,17 @@ func initMaps() {
|
||||
}{
|
||||
"tmpcopyup": {false, configs.EXT_COPYUP},
|
||||
}
|
||||
|
||||
complexFlags = map[string]func(*configs.Mount){
|
||||
"idmap": func(m *configs.Mount) {
|
||||
m.IDMapping = new(configs.MountIDMapping)
|
||||
m.IDMapping.Recursive = false // noop
|
||||
},
|
||||
"ridmap": func(m *configs.Mount) {
|
||||
m.IDMapping = new(configs.MountIDMapping)
|
||||
m.IDMapping.Recursive = true
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -415,6 +426,19 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
|
||||
if err := setupUserNamespace(spec, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// For idmap and ridmap mounts without explicit mappings, use the
|
||||
// ones from the container's userns. If we are joining another
|
||||
// userns, stash the path.
|
||||
for _, m := range config.Mounts {
|
||||
if m.IDMapping != nil && m.IDMapping.UIDMappings == nil && m.IDMapping.GIDMappings == nil {
|
||||
if path := config.Namespaces.PathOf(configs.NEWUSER); path != "" {
|
||||
m.IDMapping.UserNSPath = path
|
||||
} else {
|
||||
m.IDMapping.UIDMappings = config.UIDMappings
|
||||
m.IDMapping.GIDMappings = config.GIDMappings
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
config.MaskPaths = spec.Linux.MaskedPaths
|
||||
config.ReadonlyPaths = spec.Linux.ReadonlyPaths
|
||||
@@ -447,6 +471,7 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
|
||||
Domain: domain,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Set the host UID that should own the container's cgroup.
|
||||
@@ -552,8 +577,14 @@ func createLibcontainerMount(cwd string, m specs.Mount) (*configs.Mount, error)
|
||||
}
|
||||
}
|
||||
|
||||
mnt.UIDMappings = toConfigIDMap(m.UIDMappings)
|
||||
mnt.GIDMappings = toConfigIDMap(m.GIDMappings)
|
||||
if m.UIDMappings != nil || m.GIDMappings != nil {
|
||||
if mnt.IDMapping == nil {
|
||||
// Neither "idmap" nor "ridmap" were specified.
|
||||
mnt.IDMapping = new(configs.MountIDMapping)
|
||||
}
|
||||
mnt.IDMapping.UIDMappings = toConfigIDMap(m.UIDMappings)
|
||||
mnt.IDMapping.GIDMappings = toConfigIDMap(m.GIDMappings)
|
||||
}
|
||||
|
||||
// None of the mount arguments can contain a null byte. Normally such
|
||||
// strings would either cause some other failure or would just be truncated
|
||||
@@ -1046,12 +1077,14 @@ func parseMountOptions(options []string) *configs.Mount {
|
||||
recAttrClr |= unix.MOUNT_ATTR__ATIME
|
||||
}
|
||||
}
|
||||
} else if f, exists := extensionFlags[o]; exists && f.flag != 0 {
|
||||
} else if f, exists := extensionFlags[o]; exists {
|
||||
if f.clear {
|
||||
m.Extensions &= ^f.flag
|
||||
} else {
|
||||
m.Extensions |= f.flag
|
||||
}
|
||||
} else if fn, exists := complexFlags[o]; exists {
|
||||
fn(&m)
|
||||
} else {
|
||||
data = append(data, o)
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ load helpers
|
||||
function setup() {
|
||||
OVERFLOW_UID="$(cat /proc/sys/kernel/overflowuid)"
|
||||
OVERFLOW_GID="$(cat /proc/sys/kernel/overflowgid)"
|
||||
|
||||
requires root
|
||||
requires_kernel 5.12
|
||||
|
||||
@@ -37,12 +36,34 @@ function setup() {
|
||||
|
||||
# Add a symlink-containing source.
|
||||
ln -s source-multi1 source-multi1-symlink
|
||||
|
||||
# Add some top-level files in the mount tree.
|
||||
mkdir -p mnt-subtree/multi{1,2}
|
||||
touch mnt-subtree/{foo,bar,baz}.txt
|
||||
chown 100:211 mnt-subtree/foo.txt
|
||||
chown 200:222 mnt-subtree/bar.txt
|
||||
chown 300:233 mnt-subtree/baz.txt
|
||||
|
||||
mounts_file="$PWD/.all-mounts"
|
||||
echo -n >"$mounts_file"
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
if [ -v mounts_file ]; then
|
||||
xargs -n 1 -a "$mounts_file" -- umount -l
|
||||
rm -f "$mounts_file"
|
||||
fi
|
||||
teardown_bundle
|
||||
}
|
||||
|
||||
function setup_host_bind_mount() {
|
||||
src="$1"
|
||||
dst="$2"
|
||||
|
||||
mount --bind "$src" "$dst"
|
||||
echo "$dst" >>"$mounts_file"
|
||||
}
|
||||
|
||||
function setup_idmap_userns() {
|
||||
update_config '.linux.namespaces += [{"type": "user"}]
|
||||
| .linux.uidMappings += [{"containerID": 0, "hostID": 100000, "size": 65536}]
|
||||
@@ -341,3 +362,331 @@ function setup_idmap_basic_mount() {
|
||||
[[ "$output" == *"=/tmp/mount-multi1/bar.txt:2000=2202="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-multi1/baz.txt:3000=3303="* ]]
|
||||
}
|
||||
|
||||
@test "idmap mount (non-recursive idmap) [userns]" {
|
||||
setup_idmap_userns
|
||||
|
||||
setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
|
||||
setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"
|
||||
|
||||
update_config '.mounts += [
|
||||
{
|
||||
"source": "mnt-subtree/",
|
||||
"destination": "/tmp/mount-tree",
|
||||
"options": ["rbind"],
|
||||
"uidMappings": [
|
||||
{"containerID": 100, "hostID": 101000, "size": 3},
|
||||
{"containerID": 200, "hostID": 102000, "size": 3},
|
||||
{"containerID": 300, "hostID": 103000, "size": 3}
|
||||
],
|
||||
"gidMappings": [
|
||||
{"containerID": 210, "hostID": 101100, "size": 10},
|
||||
{"containerID": 220, "hostID": 102200, "size": 10},
|
||||
{"containerID": 230, "hostID": 103300, "size": 10}
|
||||
]
|
||||
}
|
||||
]'
|
||||
|
||||
update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
|
||||
runc run test_debian
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"=/tmp/mount-tree/foo.txt:1000=1101="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/bar.txt:2000=2202="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/baz.txt:3000=3303="* ]]
|
||||
# Because we used "idmap", the child mounts were not remapped recursively.
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
}
|
||||
|
||||
@test "idmap mount (non-recursive idmap) [no userns]" {
|
||||
setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
|
||||
setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"
|
||||
|
||||
update_config '.mounts += [
|
||||
{
|
||||
"source": "mnt-subtree/",
|
||||
"destination": "/tmp/mount-tree",
|
||||
"options": ["rbind"],
|
||||
"uidMappings": [
|
||||
{"containerID": 100, "hostID": 101000, "size": 3},
|
||||
{"containerID": 200, "hostID": 102000, "size": 3},
|
||||
{"containerID": 300, "hostID": 103000, "size": 3}
|
||||
],
|
||||
"gidMappings": [
|
||||
{"containerID": 210, "hostID": 101100, "size": 10},
|
||||
{"containerID": 220, "hostID": 102200, "size": 10},
|
||||
{"containerID": 230, "hostID": 103300, "size": 10}
|
||||
]
|
||||
}
|
||||
]'
|
||||
|
||||
update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
|
||||
runc run test_debian
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"=/tmp/mount-tree/foo.txt:101000=101101="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/bar.txt:102000=102202="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/baz.txt:103000=103303="* ]]
|
||||
# Because we used "idmap", the child mounts were not remapped recursively.
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:100=211="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:101=222="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:102=233="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:200=211="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:201=222="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:202=233="* ]]
|
||||
}
|
||||
|
||||
@test "idmap mount (idmap flag) [userns]" {
|
||||
setup_idmap_userns
|
||||
|
||||
setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
|
||||
setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"
|
||||
|
||||
update_config '.mounts += [
|
||||
{
|
||||
"source": "mnt-subtree/",
|
||||
"destination": "/tmp/mount-tree",
|
||||
"options": ["rbind", "idmap"],
|
||||
"uidMappings": [
|
||||
{"containerID": 100, "hostID": 101000, "size": 3},
|
||||
{"containerID": 200, "hostID": 102000, "size": 3},
|
||||
{"containerID": 300, "hostID": 103000, "size": 3}
|
||||
],
|
||||
"gidMappings": [
|
||||
{"containerID": 210, "hostID": 101100, "size": 10},
|
||||
{"containerID": 220, "hostID": 102200, "size": 10},
|
||||
{"containerID": 230, "hostID": 103300, "size": 10}
|
||||
]
|
||||
}
|
||||
]'
|
||||
|
||||
update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
|
||||
runc run test_debian
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"=/tmp/mount-tree/foo.txt:1000=1101="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/bar.txt:2000=2202="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/baz.txt:3000=3303="* ]]
|
||||
# Because we used "idmap", the child mounts were not remapped recursively.
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
}
|
||||
|
||||
@test "idmap mount (idmap flag) [no userns]" {
|
||||
setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
|
||||
setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"
|
||||
|
||||
update_config '.mounts += [
|
||||
{
|
||||
"source": "mnt-subtree/",
|
||||
"destination": "/tmp/mount-tree",
|
||||
"options": ["rbind", "idmap"],
|
||||
"uidMappings": [
|
||||
{"containerID": 100, "hostID": 101000, "size": 3},
|
||||
{"containerID": 200, "hostID": 102000, "size": 3},
|
||||
{"containerID": 300, "hostID": 103000, "size": 3}
|
||||
],
|
||||
"gidMappings": [
|
||||
{"containerID": 210, "hostID": 101100, "size": 10},
|
||||
{"containerID": 220, "hostID": 102200, "size": 10},
|
||||
{"containerID": 230, "hostID": 103300, "size": 10}
|
||||
]
|
||||
}
|
||||
]'
|
||||
|
||||
update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
|
||||
runc run test_debian
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"=/tmp/mount-tree/foo.txt:101000=101101="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/bar.txt:102000=102202="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/baz.txt:103000=103303="* ]]
|
||||
# Because we used "idmap", the child mounts were not remapped recursively.
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:100=211="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:101=222="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:102=233="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:200=211="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:201=222="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:202=233="* ]]
|
||||
}
|
||||
|
||||
@test "idmap mount (ridmap flag) [userns]" {
|
||||
setup_idmap_userns
|
||||
|
||||
setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
|
||||
setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"
|
||||
|
||||
update_config '.mounts += [
|
||||
{
|
||||
"source": "mnt-subtree/",
|
||||
"destination": "/tmp/mount-tree",
|
||||
"options": ["rbind", "ridmap"],
|
||||
"uidMappings": [
|
||||
{"containerID": 100, "hostID": 101000, "size": 3},
|
||||
{"containerID": 200, "hostID": 102000, "size": 3},
|
||||
{"containerID": 300, "hostID": 103000, "size": 3}
|
||||
],
|
||||
"gidMappings": [
|
||||
{"containerID": 210, "hostID": 101100, "size": 10},
|
||||
{"containerID": 220, "hostID": 102200, "size": 10},
|
||||
{"containerID": 230, "hostID": 103300, "size": 10}
|
||||
]
|
||||
}
|
||||
]'
|
||||
|
||||
update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
|
||||
runc run test_debian
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"=/tmp/mount-tree/foo.txt:1000=1101="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/bar.txt:2000=2202="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/baz.txt:3000=3303="* ]]
|
||||
# The child mounts have the same mapping applied.
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:1000=1101="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:1001=2202="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:1002=3303="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:2000=1101="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:2001=2202="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:2002=3303="* ]]
|
||||
}
|
||||
|
||||
@test "idmap mount (ridmap flag) [no userns]" {
|
||||
setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
|
||||
setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"
|
||||
|
||||
update_config '.mounts += [
|
||||
{
|
||||
"source": "mnt-subtree/",
|
||||
"destination": "/tmp/mount-tree",
|
||||
"options": ["rbind", "ridmap"],
|
||||
"uidMappings": [
|
||||
{"containerID": 100, "hostID": 101000, "size": 3},
|
||||
{"containerID": 200, "hostID": 102000, "size": 3},
|
||||
{"containerID": 300, "hostID": 103000, "size": 3}
|
||||
],
|
||||
"gidMappings": [
|
||||
{"containerID": 210, "hostID": 101100, "size": 10},
|
||||
{"containerID": 220, "hostID": 102200, "size": 10},
|
||||
{"containerID": 230, "hostID": 103300, "size": 10}
|
||||
]
|
||||
}
|
||||
]'
|
||||
|
||||
update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
|
||||
runc run test_debian
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"=/tmp/mount-tree/foo.txt:101000=101101="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/bar.txt:102000=102202="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/baz.txt:103000=103303="* ]]
|
||||
# The child mounts have the same mapping applied.
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:101000=101101="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:101001=102202="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:101002=103303="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:102000=101101="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:102001=102202="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:102002=103303="* ]]
|
||||
}
|
||||
|
||||
@test "idmap mount (idmap flag, implied mapping) [userns]" {
|
||||
setup_idmap_userns
|
||||
|
||||
setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
|
||||
setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"
|
||||
|
||||
update_config '.mounts += [
|
||||
{
|
||||
"source": "mnt-subtree/",
|
||||
"destination": "/tmp/mount-tree",
|
||||
"options": ["rbind", "idmap"],
|
||||
}
|
||||
]'
|
||||
|
||||
update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
|
||||
runc run test_debian
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"=/tmp/mount-tree/foo.txt:100=211="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/bar.txt:200=222="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/baz.txt:300=233="* ]]
|
||||
# Because we used "idmap", the child mounts were not remapped recursively.
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
}
|
||||
|
||||
@test "idmap mount (ridmap flag, implied mapping) [userns]" {
|
||||
setup_idmap_userns
|
||||
|
||||
setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
|
||||
setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"
|
||||
|
||||
update_config '.mounts += [
|
||||
{
|
||||
"source": "mnt-subtree/",
|
||||
"destination": "/tmp/mount-tree",
|
||||
"options": ["rbind", "ridmap"],
|
||||
}
|
||||
]'
|
||||
|
||||
update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
|
||||
runc run test_debian
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"=/tmp/mount-tree/foo.txt:100=211="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/bar.txt:200=222="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/baz.txt:300=233="* ]]
|
||||
# The child mounts have the same mapping applied.
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:100=211="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:101=222="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:102=233="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:200=211="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:201=222="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:202=233="* ]]
|
||||
}
|
||||
|
||||
@test "idmap mount (idmap flag, implied mapping, userns join) [userns]" {
|
||||
# Create a detached container with the id-mapping we want.
|
||||
cp config.json config.json.bak
|
||||
update_config '.linux.namespaces += [{"type": "user"}]
|
||||
| .linux.uidMappings += [{"containerID": 0, "hostID": 100000, "size": 65536}]
|
||||
| .linux.gidMappings += [{"containerID": 0, "hostID": 100000, "size": 65536}]'
|
||||
update_config '.process.args = ["sleep", "infinity"]'
|
||||
|
||||
runc run -d --console-socket "$CONSOLE_SOCKET" target_userns
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# Configure our container to attach to the first container's userns.
|
||||
target_pid="$(__runc state target_userns | jq .pid)"
|
||||
update_config '.linux.namespaces |= map(if .type == "user" then (.path = "/proc/'"$target_pid"'/ns/" + .type) else . end)'
|
||||
update_config 'del(.linux.uidMappings) | del(.linux.gidMappings)'
|
||||
|
||||
setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
|
||||
setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"
|
||||
|
||||
update_config '.mounts += [
|
||||
{
|
||||
"source": "mnt-subtree/",
|
||||
"destination": "/tmp/mount-tree",
|
||||
"options": ["rbind", "idmap"],
|
||||
}
|
||||
]'
|
||||
|
||||
update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
|
||||
runc run test_debian
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"=/tmp/mount-tree/foo.txt:100=211="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/bar.txt:200=222="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/baz.txt:300=233="* ]]
|
||||
# Because we used "idmap", the child mounts were not remapped recursively.
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
|
||||
}
|
||||
|
Reference in New Issue
Block a user