mirror of
https://github.com/containers/podman.git
synced 2025-06-22 01:48:54 +08:00
Merge pull request #8816 from giuseppe/automatically-split-userns-mappings
rootless: automatically split userns ranges
This commit is contained in:
@ -529,6 +529,13 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
availableUIDs, availableGIDs, err := rootless.GetAvailableIDMaps()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
g.Config.Linux.UIDMappings = rootless.MaybeSplitMappings(g.Config.Linux.UIDMappings, availableUIDs)
|
||||||
|
g.Config.Linux.GIDMappings = rootless.MaybeSplitMappings(g.Config.Linux.GIDMappings, availableGIDs)
|
||||||
|
|
||||||
// Hostname handling:
|
// Hostname handling:
|
||||||
// If we have a UTS namespace, set Hostname in the OCI spec.
|
// If we have a UTS namespace, set Hostname in the OCI spec.
|
||||||
// Set the HOSTNAME environment variable unless explicitly overridden by
|
// Set the HOSTNAME environment variable unless explicitly overridden by
|
||||||
@ -536,6 +543,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
|
|||||||
// set it to the host's hostname instead.
|
// set it to the host's hostname instead.
|
||||||
hostname := c.Hostname()
|
hostname := c.Hostname()
|
||||||
foundUTS := false
|
foundUTS := false
|
||||||
|
|
||||||
for _, i := range c.config.Spec.Linux.Namespaces {
|
for _, i := range c.config.Spec.Linux.Namespaces {
|
||||||
if i.Type == spec.UTSNamespace && i.Path == "" {
|
if i.Type == spec.UTSNamespace && i.Path == "" {
|
||||||
foundUTS = true
|
foundUTS = true
|
||||||
|
@ -2,10 +2,12 @@ package rootless
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/opencontainers/runc/libcontainer/user"
|
"github.com/opencontainers/runc/libcontainer/user"
|
||||||
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -50,24 +52,151 @@ func TryJoinPauseProcess(pausePidPath string) (bool, int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
availableGids int64
|
uidMap []user.IDMap
|
||||||
availableGidsErr error
|
uidMapError error
|
||||||
availableGidsOnce sync.Once
|
uidMapOnce sync.Once
|
||||||
|
|
||||||
|
gidMap []user.IDMap
|
||||||
|
gidMapError error
|
||||||
|
gidMapOnce sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetAvailableUidMap returns the UID mappings in the
|
||||||
|
// current user namespace.
|
||||||
|
func GetAvailableUidMap() ([]user.IDMap, error) {
|
||||||
|
uidMapOnce.Do(func() {
|
||||||
|
var err error
|
||||||
|
uidMap, err = user.ParseIDMapFile("/proc/self/uid_map")
|
||||||
|
if err != nil {
|
||||||
|
uidMapError = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return uidMap, uidMapError
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAvailableGidMap returns the GID mappings in the
|
||||||
|
// current user namespace.
|
||||||
|
func GetAvailableGidMap() ([]user.IDMap, error) {
|
||||||
|
gidMapOnce.Do(func() {
|
||||||
|
var err error
|
||||||
|
gidMap, err = user.ParseIDMapFile("/proc/self/gid_map")
|
||||||
|
if err != nil {
|
||||||
|
gidMapError = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return gidMap, gidMapError
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAvailableIDMaps returns the UID and GID mappings in the
|
||||||
|
// current user namespace.
|
||||||
|
func GetAvailableIDMaps() ([]user.IDMap, []user.IDMap, error) {
|
||||||
|
u, err := GetAvailableUidMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
g, err := GetAvailableGidMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return u, g, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func countAvailableIDs(mappings []user.IDMap) int64 {
|
||||||
|
availableUids := int64(0)
|
||||||
|
for _, r := range mappings {
|
||||||
|
availableUids += r.Count
|
||||||
|
}
|
||||||
|
return availableUids
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAvailableUids returns how many UIDs are available in the
|
||||||
|
// current user namespace.
|
||||||
|
func GetAvailableUids() (int64, error) {
|
||||||
|
uids, err := GetAvailableUidMap()
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return countAvailableIDs(uids), nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetAvailableGids returns how many GIDs are available in the
|
// GetAvailableGids returns how many GIDs are available in the
|
||||||
// current user namespace.
|
// current user namespace.
|
||||||
func GetAvailableGids() (int64, error) {
|
func GetAvailableGids() (int64, error) {
|
||||||
availableGidsOnce.Do(func() {
|
gids, err := GetAvailableGidMap()
|
||||||
idMap, err := user.ParseIDMapFile("/proc/self/gid_map")
|
if err != nil {
|
||||||
if err != nil {
|
return -1, err
|
||||||
availableGidsErr = err
|
}
|
||||||
return
|
|
||||||
}
|
return countAvailableIDs(gids), nil
|
||||||
availableGids = int64(0)
|
}
|
||||||
for _, r := range idMap {
|
|
||||||
availableGids += r.Count
|
// findIDInMappings find the the mapping that contains the specified ID.
|
||||||
}
|
// It assumes availableMappings is sorted by ID.
|
||||||
})
|
func findIDInMappings(id int64, availableMappings []user.IDMap) *user.IDMap {
|
||||||
return availableGids, availableGidsErr
|
i := sort.Search(len(availableMappings), func(i int) bool {
|
||||||
|
return availableMappings[i].ID >= id
|
||||||
|
})
|
||||||
|
if i < 0 || i >= len(availableMappings) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
r := &availableMappings[i]
|
||||||
|
if id >= r.ID && id < r.ID+r.Count {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaybeSplitMappings checks whether the specified OCI mappings are possible
|
||||||
|
// in the current user namespace or the specified ranges must be split.
|
||||||
|
func MaybeSplitMappings(mappings []spec.LinuxIDMapping, availableMappings []user.IDMap) []spec.LinuxIDMapping {
|
||||||
|
var ret []spec.LinuxIDMapping
|
||||||
|
var overflow spec.LinuxIDMapping
|
||||||
|
overflow.Size = 0
|
||||||
|
consumed := 0
|
||||||
|
sort.Slice(availableMappings, func(i, j int) bool {
|
||||||
|
return availableMappings[i].ID < availableMappings[j].ID
|
||||||
|
})
|
||||||
|
for {
|
||||||
|
cur := overflow
|
||||||
|
// if there is no overflow left from the previous request, get the next one
|
||||||
|
if cur.Size == 0 {
|
||||||
|
if consumed == len(mappings) {
|
||||||
|
// all done
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
cur = mappings[consumed]
|
||||||
|
consumed++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the range where the first specified ID is present
|
||||||
|
r := findIDInMappings(int64(cur.HostID), availableMappings)
|
||||||
|
if r == nil {
|
||||||
|
// The requested range is not available. Just return the original request
|
||||||
|
// and let other layers deal with it.
|
||||||
|
return mappings
|
||||||
|
}
|
||||||
|
|
||||||
|
offsetInRange := cur.HostID - uint32(r.ID)
|
||||||
|
|
||||||
|
usableIDs := uint32(r.Count) - offsetInRange
|
||||||
|
|
||||||
|
// the current range can satisfy the whole request
|
||||||
|
if usableIDs >= cur.Size {
|
||||||
|
// reset the overflow
|
||||||
|
overflow.Size = 0
|
||||||
|
} else {
|
||||||
|
// the current range can satisfy the request partially
|
||||||
|
// so move the rest to overflow
|
||||||
|
overflow.Size = cur.Size - usableIDs
|
||||||
|
overflow.ContainerID = cur.ContainerID + usableIDs
|
||||||
|
overflow.HostID = cur.HostID + usableIDs
|
||||||
|
|
||||||
|
// and cap to the usableIDs count
|
||||||
|
cur.Size = usableIDs
|
||||||
|
}
|
||||||
|
ret = append(ret, cur)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
101
pkg/rootless/rootless_test.go
Normal file
101
pkg/rootless/rootless_test.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package rootless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/user"
|
||||||
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMaybeSplitMappings(t *testing.T) {
|
||||||
|
mappings := []spec.LinuxIDMapping{
|
||||||
|
{
|
||||||
|
ContainerID: 0,
|
||||||
|
HostID: 0,
|
||||||
|
Size: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
desiredMappings := []spec.LinuxIDMapping{
|
||||||
|
{
|
||||||
|
ContainerID: 0,
|
||||||
|
HostID: 0,
|
||||||
|
Size: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ContainerID: 1,
|
||||||
|
HostID: 1,
|
||||||
|
Size: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
availableMappings := []user.IDMap{
|
||||||
|
{
|
||||||
|
ID: 1,
|
||||||
|
ParentID: 1000000,
|
||||||
|
Count: 65536,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 0,
|
||||||
|
ParentID: 1000,
|
||||||
|
Count: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
newMappings := MaybeSplitMappings(mappings, availableMappings)
|
||||||
|
if !reflect.DeepEqual(newMappings, desiredMappings) {
|
||||||
|
t.Fatal("wrong mappings generated")
|
||||||
|
}
|
||||||
|
|
||||||
|
mappings = []spec.LinuxIDMapping{
|
||||||
|
{
|
||||||
|
ContainerID: 0,
|
||||||
|
HostID: 0,
|
||||||
|
Size: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
desiredMappings = []spec.LinuxIDMapping{
|
||||||
|
{
|
||||||
|
ContainerID: 0,
|
||||||
|
HostID: 0,
|
||||||
|
Size: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
availableMappings = []user.IDMap{
|
||||||
|
{
|
||||||
|
ID: 0,
|
||||||
|
ParentID: 1000000,
|
||||||
|
Count: 65536,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
newMappings = MaybeSplitMappings(mappings, availableMappings)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(newMappings, desiredMappings) {
|
||||||
|
t.Fatal("wrong mappings generated")
|
||||||
|
}
|
||||||
|
|
||||||
|
mappings = []spec.LinuxIDMapping{
|
||||||
|
{
|
||||||
|
ContainerID: 0,
|
||||||
|
HostID: 0,
|
||||||
|
Size: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
desiredMappings = []spec.LinuxIDMapping{
|
||||||
|
{
|
||||||
|
ContainerID: 0,
|
||||||
|
HostID: 0,
|
||||||
|
Size: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
availableMappings = []user.IDMap{
|
||||||
|
{
|
||||||
|
ID: 10000,
|
||||||
|
ParentID: 10000,
|
||||||
|
Count: 65536,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
newMappings = MaybeSplitMappings(mappings, availableMappings)
|
||||||
|
if !reflect.DeepEqual(newMappings, desiredMappings) {
|
||||||
|
t.Fatal("wrong mappings generated")
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user