mirror of
https://github.com/containers/podman.git
synced 2025-08-06 11:32:07 +08:00
Merge pull request #16837 from giuseppe/idmap-oci
libpod: use OCI idmappings for mounts
This commit is contained in:
@ -37,6 +37,11 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
|
||||
. U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
|
||||
|
||||
· idmap: true or false (default). If specified, create an idmapped mount to the target user namespace in the container.
|
||||
The idmap option supports a custom mapping that can be different than the user namespace used by the container.
|
||||
The mapping can be specified after the idmap option like: idmap=uids=0-1-10#10-11-10;gids=0-100-10. For each triplet, the first value is the
|
||||
start of the backing file system IDs that are mapped to the second value on the host. The length of this mapping is given in the third value.
|
||||
|
||||
Multiple ranges are separated with #.
|
||||
|
||||
Options specific to image:
|
||||
|
||||
|
@ -47,6 +47,7 @@ import (
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
"github.com/containers/storage/pkg/idtools"
|
||||
"github.com/containers/storage/pkg/lockfile"
|
||||
stypes "github.com/containers/storage/types"
|
||||
securejoin "github.com/cyphar/filepath-securejoin"
|
||||
runcuser "github.com/opencontainers/runc/libcontainer/user"
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
@ -56,6 +57,66 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func parseOptionIDs(option string) ([]idtools.IDMap, error) {
|
||||
ranges := strings.Split(option, "#")
|
||||
ret := make([]idtools.IDMap, len(ranges))
|
||||
for i, m := range ranges {
|
||||
var v idtools.IDMap
|
||||
_, err := fmt.Sscanf(m, "%d-%d-%d", &v.ContainerID, &v.HostID, &v.Size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v.ContainerID < 0 || v.HostID < 0 || v.Size < 1 {
|
||||
return nil, fmt.Errorf("invalid value for %q", option)
|
||||
}
|
||||
ret[i] = v
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func parseIDMapMountOption(idMappings stypes.IDMappingOptions, option string) ([]spec.LinuxIDMapping, []spec.LinuxIDMapping, error) {
|
||||
uidMap := idMappings.UIDMap
|
||||
gidMap := idMappings.GIDMap
|
||||
if strings.HasPrefix(option, "idmap=") {
|
||||
var err error
|
||||
options := strings.Split(strings.SplitN(option, "=", 2)[1], ";")
|
||||
for _, i := range options {
|
||||
switch {
|
||||
case strings.HasPrefix(i, "uids="):
|
||||
uidMap, err = parseOptionIDs(strings.Replace(i, "uids=", "", 1))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
case strings.HasPrefix(i, "gids="):
|
||||
gidMap, err = parseOptionIDs(strings.Replace(i, "gids=", "", 1))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("unknown option %q", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uidMappings := make([]spec.LinuxIDMapping, len(uidMap))
|
||||
gidMappings := make([]spec.LinuxIDMapping, len(gidMap))
|
||||
for i, uidmap := range uidMap {
|
||||
uidMappings[i] = spec.LinuxIDMapping{
|
||||
HostID: uint32(uidmap.ContainerID),
|
||||
ContainerID: uint32(uidmap.HostID),
|
||||
Size: uint32(uidmap.Size),
|
||||
}
|
||||
}
|
||||
for i, gidmap := range gidMap {
|
||||
gidMappings[i] = spec.LinuxIDMapping{
|
||||
HostID: uint32(gidmap.ContainerID),
|
||||
ContainerID: uint32(gidmap.HostID),
|
||||
Size: uint32(gidmap.Size),
|
||||
}
|
||||
}
|
||||
return uidMappings, gidMappings, nil
|
||||
}
|
||||
|
||||
// Internal only function which returns upper and work dir from
|
||||
// overlay options.
|
||||
func getOverlayUpperAndWorkDir(options []string) (string, string, error) {
|
||||
@ -217,13 +278,22 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the spec file mounts contain the options z, Z or U.
|
||||
// Check if the spec file mounts contain the options z, Z, U or idmap.
|
||||
// If they have z or Z, relabel the source directory and then remove the option.
|
||||
// If they have U, chown the source directory and them remove the option.
|
||||
// If they have idmap, then calculate the mappings to use in the OCI config file.
|
||||
for i := range g.Config.Mounts {
|
||||
m := &g.Config.Mounts[i]
|
||||
var options []string
|
||||
for _, o := range m.Options {
|
||||
if o == "idmap" || strings.HasPrefix(o, "idmap=") {
|
||||
var err error
|
||||
m.UIDMappings, m.GIDMappings, err = parseIDMapMountOption(c.config.IDMappings, o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
switch o {
|
||||
case "U":
|
||||
if m.Type == "tmpfs" {
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/storage/pkg/idtools"
|
||||
stypes "github.com/containers/storage/types"
|
||||
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@ -15,6 +17,100 @@ import (
|
||||
// hookPath is the path to an example hook executable.
|
||||
var hookPath string
|
||||
|
||||
func TestParseOptionIDs(t *testing.T) {
|
||||
_, err := parseOptionIDs("uids=100-200-2")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
mappings, err := parseOptionIDs("100-200-2")
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, mappings)
|
||||
|
||||
assert.Equal(t, len(mappings), 1)
|
||||
|
||||
assert.Equal(t, mappings[0].ContainerID, 100)
|
||||
assert.Equal(t, mappings[0].HostID, 200)
|
||||
assert.Equal(t, mappings[0].Size, 2)
|
||||
|
||||
mappings, err = parseOptionIDs("100-200-2#300-400-5")
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, mappings)
|
||||
|
||||
assert.Equal(t, len(mappings), 2)
|
||||
|
||||
assert.Equal(t, mappings[0].ContainerID, 100)
|
||||
assert.Equal(t, mappings[0].HostID, 200)
|
||||
assert.Equal(t, mappings[0].Size, 2)
|
||||
|
||||
assert.Equal(t, mappings[1].ContainerID, 300)
|
||||
assert.Equal(t, mappings[1].HostID, 400)
|
||||
assert.Equal(t, mappings[1].Size, 5)
|
||||
}
|
||||
|
||||
func TestParseIDMapMountOption(t *testing.T) {
|
||||
uidMap := []idtools.IDMap{
|
||||
{
|
||||
ContainerID: 0,
|
||||
HostID: 1000,
|
||||
Size: 10000,
|
||||
},
|
||||
}
|
||||
gidMap := []idtools.IDMap{
|
||||
{
|
||||
ContainerID: 0,
|
||||
HostID: 2000,
|
||||
Size: 10000,
|
||||
},
|
||||
}
|
||||
options := stypes.IDMappingOptions{
|
||||
UIDMap: uidMap,
|
||||
GIDMap: gidMap,
|
||||
}
|
||||
uids, gids, err := parseIDMapMountOption(options, "idmap")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(uids), 1)
|
||||
assert.Equal(t, len(gids), 1)
|
||||
|
||||
assert.Equal(t, uids[0].ContainerID, uint32(1000))
|
||||
assert.Equal(t, uids[0].HostID, uint32(0))
|
||||
assert.Equal(t, uids[0].Size, uint32(10000))
|
||||
|
||||
assert.Equal(t, gids[0].ContainerID, uint32(2000))
|
||||
assert.Equal(t, gids[0].HostID, uint32(0))
|
||||
assert.Equal(t, gids[0].Size, uint32(10000))
|
||||
|
||||
uids, gids, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(uids), 2)
|
||||
assert.Equal(t, len(gids), 1)
|
||||
|
||||
assert.Equal(t, uids[0].ContainerID, uint32(1))
|
||||
assert.Equal(t, uids[0].HostID, uint32(0))
|
||||
assert.Equal(t, uids[0].Size, uint32(10))
|
||||
|
||||
assert.Equal(t, uids[1].ContainerID, uint32(11))
|
||||
assert.Equal(t, uids[1].HostID, uint32(10))
|
||||
assert.Equal(t, uids[1].Size, uint32(10))
|
||||
|
||||
assert.Equal(t, gids[0].ContainerID, uint32(3))
|
||||
assert.Equal(t, gids[0].HostID, uint32(0))
|
||||
assert.Equal(t, gids[0].Size, uint32(10))
|
||||
|
||||
_, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10;foobar=bar")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
_, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#0-12")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
_, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#0-12--12")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
_, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#-1-12-12")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
_, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#0--12-0")
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestPostDeleteHooks(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
dir := t.TempDir()
|
||||
|
Reference in New Issue
Block a user