Skip to content

Commit

Permalink
Merge pull request #6829 from rhatdan/keepid
Browse files Browse the repository at this point in the history
Add username to /etc/passwd inside of container if --userns keep-id
  • Loading branch information
openshift-merge-robot authored Jul 7, 2020
2 parents cd08485 + 6c6670f commit 54d16f3
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 10 deletions.
2 changes: 2 additions & 0 deletions cmd/podman/common/specgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion libpod/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
58 changes: 50 additions & 8 deletions libpod/container_internal_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"io/ioutil"
"net"
"os"
"os/user"
"path"
"path/filepath"
"strconv"
Expand All @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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")
}
Expand Down
42 changes: 42 additions & 0 deletions libpod/container_internal_linux_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
14 changes: 14 additions & 0 deletions libpod/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion pkg/specgen/generate/namespaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
10 changes: 10 additions & 0 deletions test/e2e/run_userns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 54d16f3

Please sign in to comment.