mirror of
https://github.com/containers/podman.git
synced 2025-12-09 23:27:09 +08:00
I found the ginkgolinter[1] by accident, this looks for not optimal matching and suggest how to do it better. Overall these fixes seem to be all correct and they will give much better error messages when something fails. Check out the repo to see what the linter reports. [1] https://github.com/nunnatsa/ginkgolinter Signed-off-by: Paul Holzinger <pholzing@redhat.com>
432 lines
16 KiB
Go
432 lines
16 KiB
Go
package integration
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
. "github.com/containers/podman/v4/test/utils"
|
|
"github.com/containers/storage"
|
|
. "github.com/onsi/ginkgo"
|
|
. "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() {
|
|
var (
|
|
tempdir string
|
|
err error
|
|
podmanTest *PodmanTestIntegration
|
|
)
|
|
|
|
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.")
|
|
}
|
|
tempdir, err = CreateTempDirInTempDir()
|
|
if err != nil {
|
|
os.Exit(1)
|
|
}
|
|
podmanTest = PodmanTestCreate(tempdir)
|
|
podmanTest.Setup()
|
|
})
|
|
|
|
AfterEach(func() {
|
|
podmanTest.Cleanup()
|
|
f := CurrentGinkgoTestDescription()
|
|
processTestResult(f)
|
|
os.Unsetenv("CONTAINERS_CONF")
|
|
})
|
|
|
|
// 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.Name
|
|
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(Exit(0))
|
|
// `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(fmt.Sprintf("%d", 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(Exit(0))
|
|
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", "-it", "alpine", "echo", "hello"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(Exit(0))
|
|
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(Exit(0))
|
|
Expect(session.OutputToString()).To(ContainSubstring("hello"))
|
|
})
|
|
|
|
It("podman uidmapping and gidmapping with an idmapped volume", func() {
|
|
session := podmanTest.Podman([]string{"run", "--uidmap=0:1:500", "--gidmap=0:200:5000", "-v", "my-foo-volume:/foo:Z,idmap", "alpine", "echo", "hello"})
|
|
session.WaitWithDefaultTimeout()
|
|
if strings.Contains(session.ErrorToString(), "Operation not permitted") {
|
|
Skip("not sufficiently privileged")
|
|
}
|
|
Expect(session).Should(Exit(0))
|
|
Expect(session.OutputToString()).To(ContainSubstring("hello"))
|
|
})
|
|
|
|
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(Exit(0))
|
|
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()
|
|
if os.Geteuid() == 0 {
|
|
Expect(session).Should(Exit(125))
|
|
return
|
|
}
|
|
|
|
Expect(session).Should(Exit(0))
|
|
uid := fmt.Sprintf("%d", 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()
|
|
if os.Geteuid() == 0 {
|
|
Expect(session).Should(Exit(125))
|
|
return
|
|
}
|
|
|
|
Expect(session).Should(Exit(0))
|
|
Expect(session.OutputToString()).To(ContainSubstring("10:12"))
|
|
})
|
|
|
|
It("podman --userns=keep-id check passwd", func() {
|
|
SkipIfNotRootless("keep-id only works in rootless mode")
|
|
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-un"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(Exit(0))
|
|
u, err := user.Current()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(session.OutputToString()).To(ContainSubstring(u.Name))
|
|
})
|
|
|
|
It("podman --userns=keep-id root owns /usr", func() {
|
|
SkipIfNotRootless("keep-id only works in rootless mode")
|
|
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "stat", "-c%u", "/usr"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(Exit(0))
|
|
Expect(session.OutputToString()).To(Equal("0"))
|
|
})
|
|
|
|
It("podman --userns=keep-id --user root:root", func() {
|
|
SkipIfNotRootless("keep-id only works in rootless mode")
|
|
session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", "alpine", "id", "-u"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(Exit(0))
|
|
Expect(session.OutputToString()).To(Equal("0"))
|
|
})
|
|
|
|
It("podman run --userns=keep-id can add users", func() {
|
|
SkipIfNotRootless("keep-id only works in rootless mode")
|
|
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, fedoraMinimal, "sleep", "600"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(Exit(0))
|
|
|
|
exec1 := podmanTest.Podman([]string{"exec", "-t", "-i", ctrName, "cat", "/etc/passwd"})
|
|
exec1.WaitWithDefaultTimeout()
|
|
Expect(exec1).Should(Exit(0))
|
|
Expect(exec1.OutputToString()).To(ContainSubstring(userName))
|
|
|
|
exec2 := podmanTest.Podman([]string{"exec", "-t", "-i", ctrName, "useradd", "testuser"})
|
|
exec2.WaitWithDefaultTimeout()
|
|
Expect(exec2).Should(Exit(0))
|
|
})
|
|
|
|
It("podman --userns=auto", func() {
|
|
u, err := user.Current()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
name := u.Name
|
|
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(Exit(0))
|
|
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(Exit(0))
|
|
Expect(session.OutputToString()).To(ContainSubstring("1019"))
|
|
})
|
|
|
|
It("podman --userns=auto:size=%d", func() {
|
|
u, err := user.Current()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
name := u.Name
|
|
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(Exit(0))
|
|
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(Exit(0))
|
|
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(Exit(0))
|
|
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(Exit(0))
|
|
Expect(session.OutputToString()).To(ContainSubstring("4001"))
|
|
})
|
|
|
|
It("podman --userns=auto:uidmapping=", func() {
|
|
u, err := user.Current()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
name := u.Name
|
|
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(Exit(0))
|
|
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(Exit(0))
|
|
Expect(session.OutputToString()).To(ContainSubstring("8191"))
|
|
})
|
|
|
|
It("podman --userns=auto:gidmapping=", func() {
|
|
u, err := user.Current()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
name := u.Name
|
|
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(Exit(0))
|
|
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(Exit(0))
|
|
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:1:4998", "--name", ctrName, "alpine", "top"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(Exit(0))
|
|
|
|
// 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(Exit(0))
|
|
|
|
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(Exit(0))
|
|
|
|
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(Exit(0))
|
|
|
|
inspectUID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .UID }}", tt.vol})
|
|
inspectUID.WaitWithDefaultTimeout()
|
|
Expect(inspectUID).Should(Exit(0))
|
|
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(Exit(0))
|
|
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(Exit(0)))
|
|
|
|
session = podmanTest.Podman([]string{"run", "--userns=private", "--subgidname=containers", "alpine", "true"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).Should(Not(Exit(0)))
|
|
})
|
|
|
|
It("podman PODMAN_USERNS", func() {
|
|
SkipIfNotRootless("keep-id only works in rootless mode")
|
|
|
|
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(Exit(0))
|
|
|
|
inspect := podmanTest.Podman([]string{"inspect", "--format", "{{ .HostConfig.IDMappings }}", result.OutputToString()})
|
|
inspect.WaitWithDefaultTimeout()
|
|
Expect(inspect.OutputToString()).To(Not(Equal("<nil>")))
|
|
|
|
if IsRemote() {
|
|
podmanTest.RestartRemoteService()
|
|
}
|
|
})
|
|
})
|