podman: implement userns=keep-id

add missing implementation for userns=keep-id and enable the user
namespaces tests.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
Giuseppe Scrivano
2020-04-24 14:54:43 +02:00
parent 81c7a2444c
commit 64d8b4eebb
5 changed files with 87 additions and 64 deletions

View File

@ -246,20 +246,6 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.NetNS = c.Net.Network
}
// TODO this is going to have to be done the libpod/server end of things
// USER
//user := c.String("user")
//if user == "" {
// switch {
// case usernsMode.IsKeepID():
// user = fmt.Sprintf("%d:%d", rootless.GetRootlessUID(), rootless.GetRootlessGID())
// case data == nil:
// user = "0"
// default:
// user = data.Config.User
// }
//}
// STOP SIGNAL
signalString := "TERM"
if sig := c.StopSignal; len(sig) > 0 {

View File

@ -10,6 +10,7 @@ import (
"github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/specgen"
"github.com/containers/libpod/pkg/util"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
@ -175,6 +176,13 @@ func GenerateNamespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod
// User
switch s.UserNS.NSMode {
case specgen.KeepID:
if rootless.IsRootless() {
s.User = ""
} else {
// keep-id as root doesn't need a user namespace
s.UserNS.NSMode = specgen.Host
}
case specgen.FromPod:
if pod == nil || infraCtr == nil {
return nil, errNoInfra
@ -378,6 +386,18 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt
if err := g.RemoveLinuxNamespace(string(spec.UserNamespace)); err != nil {
return err
}
case specgen.KeepID:
var (
err error
uid, gid int
)
s.IDMappings, uid, gid, err = util.GetKeepIDMapping()
if err != nil {
return err
}
g.SetProcessUID(uint32(uid))
g.SetProcessGID(uint32(gid))
fallthrough
case specgen.Private:
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
return err

View File

@ -76,6 +76,17 @@ func (n *Namespace) IsPod() bool {
func (n *Namespace) IsPrivate() bool {
return n.NSMode == Private
}
// IsAuto indicates the namespace is auto
func (n *Namespace) IsAuto() bool {
return n.NSMode == Auto
}
// IsKeepID indicates the namespace is KeepID
func (n *Namespace) IsKeepID() bool {
return n.NSMode == KeepID
}
func validateUserNS(n *Namespace) error {
if n == nil {
return nil
@ -186,12 +197,11 @@ func ParseUserNamespace(ns string) (Namespace, error) {
if len(split) != 2 {
return toReturn, errors.Errorf("invalid setting for auto: mode")
}
toReturn.NSMode = KeepID
toReturn.NSMode = Auto
toReturn.Value = split[1]
return toReturn, nil
case ns == "keep-id":
toReturn.NSMode = KeepID
toReturn.NSMode = FromContainer
return toReturn, nil
}
return ParseNamespace(ns)

View File

@ -330,6 +330,58 @@ func ParseSignal(rawSignal string) (syscall.Signal, error) {
return sig, nil
}
// GetKeepIDMapping returns the mappings and the user to use when keep-id is used
func GetKeepIDMapping() (*storage.IDMappingOptions, int, int, error) {
options := storage.IDMappingOptions{
HostUIDMapping: true,
HostGIDMapping: true,
}
uid, gid := 0, 0
if rootless.IsRootless() {
min := func(a, b int) int {
if a < b {
return a
}
return b
}
uid = rootless.GetRootlessUID()
gid = rootless.GetRootlessGID()
uids, gids, err := rootless.GetConfiguredMappings()
if err != nil {
return nil, -1, -1, errors.Wrapf(err, "cannot read mappings")
}
maxUID, maxGID := 0, 0
for _, u := range uids {
maxUID += u.Size
}
for _, g := range gids {
maxGID += g.Size
}
options.UIDMap, options.GIDMap = nil, nil
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)})
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1})
if maxUID > uid {
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid + 1, HostID: uid + 1, Size: maxUID - uid})
}
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)})
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1})
if maxGID > gid {
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid + 1, HostID: gid + 1, Size: maxGID - gid})
}
options.HostUIDMapping = false
options.HostGIDMapping = false
}
// Simply ignore the setting and do not setup an inner namespace for root as it is a no-op
return &options, uid, gid, nil
}
// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []string, subUIDMap, subGIDMap string) (*storage.IDMappingOptions, error) {
options := storage.IDMappingOptions{
@ -350,53 +402,8 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin
return &options, nil
}
if mode.IsKeepID() {
if len(uidMapSlice) > 0 || len(gidMapSlice) > 0 {
return nil, errors.New("cannot specify custom mappings with --userns=keep-id")
}
if len(subUIDMap) > 0 || len(subGIDMap) > 0 {
return nil, errors.New("cannot specify subuidmap or subgidmap with --userns=keep-id")
}
if rootless.IsRootless() {
min := func(a, b int) int {
if a < b {
return a
}
return b
}
uid := rootless.GetRootlessUID()
gid := rootless.GetRootlessGID()
uids, gids, err := rootless.GetConfiguredMappings()
if err != nil {
return nil, errors.Wrapf(err, "cannot read mappings")
}
maxUID, maxGID := 0, 0
for _, u := range uids {
maxUID += u.Size
}
for _, g := range gids {
maxGID += g.Size
}
options.UIDMap, options.GIDMap = nil, nil
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)})
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1})
if maxUID > uid {
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid + 1, HostID: uid + 1, Size: maxUID - uid})
}
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)})
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1})
if maxGID > gid {
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid + 1, HostID: gid + 1, Size: maxGID - gid})
}
options.HostUIDMapping = false
options.HostGIDMapping = false
}
// Simply ignore the setting and do not setup an inner namespace for root as it is a no-op
options.HostUIDMapping = false
options.HostGIDMapping = false
return &options, nil
}

View File

@ -22,7 +22,6 @@ var _ = Describe("Podman UserNS support", func() {
)
BeforeEach(func() {
Skip(v2fail)
if os.Getenv("SKIP_USERNS") != "" {
Skip("Skip userns tests.")
}
@ -219,6 +218,7 @@ var _ = Describe("Podman UserNS support", func() {
})
It("podman --userns=container:CTR", func() {
Skip(v2fail)
ctrName := "userns-ctr"
session := podmanTest.Podman([]string{"run", "-d", "--uidmap=0:0:1", "--uidmap=1:1:4998", "--name", ctrName, "alpine", "top"})
session.WaitWithDefaultTimeout()