mirror of https://github.com/containers/podman.git
Merge pull request #3926 from giuseppe/add-warning-mismatch-configuration
rootless: detect user namespace configuration changes
This commit is contained in:
commit
a16f63e96e
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue