Skip to content

Commit

Permalink
Merge pull request #10079 from giuseppe/create-userns-for-root-withou…
Browse files Browse the repository at this point in the history
…t-CAP_SYS_ADMIN

runtime: create userns when CAP_SYS_ADMIN is not present
  • Loading branch information
openshift-merge-robot authored Apr 26, 2021
2 parents 9ca53cf + 722ea2f commit 333817a
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 12 deletions.
12 changes: 10 additions & 2 deletions libpod/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage"
"github.com/containers/storage/pkg/unshare"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/docker/pkg/namesgenerator"
spec "github.com/opencontainers/runtime-spec/specs-go"
Expand Down Expand Up @@ -338,9 +339,16 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
}
logrus.Debugf("Set libpod namespace to %q", runtime.config.Engine.Namespace)

hasCapSysAdmin, err := unshare.HasCapSysAdmin()
if err != nil {
return err
}

needsUserns := !hasCapSysAdmin

// Set up containers/storage
var store storage.Store
if os.Geteuid() != 0 {
if needsUserns {
logrus.Debug("Not configuring container store")
} else if runtime.noStore {
logrus.Debug("No store required. Not opening container store.")
Expand Down Expand Up @@ -480,7 +488,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
// If we need to refresh, then it is safe to assume there are
// no containers running. Create immediately a namespace, as
// we will need to access the storage.
if os.Geteuid() != 0 {
if needsUserns {
aliveLock.Unlock() // Unlock to avoid deadlock as BecomeRootInUserNS will reexec.
pausePid, err := util.GetRootlessPauseProcessPidPathGivenDir(runtime.config.Engine.TmpDir)
if err != nil {
Expand Down
7 changes: 6 additions & 1 deletion pkg/domain/infra/abi/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/podman/v3/utils"
"github.com/containers/storage"
"github.com/containers/storage/pkg/unshare"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -58,7 +59,11 @@ func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {

func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) error {
// do it only after podman has already re-execed and running with uid==0.
if os.Geteuid() == 0 {
hasCapSysAdmin, err := unshare.HasCapSysAdmin()
if err != nil {
return err
}
if hasCapSysAdmin {
ownsCgroup, err := cgroups.UserOwnsCurrentSystemdCgroup()
if err != nil {
logrus.Infof("Failed to detect the owner for the current cgroup: %v", err)
Expand Down
65 changes: 56 additions & 9 deletions pkg/rootless/rootless_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package rootless

import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
Expand All @@ -18,6 +19,7 @@ import (

"github.com/containers/podman/v3/pkg/errorhandling"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/unshare"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
Expand Down Expand Up @@ -67,6 +69,15 @@ func IsRootless() bool {
}
}
isRootless = os.Geteuid() != 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != ""
if !isRootless {
hasCapSysAdmin, err := unshare.HasCapSysAdmin()
if err != nil {
logrus.Warnf("failed to read CAP_SYS_ADMIN presence for the current process")
}
if err == nil && !hasCapSysAdmin {
isRootless = true
}
}
})
return isRootless
}
Expand Down Expand Up @@ -142,8 +153,12 @@ func tryMappingTool(uid bool, pid int, hostID int, mappings []idtools.IDMap) err
// namespace of the specified PID without looking up its parent. Useful to join directly
// the conmon process.
func joinUserAndMountNS(pid uint, pausePid string) (bool, int, error) {
if os.Geteuid() == 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" {
return false, -1, nil
hasCapSysAdmin, err := unshare.HasCapSysAdmin()
if err != nil {
return false, 0, err
}
if hasCapSysAdmin || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" {
return false, 0, nil
}

cPausePid := C.CString(pausePid)
Expand Down Expand Up @@ -180,17 +195,40 @@ func GetConfiguredMappings() ([]idtools.IDMap, []idtools.IDMap, error) {
}
mappings, err := idtools.NewIDMappings(username, username)
if err != nil {
logrus.Errorf(
"cannot find UID/GID for user %s: %v - check rootless mode in man pages.", username, err)
logLevel := logrus.ErrorLevel
if os.Geteuid() == 0 && GetRootlessUID() == 0 {
logLevel = logrus.DebugLevel
}
logrus.StandardLogger().Logf(logLevel, "cannot find UID/GID for user %s: %v - check rootless mode in man pages.", username, err)
} else {
uids = mappings.UIDs()
gids = mappings.GIDs()
}
return uids, gids, nil
}

func copyMappings(from, to string) error {
content, err := ioutil.ReadFile(from)
if err != nil {
return err
}
// Both runc and crun check whether the current process is in a user namespace
// by looking up 4294967295 in /proc/self/uid_map. If the mappings would be
// copied as they are, the check in the OCI runtimes would fail. So just split
// it in two different ranges.
if bytes.Contains(content, []byte("4294967295")) {
content = []byte("0 0 1\n1 1 4294967294\n")
}
return ioutil.WriteFile(to, content, 0600)
}

func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ bool, _ int, retErr error) {
if os.Geteuid() == 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" {
hasCapSysAdmin, err := unshare.HasCapSysAdmin()
if err != nil {
return false, 0, err
}

if hasCapSysAdmin || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" {
if os.Getenv("_CONTAINERS_USERNS_CONFIGURED") == "init" {
return false, 0, runInUser()
}
Expand Down Expand Up @@ -247,8 +285,16 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo
return false, -1, err
}

uidMap := fmt.Sprintf("/proc/%d/uid_map", pid)
gidMap := fmt.Sprintf("/proc/%d/gid_map", pid)

uidsMapped := false
if uids != nil {

if err := copyMappings("/proc/self/uid_map", uidMap); err == nil {
uidsMapped = true
}

if uids != nil && !uidsMapped {
err := tryMappingTool(true, pid, os.Geteuid(), uids)
// If some mappings were specified, do not ignore the error
if err != nil && len(uids) > 0 {
Expand All @@ -265,7 +311,6 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo
}
logrus.Debugf("write setgroups file exited with 0")

uidMap := fmt.Sprintf("/proc/%d/uid_map", pid)
err = ioutil.WriteFile(uidMap, []byte(fmt.Sprintf("%d %d 1\n", 0, os.Geteuid())), 0666)
if err != nil {
return false, -1, errors.Wrapf(err, "cannot write uid_map")
Expand All @@ -274,7 +319,10 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo
}

gidsMapped := false
if gids != nil {
if err := copyMappings("/proc/self/gid_map", gidMap); err == nil {
gidsMapped = true
}
if gids != nil && !gidsMapped {
err := tryMappingTool(false, pid, os.Getegid(), gids)
// If some mappings were specified, do not ignore the error
if err != nil && len(gids) > 0 {
Expand All @@ -283,7 +331,6 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo
gidsMapped = err == nil
}
if !gidsMapped {
gidMap := fmt.Sprintf("/proc/%d/gid_map", pid)
err = ioutil.WriteFile(gidMap, []byte(fmt.Sprintf("%d %d 1\n", 0, os.Getegid())), 0666)
if err != nil {
return false, -1, errors.Wrapf(err, "cannot write gid_map")
Expand Down

0 comments on commit 333817a

Please sign in to comment.