mirror of
https://github.com/containers/podman.git
synced 2025-06-24 11:28:24 +08:00
Generate a passwd file for users not in container
If someone runs podman as a user (uid) that is not defined in the container we want generate a passwd file so that getpwuid() will work inside of container. Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
@ -642,6 +642,11 @@ func (c *Container) Hostname() string {
|
|||||||
return c.ID()[:12]
|
return c.ID()[:12]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WorkingDir returns the containers working dir
|
||||||
|
func (c *Container) WorkingDir() string {
|
||||||
|
return c.config.Spec.Process.Cwd
|
||||||
|
}
|
||||||
|
|
||||||
// State Accessors
|
// State Accessors
|
||||||
// Require locking
|
// Require locking
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
@ -946,6 +947,19 @@ func (c *Container) makeBindMounts() error {
|
|||||||
}
|
}
|
||||||
c.state.BindMounts["/etc/resolv.conf"] = newResolv
|
c.state.BindMounts["/etc/resolv.conf"] = newResolv
|
||||||
|
|
||||||
|
newPasswd, err := c.generatePasswd()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error creating temporary passwd file for container %s", c.ID())
|
||||||
|
}
|
||||||
|
if newPasswd != "" {
|
||||||
|
// Make /etc/passwd
|
||||||
|
if _, ok := c.state.BindMounts["/etc/passwd"]; ok {
|
||||||
|
// If it already exists, delete so we can recreate
|
||||||
|
delete(c.state.BindMounts, "/etc/passwd")
|
||||||
|
}
|
||||||
|
logrus.Debugf("adding entry to /etc/passwd for non existent default user")
|
||||||
|
c.state.BindMounts["/etc/passwd"] = newPasswd
|
||||||
|
}
|
||||||
// Make /etc/hosts
|
// Make /etc/hosts
|
||||||
if _, ok := c.state.BindMounts["/etc/hosts"]; ok {
|
if _, ok := c.state.BindMounts["/etc/hosts"]; ok {
|
||||||
// If it already exists, delete so we can recreate
|
// If it already exists, delete so we can recreate
|
||||||
@ -1017,6 +1031,58 @@ func (c *Container) writeStringToRundir(destFile, output string) (string, error)
|
|||||||
return filepath.Join(c.state.DestinationRunDir, destFile), nil
|
return filepath.Join(c.state.DestinationRunDir, destFile), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generatePasswd generates a container specific passwd file,
|
||||||
|
// iff g.config.User is a number
|
||||||
|
func (c *Container) generatePasswd() (string, error) {
|
||||||
|
var (
|
||||||
|
groupspec string
|
||||||
|
gid uint32
|
||||||
|
)
|
||||||
|
if c.config.User == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
spec := strings.SplitN(c.config.User, ":", 2)
|
||||||
|
userspec := spec[0]
|
||||||
|
if len(spec) > 1 {
|
||||||
|
groupspec = spec[1]
|
||||||
|
}
|
||||||
|
// If a non numeric User, then don't generate passwd
|
||||||
|
uid, err := strconv.ParseUint(userspec, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
// if UID exists inside of container rootfs /etc/passwd then
|
||||||
|
// don't generate passwd
|
||||||
|
if _, _, err := chrootuser.LookupUIDInContainer(c.state.Mountpoint, uid); err == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
if err == nil && groupspec != "" {
|
||||||
|
if !c.state.Mounted {
|
||||||
|
return "", errors.Wrapf(ErrCtrStateInvalid, "container %s must be mounted in order to translate group field for passwd record", c.ID())
|
||||||
|
}
|
||||||
|
gid, err = chrootuser.GetGroup(c.state.Mountpoint, groupspec)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "unable to get gid from %s formporary passwd file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
originPasswdFile := filepath.Join(c.state.Mountpoint, "/etc/passwd")
|
||||||
|
orig, err := ioutil.ReadFile(originPasswdFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "unable to read passwd file %s", originPasswdFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
pwd := fmt.Sprintf("%s%d:x:%d:%d:container user:%s:/bin/sh\n", orig, uid, uid, gid, c.WorkingDir())
|
||||||
|
passwdFile, err := c.writeStringToRundir("passwd", pwd)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "failed to create temporary passwd fileo")
|
||||||
|
}
|
||||||
|
if os.Chmod(passwdFile, 0644); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return passwdFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
// generateResolvConf generates a containers resolv.conf
|
// generateResolvConf generates a containers resolv.conf
|
||||||
func (c *Container) generateResolvConf() (string, error) {
|
func (c *Container) generateResolvConf() (string, error) {
|
||||||
// Determine the endpoint for resolv.conf in case it is a symlink
|
// Determine the endpoint for resolv.conf in case it is a symlink
|
||||||
|
@ -99,3 +99,10 @@ func GetAdditionalGroupsForUser(rootdir string, userid uint64) ([]uint32, error)
|
|||||||
}
|
}
|
||||||
return gids, nil
|
return gids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LookupUIDInContainer returns username and gid associated with a UID in a container
|
||||||
|
// it will use the /etc/passwd files inside of the rootdir
|
||||||
|
// to return this information.
|
||||||
|
func LookupUIDInContainer(rootdir string, uid uint64) (user string, gid uint64, err error) {
|
||||||
|
return lookupUIDInContainer(rootdir, uid)
|
||||||
|
}
|
||||||
|
@ -21,3 +21,7 @@ func lookupGroupForUIDInContainer(rootdir string, userid uint64) (string, uint64
|
|||||||
func lookupAdditionalGroupsForUIDInContainer(rootdir string, userid uint64) (gid []uint32, err error) {
|
func lookupAdditionalGroupsForUIDInContainer(rootdir string, userid uint64) (gid []uint32, err error) {
|
||||||
return nil, errors.New("supplemental groups list lookup by uid not supported")
|
return nil, errors.New("supplemental groups list lookup by uid not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lookupUIDInContainer(rootdir string, uid uint64) (string, uint64, error) {
|
||||||
|
return "", 0, errors.New("UID lookup not supported")
|
||||||
|
}
|
||||||
|
@ -265,3 +265,29 @@ func lookupGroupInContainer(rootdir, groupname string) (gid uint64, err error) {
|
|||||||
|
|
||||||
return 0, user.UnknownGroupError(fmt.Sprintf("error looking up group %q", groupname))
|
return 0, user.UnknownGroupError(fmt.Sprintf("error looking up group %q", groupname))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lookupUIDInContainer(rootdir string, uid uint64) (string, uint64, error) {
|
||||||
|
cmd, f, err := openChrootedFile(rootdir, "/etc/passwd")
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = cmd.Wait()
|
||||||
|
}()
|
||||||
|
rc := bufio.NewReader(f)
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
lookupUser.Lock()
|
||||||
|
defer lookupUser.Unlock()
|
||||||
|
|
||||||
|
pwd := parseNextPasswd(rc)
|
||||||
|
for pwd != nil {
|
||||||
|
if pwd.uid != uid {
|
||||||
|
pwd = parseNextPasswd(rc)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return pwd.name, pwd.gid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", 0, user.UnknownUserError(fmt.Sprintf("error looking up uid %q", uid))
|
||||||
|
}
|
||||||
|
60
test/e2e/run_passwd_test.go
Normal file
60
test/e2e/run_passwd_test.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Podman run passwd", func() {
|
||||||
|
var (
|
||||||
|
tempdir string
|
||||||
|
err error
|
||||||
|
podmanTest PodmanTest
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
tempdir, err = CreateTempDirInTempDir()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
podmanTest = PodmanCreate(tempdir)
|
||||||
|
podmanTest.RestoreAllArtifacts()
|
||||||
|
})
|
||||||
|
|
||||||
|
AfterEach(func() {
|
||||||
|
podmanTest.Cleanup()
|
||||||
|
f := CurrentGinkgoTestDescription()
|
||||||
|
timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds())
|
||||||
|
GinkgoWriter.Write([]byte(timedResult))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman run no user specified ", func() {
|
||||||
|
session := podmanTest.Podman([]string{"run", ALPINE, "mount"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
Expect(session.LineInOutputContains("passwd")).To(BeFalse())
|
||||||
|
})
|
||||||
|
It("podman run user specified in container", func() {
|
||||||
|
session := podmanTest.Podman([]string{"run", "-u", "bin", ALPINE, "mount"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
Expect(session.LineInOutputContains("passwd")).To(BeFalse())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman run UID specified in container", func() {
|
||||||
|
session := podmanTest.Podman([]string{"run", "-u", "2:1", ALPINE, "mount"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
Expect(session.LineInOutputContains("passwd")).To(BeFalse())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman run UID not specified in container", func() {
|
||||||
|
session := podmanTest.Podman([]string{"run", "-u", "20001:1", ALPINE, "mount"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
Expect(session.LineInOutputContains("passwd")).To(BeTrue())
|
||||||
|
})
|
||||||
|
})
|
@ -401,7 +401,7 @@ var _ = Describe("Podman run", func() {
|
|||||||
session := podmanTest.Podman([]string{"run", "--rm", "--user=1234", ALPINE, "id"})
|
session := podmanTest.Podman([]string{"run", "--rm", "--user=1234", ALPINE, "id"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session.ExitCode()).To(Equal(0))
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
Expect(session.OutputToString()).To(Equal("uid=1234 gid=0(root)"))
|
Expect(session.OutputToString()).To(Equal("uid=1234(1234) gid=0(root)"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman run with user (integer, in /etc/passwd)", func() {
|
It("podman run with user (integer, in /etc/passwd)", func() {
|
||||||
|
Reference in New Issue
Block a user