mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +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.
|
. 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.
|
· 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:
|
Options specific to image:
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ import (
|
|||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
"github.com/containers/storage/pkg/idtools"
|
"github.com/containers/storage/pkg/idtools"
|
||||||
"github.com/containers/storage/pkg/lockfile"
|
"github.com/containers/storage/pkg/lockfile"
|
||||||
|
stypes "github.com/containers/storage/types"
|
||||||
securejoin "github.com/cyphar/filepath-securejoin"
|
securejoin "github.com/cyphar/filepath-securejoin"
|
||||||
runcuser "github.com/opencontainers/runc/libcontainer/user"
|
runcuser "github.com/opencontainers/runc/libcontainer/user"
|
||||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
@ -56,6 +57,66 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"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
|
// Internal only function which returns upper and work dir from
|
||||||
// overlay options.
|
// overlay options.
|
||||||
func getOverlayUpperAndWorkDir(options []string) (string, string, error) {
|
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 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 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 {
|
for i := range g.Config.Mounts {
|
||||||
m := &g.Config.Mounts[i]
|
m := &g.Config.Mounts[i]
|
||||||
var options []string
|
var options []string
|
||||||
for _, o := range m.Options {
|
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 {
|
switch o {
|
||||||
case "U":
|
case "U":
|
||||||
if m.Type == "tmpfs" {
|
if m.Type == "tmpfs" {
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containers/storage/pkg/idtools"
|
||||||
|
stypes "github.com/containers/storage/types"
|
||||||
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -15,6 +17,100 @@ import (
|
|||||||
// hookPath is the path to an example hook executable.
|
// hookPath is the path to an example hook executable.
|
||||||
var hookPath string
|
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) {
|
func TestPostDeleteHooks(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
|
Reference in New Issue
Block a user