mirror of
https://github.com/containers/podman.git
synced 2025-06-23 02:18:13 +08:00
Merge pull request #6829 from rhatdan/keepid
Add username to /etc/passwd inside of container if --userns keep-id
This commit is contained in:
@ -241,6 +241,8 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
|
||||
// If some mappings are specified, assume a private user namespace
|
||||
if userNS.IsDefaultValue() && (!s.IDMappings.HostUIDMapping || !s.IDMappings.HostGIDMapping) {
|
||||
s.UserNS.NSMode = specgen.Private
|
||||
} else {
|
||||
s.UserNS.NSMode = specgen.NamespaceMode(userNS)
|
||||
}
|
||||
|
||||
s.Terminal = c.TTY
|
||||
|
@ -278,6 +278,9 @@ type ContainerConfig struct {
|
||||
User string `json:"user,omitempty"`
|
||||
// Additional groups to add
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
// AddCurrentUserPasswdEntry indicates that the current user passwd entry
|
||||
// should be added to the /etc/passwd within the container
|
||||
AddCurrentUserPasswdEntry bool `json:"addCurrentUserPasswdEntry,omitempty"`
|
||||
|
||||
// Namespace Config
|
||||
// IDs of container to share namespaces with
|
||||
@ -786,7 +789,10 @@ func (c *Container) Hostname() string {
|
||||
|
||||
// WorkingDir returns the containers working dir
|
||||
func (c *Container) WorkingDir() string {
|
||||
return c.config.Spec.Process.Cwd
|
||||
if c.config.Spec.Process != nil {
|
||||
return c.config.Spec.Process.Cwd
|
||||
}
|
||||
return "/"
|
||||
}
|
||||
|
||||
// State Accessors
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@ -34,7 +35,7 @@ import (
|
||||
"github.com/containers/libpod/v2/utils"
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
securejoin "github.com/cyphar/filepath-securejoin"
|
||||
"github.com/opencontainers/runc/libcontainer/user"
|
||||
User "github.com/opencontainers/runc/libcontainer/user"
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/runtime-tools/generate"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
@ -1448,9 +1449,23 @@ func (c *Container) getHosts() string {
|
||||
return hosts
|
||||
}
|
||||
|
||||
// generatePasswd generates a container specific passwd file,
|
||||
// iff g.config.User is a number
|
||||
func (c *Container) generatePasswd() (string, error) {
|
||||
// generateCurrentUserPasswdEntry generates an /etc/passwd entry for the user
|
||||
// running the container engine
|
||||
func (c *Container) generateCurrentUserPasswdEntry() (string, error) {
|
||||
uid := rootless.GetRootlessUID()
|
||||
if uid == 0 {
|
||||
return "", nil
|
||||
}
|
||||
u, err := user.LookupId(strconv.Itoa(rootless.GetRootlessUID()))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to get current user")
|
||||
}
|
||||
return fmt.Sprintf("%s:x:%s:%s:%s:%s:/bin/sh\n", u.Username, u.Uid, u.Gid, u.Username, c.WorkingDir()), nil
|
||||
}
|
||||
|
||||
// generateUserPasswdEntry generates an /etc/passwd entry for the container user
|
||||
// to run in the container.
|
||||
func (c *Container) generateUserPasswdEntry() (string, error) {
|
||||
var (
|
||||
groupspec string
|
||||
gid int
|
||||
@ -1468,14 +1483,16 @@ func (c *Container) generatePasswd() (string, error) {
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Lookup the user to see if it exists in the container image
|
||||
_, err = lookup.GetUser(c.state.Mountpoint, userspec)
|
||||
if err != nil && err != user.ErrNoPasswdEntries {
|
||||
if err != nil && err != User.ErrNoPasswdEntries {
|
||||
return "", err
|
||||
}
|
||||
if err == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if groupspec != "" {
|
||||
ugid, err := strconv.ParseUint(groupspec, 10, 32)
|
||||
if err == nil {
|
||||
@ -1488,14 +1505,39 @@ func (c *Container) generatePasswd() (string, error) {
|
||||
gid = group.Gid
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%d:x:%d:%d:container user:%s:/bin/sh\n", uid, uid, gid, c.WorkingDir()), nil
|
||||
}
|
||||
|
||||
// generatePasswd generates a container specific passwd file,
|
||||
// iff g.config.User is a number
|
||||
func (c *Container) generatePasswd() (string, error) {
|
||||
if !c.config.AddCurrentUserPasswdEntry && c.config.User == "" {
|
||||
return "", nil
|
||||
}
|
||||
pwd := ""
|
||||
if c.config.User != "" {
|
||||
entry, err := c.generateUserPasswdEntry()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pwd += entry
|
||||
}
|
||||
if c.config.AddCurrentUserPasswdEntry {
|
||||
entry, err := c.generateCurrentUserPasswdEntry()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pwd += entry
|
||||
}
|
||||
if pwd == "" {
|
||||
return "", nil
|
||||
}
|
||||
originPasswdFile := filepath.Join(c.state.Mountpoint, "/etc/passwd")
|
||||
orig, err := ioutil.ReadFile(originPasswdFile)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
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)
|
||||
passwdFile, err := c.writeStringToRundir("passwd", string(orig)+pwd)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to create temporary passwd file")
|
||||
}
|
||||
|
42
libpod/container_internal_linux_test.go
Normal file
42
libpod/container_internal_linux_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
// +build linux
|
||||
|
||||
package libpod
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGenerateUserPasswdEntry(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "libpod_test_")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
c := Container{
|
||||
config: &ContainerConfig{
|
||||
User: "123:456",
|
||||
Spec: &spec.Spec{},
|
||||
},
|
||||
state: &ContainerState{
|
||||
Mountpoint: "/does/not/exist/tmp/",
|
||||
},
|
||||
}
|
||||
user, err := c.generateUserPasswdEntry()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, user, "123:x:123:456:container user:/:/bin/sh\n")
|
||||
|
||||
c.config.User = "567"
|
||||
user, err = c.generateUserPasswdEntry()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, user, "567:x:567:0:container user:/:/bin/sh\n")
|
||||
}
|
@ -866,6 +866,20 @@ func WithPIDNSFrom(nsCtr *Container) CtrCreateOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithAddCurrentUserPasswdEntry indicates that container should add current
|
||||
// user entry to /etc/passwd, since the UID will be mapped into the container,
|
||||
// via user namespace
|
||||
func WithAddCurrentUserPasswdEntry() CtrCreateOption {
|
||||
return func(ctr *Container) error {
|
||||
if ctr.valid {
|
||||
return define.ErrCtrFinalized
|
||||
}
|
||||
|
||||
ctr.config.AddCurrentUserPasswdEntry = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithUserNSFrom indicates the the container should join the user namespace of
|
||||
// the given container.
|
||||
// If the container has joined a pod, it can only join the namespaces of
|
||||
|
@ -153,7 +153,9 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.
|
||||
// User
|
||||
switch s.UserNS.NSMode {
|
||||
case specgen.KeepID:
|
||||
if !rootless.IsRootless() {
|
||||
if rootless.IsRootless() {
|
||||
toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry())
|
||||
} else {
|
||||
// keep-id as root doesn't need a user namespace
|
||||
s.UserNS.NSMode = specgen.Host
|
||||
}
|
||||
|
@ -89,6 +89,16 @@ var _ = Describe("Podman UserNS support", func() {
|
||||
Expect(ok).To(BeTrue())
|
||||
})
|
||||
|
||||
It("podman --userns=keep-id check passwd", func() {
|
||||
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-un"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
u, err := user.Current()
|
||||
Expect(err).To(BeNil())
|
||||
ok, _ := session.GrepString(u.Name)
|
||||
Expect(ok).To(BeTrue())
|
||||
})
|
||||
|
||||
It("podman --userns=keep-id root owns /usr", func() {
|
||||
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "stat", "-c%u", "/usr"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
|
Reference in New Issue
Block a user