mirror of https://github.com/containers/podman.git
libpod: use OCI idmappings for mounts
Now that the OCI runtime specs have support for idmapped mounts, let's use them instead of relying on the custom annotation in crun. Also add the mechanism to specify the mapping to use. Pick the same format used by crun so it won't be a breaking change for users that are already using it. Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
parent
2a48296b0b
commit
fdcc2257df
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue