mirror of
https://github.com/containers/podman.git
synced 2025-05-20 08:36:23 +08:00
410 lines
15 KiB
Go
410 lines
15 KiB
Go
package integration
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
. "github.com/containers/podman/v4/test/utils"
|
|
"github.com/containers/storage"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
. "github.com/onsi/gomega/gexec"
|
|
)
|
|
|
|
func createContainersConfFileWithCustomUserns(pTest *PodmanTestIntegration, userns string) {
|
|
configPath := filepath.Join(pTest.TempDir, "containers.conf")
|
|
containersConf := []byte(fmt.Sprintf("[containers]\nuserns = \"%s\"\n", userns))
|
|
err := os.WriteFile(configPath, containersConf, os.ModePerm)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
// Set custom containers.conf file
|
|
os.Setenv("CONTAINERS_CONF", configPath)
|
|
if IsRemote() {
|
|
pTest.RestartRemoteService()
|
|
}
|
|
}
|
|
|
|
var _ = Describe("Podman UserNS support", func() {
|
|
BeforeEach(func() {
|
|
if os.Getenv("SKIP_USERNS") != "" {
|
|
Skip("Skip userns tests.")
|
|
}
|
|
if _, err := os.Stat("/proc/self/uid_map"); err != nil {
|
|
Skip("User namespaces not supported.")
|
|
}
|
|
})
|
|
|
|
// Note: Lot of tests for build with --userns=auto are already there in buildah
|
|
// but they are skipped in podman CI because bud tests are executed in rootfull
|
|
// environment ( where mappings for the `containers` user is not present in /etc/subuid )
|
|
// causing them to skip hence this is a redundant test for sanity to make sure
|
|
// we don't break this feature for podman-remote.
|
|
It("podman build with --userns=auto", func() {
|
|
u, err := user.Current()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
name := u.Username
|
|
if name == "root" {
|
|
name = "containers"
|
|
}
|
|
content, err := os.ReadFile("/etc/subuid")
|
|
if err != nil {
|
|
Skip("cannot read /etc/subuid")
|
|
}
|
|
if !strings.Contains(string(content), name) {
|
|
Skip("cannot find mappings for the current user")
|
|
}
|
|
session := podmanTest.Podman([]string{"build", "-f", "build/Containerfile.userns-auto", "-t", "test", "--userns=auto"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
// `1024` is the default size or length of the range of user IDs
|
|
// that is mapped between the two user namespaces by --userns=auto.
|
|
Expect(session.OutputToString()).To(ContainSubstring(strconv.Itoa(storage.AutoUserNsMinSize)))
|
|
})
|
|
|
|
It("podman uidmapping and gidmapping", func() {
|
|
session := podmanTest.Podman([]string{"run", "--uidmap=0:100:5000", "--gidmap=0:200:5000", "alpine", "echo", "hello"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(ContainSubstring("hello"))
|
|
})
|
|
|
|
// It essentially repeats the test above but with the `-it` short option
|
|
// that broke execution at:
|
|
// https://github.com/containers/podman/pull/1066#issuecomment-403562116
|
|
// To avoid a potential future regression, use this as a test.
|
|
It("podman uidmapping and gidmapping with short-opts", func() {
|
|
session := podmanTest.Podman([]string{"run", "--uidmap=0:1:5000", "--gidmap=0:200:5000", "alpine", "echo", "hello"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(ContainSubstring("hello"))
|
|
})
|
|
|
|
It("podman uidmapping and gidmapping with a volume", func() {
|
|
session := podmanTest.Podman([]string{"run", "--uidmap=0:1:500", "--gidmap=0:200:5000", "-v", "my-foo-volume:/foo:Z", "alpine", "echo", "hello"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(ContainSubstring("hello"))
|
|
})
|
|
|
|
It("podman uidmapping and gidmapping with an idmapped volume", func() {
|
|
SkipIfRunc(podmanTest, "Test not supported yet with runc (issue 17433, wontfix)")
|
|
SkipOnOSVersion("fedora", "36")
|
|
session := podmanTest.Podman([]string{"run", "--uidmap=0:1:500", "--gidmap=0:200:5000", "-v", "my-foo-volume:/foo:Z,idmap", "alpine", "stat", "-c", "#%u:%g#", "/foo"})
|
|
session.WaitWithDefaultTimeout()
|
|
if strings.Contains(session.ErrorToString(), "Operation not permitted") {
|
|
Skip("not sufficiently privileged")
|
|
}
|
|
if strings.Contains(session.ErrorToString(), "Invalid argument") {
|
|
Skip("the file system doesn't support idmapped mounts")
|
|
}
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(ContainSubstring("#0:0#"))
|
|
})
|
|
|
|
It("podman uidmapping and gidmapping --net=host", func() {
|
|
session := podmanTest.Podman([]string{"run", "--net=host", "--uidmap=0:1:5000", "--gidmap=0:200:5000", "alpine", "echo", "hello"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(ContainSubstring("hello"))
|
|
})
|
|
|
|
It("podman --userns=keep-id", func() {
|
|
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-u"})
|
|
session.WaitWithDefaultTimeout()
|
|
|
|
Expect(session).Should(ExitCleanly())
|
|
uid := strconv.Itoa(os.Geteuid())
|
|
Expect(session.OutputToString()).To(ContainSubstring(uid))
|
|
|
|
session = podmanTest.Podman([]string{"run", "--userns=keep-id:uid=10,gid=12", "alpine", "sh", "-c", "echo $(id -u):$(id -g)"})
|
|
session.WaitWithDefaultTimeout()
|
|
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(ContainSubstring("10:12"))
|
|
})
|
|
|
|
It("podman --userns=keep-id check passwd", func() {
|
|
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-un"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
u, err := user.Current()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(session.OutputToString()).To(Equal(u.Username))
|
|
})
|
|
|
|
It("podman --userns=keep-id root owns /usr", func() {
|
|
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "stat", "-c%u", "/usr"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(Equal("0"))
|
|
})
|
|
|
|
It("podman --userns=keep-id --user root:root", func() {
|
|
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", "alpine", "id", "-u"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(Equal("0"))
|
|
})
|
|
|
|
It("podman run --userns=keep-id can add users", func() {
|
|
userName := os.Getenv("USER")
|
|
if userName == "" {
|
|
Skip("Can't complete test if no username available")
|
|
}
|
|
|
|
ctrName := "ctr-name"
|
|
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", "-d", "--stop-signal", "9", "--name", ctrName, CITEST_IMAGE, "sleep", "600"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
|
|
exec1 := podmanTest.Podman([]string{"exec", ctrName, "cat", "/etc/passwd"})
|
|
exec1.WaitWithDefaultTimeout()
|
|
Expect(exec1).Should(ExitCleanly())
|
|
Expect(exec1.OutputToString()).To(ContainSubstring(userName))
|
|
|
|
exec2 := podmanTest.Podman([]string{"exec", ctrName, "adduser", "-D", "testuser"})
|
|
exec2.WaitWithDefaultTimeout()
|
|
Expect(exec2).Should(ExitCleanly())
|
|
})
|
|
|
|
It("podman --userns=auto", func() {
|
|
u, err := user.Current()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
name := u.Username
|
|
if name == "root" {
|
|
name = "containers"
|
|
}
|
|
|
|
content, err := os.ReadFile("/etc/subuid")
|
|
if err != nil {
|
|
Skip("cannot read /etc/subuid")
|
|
}
|
|
if !strings.Contains(string(content), name) {
|
|
Skip("cannot find mappings for the current user")
|
|
}
|
|
|
|
m := make(map[string]string)
|
|
for i := 0; i < 5; i++ {
|
|
session := podmanTest.Podman([]string{"run", "--userns=auto", "alpine", "cat", "/proc/self/uid_map"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
l := session.OutputToString()
|
|
// `1024` is the default size or length of the range of user IDs
|
|
// that is mapped between the two user namespaces by --userns=auto.
|
|
Expect(l).To(ContainSubstring("1024"))
|
|
m[l] = l
|
|
}
|
|
// check for no duplicates
|
|
Expect(m).To(HaveLen(5))
|
|
|
|
createContainersConfFileWithCustomUserns(podmanTest, "auto:size=1019")
|
|
session := podmanTest.Podman([]string{"run", "alpine", "cat", "/proc/self/uid_map"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(ContainSubstring("1019"))
|
|
})
|
|
|
|
It("podman --userns=auto:size=%d", func() {
|
|
u, err := user.Current()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
name := u.Username
|
|
if name == "root" {
|
|
name = "containers"
|
|
}
|
|
|
|
content, err := os.ReadFile("/etc/subuid")
|
|
if err != nil {
|
|
Skip("cannot read /etc/subuid")
|
|
}
|
|
if !strings.Contains(string(content), name) {
|
|
Skip("cannot find mappings for the current user")
|
|
}
|
|
|
|
session := podmanTest.Podman([]string{"run", "--userns=auto:size=500", "alpine", "cat", "/proc/self/uid_map"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(ContainSubstring("500"))
|
|
|
|
session = podmanTest.Podman([]string{"run", "--userns=auto:size=3000", "alpine", "cat", "/proc/self/uid_map"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(ContainSubstring("3000"))
|
|
|
|
session = podmanTest.Podman([]string{"run", "--userns=auto", "--user=2000:3000", "alpine", "cat", "/proc/self/uid_map"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(ContainSubstring("3001"))
|
|
|
|
session = podmanTest.Podman([]string{"run", "--userns=auto", "--user=4000:1000", "alpine", "cat", "/proc/self/uid_map"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(ContainSubstring("4001"))
|
|
})
|
|
|
|
It("podman --userns=auto:uidmapping=", func() {
|
|
u, err := user.Current()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
name := u.Username
|
|
if name == "root" {
|
|
name = "containers"
|
|
}
|
|
|
|
content, err := os.ReadFile("/etc/subuid")
|
|
if err != nil {
|
|
Skip("cannot read /etc/subuid")
|
|
}
|
|
if !strings.Contains(string(content), name) {
|
|
Skip("cannot find mappings for the current user")
|
|
}
|
|
|
|
session := podmanTest.Podman([]string{"run", "--userns=auto:uidmapping=0:0:1", "alpine", "cat", "/proc/self/uid_map"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
output := session.OutputToString()
|
|
Expect(output).To(MatchRegexp("\\s0\\s0\\s1"))
|
|
|
|
session = podmanTest.Podman([]string{"run", "--userns=auto:size=8192,uidmapping=0:0:1", "alpine", "cat", "/proc/self/uid_map"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(ContainSubstring("8191"))
|
|
})
|
|
|
|
It("podman --userns=auto:gidmapping=", func() {
|
|
u, err := user.Current()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
name := u.Username
|
|
if name == "root" {
|
|
name = "containers"
|
|
}
|
|
|
|
content, err := os.ReadFile("/etc/subuid")
|
|
if err != nil {
|
|
Skip("cannot read /etc/subuid")
|
|
}
|
|
if !strings.Contains(string(content), name) {
|
|
Skip("cannot find mappings for the current user")
|
|
}
|
|
|
|
session := podmanTest.Podman([]string{"run", "--userns=auto:gidmapping=0:0:1", "alpine", "cat", "/proc/self/gid_map"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
output := session.OutputToString()
|
|
Expect(output).To(MatchRegexp("\\s0\\s0\\s1"))
|
|
|
|
session = podmanTest.Podman([]string{"run", "--userns=auto:size=8192,gidmapping=0:0:1", "alpine", "cat", "/proc/self/gid_map"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
Expect(session.OutputToString()).To(ContainSubstring("8191"))
|
|
})
|
|
|
|
It("podman --userns=container:CTR", func() {
|
|
ctrName := "userns-ctr"
|
|
session := podmanTest.Podman([]string{"run", "-d", "--uidmap=0:0:1", "--uidmap=1:2:4998", "--name", ctrName, "alpine", "top"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
|
|
// runc has an issue and we also need to join the IPC namespace.
|
|
session = podmanTest.Podman([]string{"run", "--rm", "--userns=container:" + ctrName, "--ipc=container:" + ctrName, "alpine", "cat", "/proc/self/uid_map"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
|
|
Expect(session.OutputToString()).To(ContainSubstring("4998"))
|
|
|
|
session = podmanTest.Podman([]string{"run", "--rm", "--userns=container:" + ctrName, "--net=container:" + ctrName, "alpine", "cat", "/proc/self/uid_map"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
|
|
Expect(session.OutputToString()).To(ContainSubstring("4998"))
|
|
})
|
|
|
|
It("podman --user with volume", func() {
|
|
tests := []struct {
|
|
uid, gid, arg, vol string
|
|
}{
|
|
{"0", "0", "0:0", "vol-0"},
|
|
{"1000", "0", "1000", "vol-1"},
|
|
{"1000", "1000", "1000:1000", "vol-2"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
session := podmanTest.Podman([]string{"run", "-d", "--user", tt.arg, "--mount", "type=volume,src=" + tt.vol + ",dst=/home/user", "alpine", "top"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(ExitCleanly())
|
|
|
|
inspectUID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .UID }}", tt.vol})
|
|
inspectUID.WaitWithDefaultTimeout()
|
|
Expect(inspectUID).Should(ExitCleanly())
|
|
Expect(inspectUID.OutputToString()).To(Equal(tt.uid))
|
|
|
|
// Make sure we're defaulting to 0.
|
|
inspectGID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .GID }}", tt.vol})
|
|
inspectGID.WaitWithDefaultTimeout()
|
|
Expect(inspectGID).Should(ExitCleanly())
|
|
Expect(inspectGID.OutputToString()).To(Equal(tt.gid))
|
|
}
|
|
|
|
})
|
|
|
|
It("podman --userns= conflicts with ui[dg]map and sub[ug]idname", func() {
|
|
session := podmanTest.Podman([]string{"run", "--userns=host", "--uidmap=0:1:500", "alpine", "true"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(Exit(125))
|
|
Expect(session.ErrorToString()).To(ContainSubstring("--userns and --uidmap/--gidmap/--subuidname/--subgidname are mutually exclusive"))
|
|
|
|
session = podmanTest.Podman([]string{"run", "--userns=host", "--gidmap=0:200:5000", "alpine", "true"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(Exit(125))
|
|
Expect(session.ErrorToString()).To(ContainSubstring("--userns and --uidmap/--gidmap/--subuidname/--subgidname are mutually exclusive"))
|
|
|
|
// with sub[ug]idname we don't check for the error output since the error message could be different, depending on the
|
|
// system configuration since the specified user could not be defined and cause a different earlier error.
|
|
// In any case, make sure the command doesn't succeed.
|
|
session = podmanTest.Podman([]string{"run", "--userns=private", "--subuidname=containers", "alpine", "true"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(Not(ExitCleanly()))
|
|
|
|
session = podmanTest.Podman([]string{"run", "--userns=private", "--subgidname=containers", "alpine", "true"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(Not(ExitCleanly()))
|
|
})
|
|
|
|
It("podman PODMAN_USERNS", func() {
|
|
podmanUserns, podmanUserusSet := os.LookupEnv("PODMAN_USERNS")
|
|
os.Setenv("PODMAN_USERNS", "keep-id")
|
|
defer func() {
|
|
if podmanUserusSet {
|
|
os.Setenv("PODMAN_USERNS", podmanUserns)
|
|
} else {
|
|
os.Unsetenv("PODMAN_USERNS")
|
|
}
|
|
}()
|
|
if IsRemote() {
|
|
podmanTest.RestartRemoteService()
|
|
}
|
|
|
|
result := podmanTest.Podman([]string{"create", ALPINE, "true"})
|
|
result.WaitWithDefaultTimeout()
|
|
Expect(result).Should(ExitCleanly())
|
|
|
|
inspect := podmanTest.Podman([]string{"inspect", "--format", "{{ .HostConfig.IDMappings }}", result.OutputToString()})
|
|
inspect.WaitWithDefaultTimeout()
|
|
Expect(inspect.OutputToString()).To(Not(Equal("<nil>")))
|
|
|
|
// --pod should work.
|
|
result = podmanTest.Podman([]string{"create", "--pod=new:new-pod", ALPINE, "true"})
|
|
result.WaitWithDefaultTimeout()
|
|
Expect(result).Should(ExitCleanly())
|
|
|
|
if IsRemote() {
|
|
podmanTest.RestartRemoteService()
|
|
}
|
|
})
|
|
})
|