summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel J Walsh <dwalsh@redhat.com>2020-06-30 15:44:14 -0400
committerValentin Rothberg <rothberg@redhat.com>2020-08-11 14:58:29 +0200
commit3dfd8630a51a37734ad8c51162c4d004b8ffffb2 (patch)
tree6ebf1d923f718c6717add408a11c714070ccc995
parent92d01d76c63d0a286365f610151524fb24b74e16 (diff)
downloadpodman-3dfd8630a51a37734ad8c51162c4d004b8ffffb2.tar.gz
podman-3dfd8630a51a37734ad8c51162c4d004b8ffffb2.tar.bz2
podman-3dfd8630a51a37734ad8c51162c4d004b8ffffb2.zip
Add username to /etc/passwd inside of container if --userns keep-id
If I enter a continer with --userns keep-id, my UID will be present inside of the container, but most likely my user will not be defined. This patch will take information about the user and stick it into the container. Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
-rw-r--r--cmd/podman/common/specgen.go2
-rw-r--r--libpod/container.go8
-rw-r--r--libpod/container_internal_linux.go58
-rw-r--r--libpod/container_internal_linux_test.go42
-rw-r--r--libpod/options.go14
-rw-r--r--pkg/specgen/generate/namespaces.go4
-rw-r--r--test/e2e/run_userns_test.go10
7 files changed, 128 insertions, 10 deletions
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index 7716fc150..2333f2f7e 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -260,6 +260,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
diff --git a/libpod/container.go b/libpod/container.go
index 6a87ad3a9..9ad938a5c 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -277,6 +277,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
@@ -774,7 +777,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
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index cfcf9b823..f86903fd1 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -9,6 +9,7 @@ import (
"io/ioutil"
"net"
"os"
+ "os/user"
"path"
"path/filepath"
"strconv"
@@ -33,7 +34,7 @@ import (
"github.com/containers/libpod/v2/pkg/util"
"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"
@@ -1420,9 +1421,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
@@ -1440,14 +1455,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 {
@@ -1460,14 +1477,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")
}
diff --git a/libpod/container_internal_linux_test.go b/libpod/container_internal_linux_test.go
new file mode 100644
index 000000000..078cc53a7
--- /dev/null
+++ b/libpod/container_internal_linux_test.go
@@ -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")
+}
diff --git a/libpod/options.go b/libpod/options.go
index bff3f3c18..560b406e2 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -844,6 +844,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
diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go
index 39a45398d..22670ca61 100644
--- a/pkg/specgen/generate/namespaces.go
+++ b/pkg/specgen/generate/namespaces.go
@@ -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
}
diff --git a/test/e2e/run_userns_test.go b/test/e2e/run_userns_test.go
index c0d98f7b1..24d3e42eb 100644
--- a/test/e2e/run_userns_test.go
+++ b/test/e2e/run_userns_test.go
@@ -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()