diff options
-rw-r--r-- | .cirrus.yml | 4 | ||||
-rw-r--r-- | cmd/podman/main_local.go | 10 | ||||
-rw-r--r-- | contrib/cirrus/git_authors_to_irc_nicks.csv | 4 | ||||
-rwxr-xr-x | contrib/cirrus/success.sh | 7 | ||||
-rwxr-xr-x | contrib/imgprune/entrypoint.sh | 14 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.go | 139 | ||||
-rw-r--r-- | pkg/rootless/rootless_unsupported.go | 12 | ||||
-rw-r--r-- | pkg/util/utils.go | 16 |
8 files changed, 163 insertions, 43 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 59f1b5e01..d41828d5d 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -300,11 +300,9 @@ image_prune_task: memory: 1 env: - # order is significant, Cirrus not always overriding alias values as intended + <<: *meta_env_vars GCPJSON: ENCRYPTED[4c11d8e09c904c30fc70eecb95c73dec0ddf19976f9b981a0f80f3f6599e8f990bcef93c253ac0277f200850d98528e7] GCPNAME: ENCRYPTED[7f54557ba6e5a437f11283a53e71baec9ca546f48a9835538cc54d297f79968eb1337d4596a1025b14f9d1c5723fbd29] - GCPPROJECT: ENCRYPTED[7c80e728e046b1c76147afd156a32c1c57d4a1ac1eab93b7e68e718c61ca8564fc61fef815952b8ae0a64e7034b8fe4f] - <<: *meta_env_vars timeout_in: 10m diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go index 648dc166e..0feba609b 100644 --- a/cmd/podman/main_local.go +++ b/cmd/podman/main_local.go @@ -120,6 +120,14 @@ func profileOff(cmd *cobra.Command) error { } func setupRootless(cmd *cobra.Command, args []string) error { + matches, err := rootless.ConfigurationMatches() + if err != nil { + return err + } + if !matches { + logrus.Warningf("the current user namespace doesn't match the configuration in /etc/subuid or /etc/subgid") + logrus.Warningf("you can use `%s system migrate` to recreate the user namespace and restart the containers", os.Args[0]) + } if os.Geteuid() == 0 || cmd == _searchCommand || cmd == _versionCommand || cmd == _mountCommand || cmd == _migrateCommand || strings.HasPrefix(cmd.Use, "help") { return nil } @@ -140,7 +148,7 @@ func setupRootless(cmd *cobra.Command, args []string) error { became, ret, err := rootless.TryJoinFromFilePaths("", false, []string{pausePidPath}) if err != nil { logrus.Errorf("cannot join pause process. You may need to remove %s and stop all containers", pausePidPath) - logrus.Errorf("you can use `%s system migrate` to recreate the pause process", os.Args[0]) + logrus.Errorf("you can use `%s system migrate` to recreate the pause process and restart the containers", os.Args[0]) logrus.Errorf(err.Error()) os.Exit(1) } diff --git a/contrib/cirrus/git_authors_to_irc_nicks.csv b/contrib/cirrus/git_authors_to_irc_nicks.csv index 4334b5cd2..a584cc76a 100644 --- a/contrib/cirrus/git_authors_to_irc_nicks.csv +++ b/contrib/cirrus/git_authors_to_irc_nicks.csv @@ -3,6 +3,10 @@ # Sorting is done at runtime - first-found e-mail match wins. # Comments (like this) and blank lines are ignored. +bbaude@redhat.com,baude +matthew.heon@pm.me,mheon +matthew.heon@gmail.com,mheon +emilien@redhat.com,EmilienM rothberg@redhat.com,vrothberg santiago@redhat.com,edsantiago gscrivan@redhat.com,giuseppe diff --git a/contrib/cirrus/success.sh b/contrib/cirrus/success.sh index f2c9fbc7f..30d375d95 100755 --- a/contrib/cirrus/success.sh +++ b/contrib/cirrus/success.sh @@ -4,12 +4,14 @@ set -e source $(dirname $0)/lib.sh -req_env_var CIRRUS_BRANCH CIRRUS_BUILD_ID CIRRUS_REPO_FULL_NAME +req_env_var CIRRUS_BRANCH CIRRUS_BUILD_ID CIRRUS_REPO_FULL_NAME CIRRUS_BASE_SHA CIRRUS_CHANGE_IN_REPO cd $CIRRUS_WORKING_DIR if [[ "$CIRRUS_BRANCH" =~ "pull" ]] then + echo "Retrieving latest HEADS and tags" + git fetch --all --tags echo "Finding commit authors for PR $CIRRUS_PR" unset NICKS if [[ -r "$AUTHOR_NICKS_FILEPATH" ]] @@ -20,7 +22,8 @@ then # Depending on branch-state, it's possible SHARANGE could be _WAY_ too big MAX_NICKS=10 # newline separated - COMMIT_AUTHORS=$(git log --format='%ae' $SHARANGE | \ + GITLOG="git log --format='%ae'" + COMMIT_AUTHORS=$($GITLOGt $SHARANGE || $GITLOG -1 HEAD | \ sort -u | \ egrep -v "$EXCLUDE_RE" | \ tail -$MAX_NICKS) diff --git a/contrib/imgprune/entrypoint.sh b/contrib/imgprune/entrypoint.sh index 829e9938e..b0f006332 100755 --- a/contrib/imgprune/entrypoint.sh +++ b/contrib/imgprune/entrypoint.sh @@ -6,13 +6,17 @@ source /usr/local/bin/lib_entrypoint.sh req_env_var GCPJSON GCPNAME GCPPROJECT IMGNAMES -BASE_IMAGES="" -# When executing under Cirrus-CI, have access to current source -if [[ "$CI" == "true" ]] && [[ -r "$CIRRUS_WORKING_DIR/$SCRIPT_BASE" ]] +unset BASE_IMAGES +# When executing under Cirrus-CI, script have access to current source +LIB="$CIRRUS_WORKING_DIR/$SCRIPT_BASE/lib.sh" +if [[ "$CI" == "true" ]] && [[ -r "$LIB" ]] then # Avoid importing anything that might conflict - eval "$(egrep -sh '^export .+BASE_IMAGE=' < $CIRRUS_WORKING_DIR/$SCRIPT_BASE/lib.sh)" - BASE_IMAGES="$UBUNTU_BASE_IMAGE $PRIOR_UBUNTU_BASE_IMAGE $FEDORA_BASE_IMAGE $PRIOR_FEDORA_BASE_IMAGE" + for env in $(sed -ne 's/^[^#]\+_BASE_IMAGE=/img=/p' "$LIB") + do + eval $env + BASE_IMAGES="$BASE_IMAGES $img" + done else # metadata labeling may have broken for some reason in the future echo "Warning: Running outside of Cirrus-CI, very minor-risk of base-image deletion." diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 6e48988c5..ecb84f6a9 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -3,7 +3,9 @@ package rootless import ( + "bufio" "fmt" + "io" "io/ioutil" "os" "os/exec" @@ -106,7 +108,7 @@ func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap) } appendTriplet := func(l []string, a, b, c int) []string { - return append(l, fmt.Sprintf("%d", a), fmt.Sprintf("%d", b), fmt.Sprintf("%d", c)) + return append(l, strconv.Itoa(a), strconv.Itoa(b), strconv.Itoa(c)) } args := []string{path, fmt.Sprintf("%d", pid)} @@ -345,6 +347,32 @@ func joinUserAndMountNS(pid uint, pausePid string) (bool, int, error) { return true, int(ret), nil } +// GetConfiguredMappings returns the additional IDs configured for the current user. +func GetConfiguredMappings() ([]idtools.IDMap, []idtools.IDMap, error) { + var uids, gids []idtools.IDMap + username := os.Getenv("USER") + if username == "" { + var id string + if os.Geteuid() == 0 { + id = strconv.Itoa(GetRootlessUID()) + } else { + id = strconv.Itoa(os.Geteuid()) + } + userID, err := user.LookupId(id) + if err == nil { + username = userID.Username + } + } + mappings, err := idtools.NewIDMappings(username, username) + if err != nil { + logrus.Warnf("cannot find mappings for user %s: %v", username, err) + } else { + uids = mappings.UIDs() + gids = mappings.GIDs() + } + return uids, gids, nil +} + func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool, int, error) { if os.Geteuid() == 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" { if os.Getenv("_CONTAINERS_USERNS_CONFIGURED") == "init" { @@ -386,25 +414,14 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool, return false, -1, errors.Errorf("cannot re-exec process") } - var uids, gids []idtools.IDMap - username := os.Getenv("USER") - if username == "" { - userID, err := user.LookupId(fmt.Sprintf("%d", os.Getuid())) - if err == nil { - username = userID.Username - } - } - mappings, err := idtools.NewIDMappings(username, username) + uids, gids, err := GetConfiguredMappings() if err != nil { - logrus.Warnf("cannot find mappings for user %s: %v", username, err) - } else { - uids = mappings.UIDs() - gids = mappings.GIDs() + return false, -1, err } uidsMapped := false - if mappings != nil && uids != nil { - err := tryMappingTool("newuidmap", pid, os.Getuid(), uids) + if uids != nil { + err := tryMappingTool("newuidmap", pid, os.Geteuid(), uids) uidsMapped = err == nil } if !uidsMapped { @@ -416,20 +433,20 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool, } uidMap := fmt.Sprintf("/proc/%d/uid_map", pid) - err = ioutil.WriteFile(uidMap, []byte(fmt.Sprintf("%d %d 1\n", 0, os.Getuid())), 0666) + 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") } } gidsMapped := false - if mappings != nil && gids != nil { - err := tryMappingTool("newgidmap", pid, os.Getgid(), gids) + if gids != nil { + err := tryMappingTool("newgidmap", pid, os.Getegid(), gids) 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.Getgid())), 0666) + 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") } @@ -586,3 +603,85 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st return joinUserAndMountNS(uint(pausePid), pausePidPath) } +func readMappingsProc(path string) ([]idtools.IDMap, error) { + file, err := os.Open(path) + if err != nil { + return nil, errors.Wrapf(err, "cannot open %s", path) + } + defer file.Close() + + mappings := []idtools.IDMap{} + + buf := bufio.NewReader(file) + for { + line, _, err := buf.ReadLine() + if err != nil { + if err == io.EOF { + return mappings, nil + } + return nil, errors.Wrapf(err, "cannot read line from %s", path) + } + if line == nil { + return mappings, nil + } + + containerID, hostID, size := 0, 0, 0 + if _, err := fmt.Sscanf(string(line), "%d %d %d", &containerID, &hostID, &size); err != nil { + return nil, errors.Wrapf(err, "cannot parse %s", string(line)) + } + mappings = append(mappings, idtools.IDMap{ContainerID: containerID, HostID: hostID, Size: size}) + } +} + +func matches(id int, configuredIDs []idtools.IDMap, currentIDs []idtools.IDMap) bool { + // The first mapping is the host user, handle it separately. + if currentIDs[0].HostID != id || currentIDs[0].Size != 1 { + return false + } + + currentIDs = currentIDs[1:] + if len(currentIDs) != len(configuredIDs) { + return false + } + + // It is fine to iterate sequentially as both slices are sorted. + for i := range currentIDs { + if currentIDs[i].HostID != configuredIDs[i].HostID { + return false + } + if currentIDs[i].Size != configuredIDs[i].Size { + return false + } + } + + return true +} + +// ConfigurationMatches checks whether the additional uids/gids configured for the user +// match the current user namespace. +func ConfigurationMatches() (bool, error) { + if !IsRootless() || os.Geteuid() != 0 { + return true, nil + } + + uids, gids, err := GetConfiguredMappings() + if err != nil { + return false, err + } + + currentUIDs, err := readMappingsProc("/proc/self/uid_map") + if err != nil { + return false, err + } + + if !matches(GetRootlessUID(), uids, currentUIDs) { + return false, err + } + + currentGIDs, err := readMappingsProc("/proc/self/gid_map") + if err != nil { + return false, err + } + + return matches(GetRootlessGID(), gids, currentGIDs), nil +} diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go index a8485c083..ddd9182b0 100644 --- a/pkg/rootless/rootless_unsupported.go +++ b/pkg/rootless/rootless_unsupported.go @@ -5,6 +5,7 @@ package rootless import ( "os" + "github.com/containers/storage/pkg/idtools" "github.com/pkg/errors" ) @@ -53,3 +54,14 @@ func EnableLinger() (string, error) { func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []string) (bool, int, error) { return false, -1, errors.New("this function is not supported on this os") } + +// ConfigurationMatches checks whether the additional uids/gids configured for the user +// match the current user namespace. +func ConfigurationMatches() (bool, error) { + return true, nil +} + +// GetConfiguredMappings returns the additional IDs configured for the current user. +func GetConfiguredMappings() ([]idtools.IDMap, []idtools.IDMap, error) { + return nil, nil, errors.New("this function is not supported on this os") +} diff --git a/pkg/util/utils.go b/pkg/util/utils.go index 3f73639e7..2261934f0 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -3,7 +3,6 @@ package util import ( "fmt" "os" - ouser "os/user" "path/filepath" "strings" "sync" @@ -156,22 +155,15 @@ func ParseIDMapping(mode namespaces.UsernsMode, UIDMapSlice, GIDMapSlice []strin uid := rootless.GetRootlessUID() gid := rootless.GetRootlessGID() - username := os.Getenv("USER") - if username == "" { - user, err := ouser.LookupId(fmt.Sprintf("%d", uid)) - if err == nil { - username = user.Username - } - } - mappings, err := idtools.NewIDMappings(username, username) + uids, gids, err := rootless.GetConfiguredMappings() if err != nil { - return nil, errors.Wrapf(err, "cannot find mappings for user %s", username) + return nil, errors.Wrapf(err, "cannot read mappings") } maxUID, maxGID := 0, 0 - for _, u := range mappings.UIDs() { + for _, u := range uids { maxUID += u.Size } - for _, g := range mappings.GIDs() { + for _, g := range gids { maxGID += g.Size } |