summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/apiv2/20-containers.at25
-rw-r--r--test/e2e/common_test.go4
-rw-r--r--test/e2e/create_staticip_test.go27
-rw-r--r--test/e2e/create_staticmac_test.go12
-rw-r--r--test/e2e/libpod_suite_remote_test.go5
-rw-r--r--test/e2e/libpod_suite_test.go6
-rw-r--r--test/e2e/pod_create_test.go35
-rw-r--r--test/e2e/pod_inspect_test.go21
-rw-r--r--test/e2e/run_entrypoint_test.go5
-rw-r--r--test/e2e/run_networking_test.go14
-rw-r--r--test/e2e/systemd_test.go35
-rw-r--r--test/system/030-run.bats42
-rw-r--r--test/system/075-exec.bats22
-rw-r--r--test/system/200-pod.bats8
-rw-r--r--test/system/400-unprivileged-access.bats65
-rw-r--r--test/system/410-selinux.bats9
-rw-r--r--test/system/helpers.bash32
-rwxr-xr-xtest/system/helpers.t70
18 files changed, 374 insertions, 63 deletions
diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at
index 9a1db5154..4bb00398e 100644
--- a/test/apiv2/20-containers.at
+++ b/test/apiv2/20-containers.at
@@ -51,17 +51,19 @@ cid=$(jq -r '.[0].Id' <<<"$output")
t DELETE libpod/containers/$cid 204
-# Ensure that API does not occur: Create Container creates an invalid and the container fails to start
-# https://github.com/containers/libpod/issues/6799
-CNAME=testArgs
-t POST libpod/containers/create?name=${CNAME} Image=${IMAGE} 201 \
+# Issue #6799: it should be possible to start a container, even w/o args.
+t POST libpod/containers/create?name=test_noargs Image=${IMAGE} 201 \
.Id~[0-9a-f]\\{64\\}
-t GET libpod/containers/json?limit=1 200 \
- length=1 \
- .[0].Id~[0-9a-f]\\{64\\}
-cid=$(jq -r '.[0].Id' <<<"$output")
-# This step should start the container properly
-t POST libpod/containers/${cid}/start '' 204
+cid=$(jq -r '.Id' <<<"$output")
+# Prior to the fix in #6835, this would fail 500 "args must not be empty"
+t POST libpod/containers/${cid}/start '' 204
+# Container should exit almost immediately. Wait for it, confirm successful run
+t POST libpod/containers/${cid}/wait '' 200 '0'
+t GET libpod/containers/${cid}/json 200 \
+ .Id=$cid \
+ .State.Status~\\\(exited\\\|stopped\\\) \
+ .State.Running=false \
+ .State.ExitCode=0
t DELETE libpod/containers/$cid 204
CNAME=myfoo
@@ -75,7 +77,8 @@ t POST "libpod/commit?container=nonesuch" '' 404
# Comment can only be used with docker format, not OCI
cparam="repo=newrepo&comment=foo&author=bob"
-t POST "libpod/commit?container=$CNAME&$cparam" '' 500
+t POST "libpod/commit?container=$CNAME&$cparam" '' 500 \
+ .cause="messages are only compatible with the docker image format (-f docker)"
# Commit a new image from the container
t POST "libpod/commit?container=$CNAME" '' 200 \
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index 51f290159..aa0e9635a 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -595,3 +595,7 @@ func SkipIfNotFedora() {
ginkgo.Skip("Test can only run on Fedora")
}
}
+
+func isRootless() bool {
+ return os.Geteuid() != 0
+}
diff --git a/test/e2e/create_staticip_test.go b/test/e2e/create_staticip_test.go
index e52b37417..a1a08045a 100644
--- a/test/e2e/create_staticip_test.go
+++ b/test/e2e/create_staticip_test.go
@@ -6,6 +6,7 @@ import (
"os"
"time"
+ "github.com/containers/libpod/v2/pkg/rootless"
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -19,7 +20,6 @@ var _ = Describe("Podman create with --ip flag", func() {
)
BeforeEach(func() {
- SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
@@ -39,18 +39,21 @@ var _ = Describe("Podman create with --ip flag", func() {
})
It("Podman create --ip with garbage address", func() {
+ SkipIfRootless()
result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", "114232346", ALPINE, "ls"})
result.WaitWithDefaultTimeout()
Expect(result).To(ExitWithError())
})
It("Podman create --ip with v6 address", func() {
+ SkipIfRootless()
result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", "2001:db8:bad:beef::1", ALPINE, "ls"})
result.WaitWithDefaultTimeout()
Expect(result).To(ExitWithError())
})
It("Podman create --ip with non-allocatable IP", func() {
+ SkipIfRootless()
result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", "203.0.113.124", ALPINE, "ls"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
@@ -64,19 +67,25 @@ var _ = Describe("Podman create with --ip flag", func() {
ip := GetRandomIPAddress()
result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", ip, ALPINE, "ip", "addr"})
result.WaitWithDefaultTimeout()
- Expect(result.ExitCode()).To(Equal(0))
+ // Rootless static ip assignment should error
+ if rootless.IsRootless() {
+ Expect(result.ExitCode()).To(Equal(125))
+ } else {
+ Expect(result.ExitCode()).To(Equal(0))
- result = podmanTest.Podman([]string{"start", "test"})
- result.WaitWithDefaultTimeout()
- Expect(result.ExitCode()).To(Equal(0))
+ result = podmanTest.Podman([]string{"start", "test"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
- result = podmanTest.Podman([]string{"logs", "test"})
- result.WaitWithDefaultTimeout()
- Expect(result.ExitCode()).To(Equal(0))
- Expect(result.OutputToString()).To(ContainSubstring(ip + "/16"))
+ result = podmanTest.Podman([]string{"logs", "test"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(result.OutputToString()).To(ContainSubstring(ip + "/16"))
+ }
})
It("Podman create two containers with the same IP", func() {
+ SkipIfRootless()
ip := GetRandomIPAddress()
result := podmanTest.Podman([]string{"create", "--name", "test1", "--ip", ip, ALPINE, "sleep", "999"})
result.WaitWithDefaultTimeout()
diff --git a/test/e2e/create_staticmac_test.go b/test/e2e/create_staticmac_test.go
index fbe11440c..33675d607 100644
--- a/test/e2e/create_staticmac_test.go
+++ b/test/e2e/create_staticmac_test.go
@@ -1,10 +1,9 @@
-// +build !remote
-
package integration
import (
"os"
+ "github.com/containers/libpod/v2/pkg/rootless"
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -18,7 +17,6 @@ var _ = Describe("Podman run with --mac-address flag", func() {
)
BeforeEach(func() {
- SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
@@ -40,7 +38,11 @@ var _ = Describe("Podman run with --mac-address flag", func() {
It("Podman run --mac-address", func() {
result := podmanTest.Podman([]string{"run", "--mac-address", "92:d0:c6:0a:29:34", ALPINE, "ip", "addr"})
result.WaitWithDefaultTimeout()
- Expect(result.ExitCode()).To(Equal(0))
- Expect(result.OutputToString()).To(ContainSubstring("92:d0:c6:0a:29:34"))
+ if rootless.IsRootless() {
+ Expect(result.ExitCode()).To(Equal(125))
+ } else {
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(result.OutputToString()).To(ContainSubstring("92:d0:c6:0a:29:34"))
+ }
})
})
diff --git a/test/e2e/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go
index 7a067e861..13f4e1aef 100644
--- a/test/e2e/libpod_suite_remote_test.go
+++ b/test/e2e/libpod_suite_remote_test.go
@@ -28,11 +28,6 @@ func SkipIfRootless() {
ginkgo.Skip("This function is not enabled for rootless podman")
}
}
-func SkipIfRootlessV2() {
- if os.Geteuid() != 0 {
- ginkgo.Skip("This function is not enabled for v2 rootless podman")
- }
-}
// Podman is the exec call to podman on the filesystem
func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration {
diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go
index 105fcf6a8..29ad01363 100644
--- a/test/e2e/libpod_suite_test.go
+++ b/test/e2e/libpod_suite_test.go
@@ -41,12 +41,6 @@ func SkipIfRootless() {
}
}
-func SkipIfRootlessV2() {
- if os.Geteuid() != 0 {
- Skip("This function is not enabled for v2 rootless podman")
- }
-}
-
// Podman is the exec call to podman on the filesystem
func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration {
podmanSession := p.PodmanBase(args, false, false)
diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go
index 57737ad59..016eaaa99 100644
--- a/test/e2e/pod_create_test.go
+++ b/test/e2e/pod_create_test.go
@@ -7,6 +7,7 @@ import (
"path/filepath"
"strings"
+ "github.com/containers/libpod/v2/pkg/rootless"
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -238,17 +239,20 @@ var _ = Describe("Podman pod create", func() {
})
It("podman create pod with IP address", func() {
- SkipIfRootless()
name := "test"
ip := GetRandomIPAddress()
podCreate := podmanTest.Podman([]string{"pod", "create", "--ip", ip, "--name", name})
podCreate.WaitWithDefaultTimeout()
- Expect(podCreate.ExitCode()).To(Equal(0))
-
- podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"})
- podResolvConf.WaitWithDefaultTimeout()
- Expect(podResolvConf.ExitCode()).To(Equal(0))
- Expect(strings.Contains(podResolvConf.OutputToString(), ip)).To(BeTrue())
+ // Rootless should error
+ if rootless.IsRootless() {
+ Expect(podCreate.ExitCode()).To(Equal(125))
+ } else {
+ Expect(podCreate.ExitCode()).To(Equal(0))
+ podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"})
+ podResolvConf.WaitWithDefaultTimeout()
+ Expect(podResolvConf.ExitCode()).To(Equal(0))
+ Expect(strings.Contains(podResolvConf.OutputToString(), ip)).To(BeTrue())
+ }
})
It("podman create pod with IP address and no infra should fail", func() {
@@ -262,17 +266,20 @@ var _ = Describe("Podman pod create", func() {
It("podman create pod with MAC address", func() {
SkipIfRemote()
- SkipIfRootless()
name := "test"
mac := "92:d0:c6:0a:29:35"
podCreate := podmanTest.Podman([]string{"pod", "create", "--mac-address", mac, "--name", name})
podCreate.WaitWithDefaultTimeout()
- Expect(podCreate.ExitCode()).To(Equal(0))
-
- podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"})
- podResolvConf.WaitWithDefaultTimeout()
- Expect(podResolvConf.ExitCode()).To(Equal(0))
- Expect(strings.Contains(podResolvConf.OutputToString(), mac)).To(BeTrue())
+ // Rootless should error
+ if rootless.IsRootless() {
+ Expect(podCreate.ExitCode()).To(Equal(125))
+ } else {
+ Expect(podCreate.ExitCode()).To(Equal(0))
+ podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"})
+ podResolvConf.WaitWithDefaultTimeout()
+ Expect(podResolvConf.ExitCode()).To(Equal(0))
+ Expect(strings.Contains(podResolvConf.OutputToString(), mac)).To(BeTrue())
+ }
})
It("podman create pod with MAC address and no infra should fail", func() {
diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go
index 5e3634435..16bf1c4c9 100644
--- a/test/e2e/pod_inspect_test.go
+++ b/test/e2e/pod_inspect_test.go
@@ -1,8 +1,11 @@
package integration
import (
+ "encoding/json"
"os"
+ "github.com/containers/libpod/v2/libpod/define"
+
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -79,4 +82,22 @@ var _ = Describe("Podman pod inspect", func() {
index := len(inspectCreateCommand) - len(createCommand)
Expect(inspectCreateCommand[index:]).To(Equal(createCommand))
})
+
+ It("podman pod inspect outputs port bindings", func() {
+ podName := "testPod"
+ create := podmanTest.Podman([]string{"pod", "create", "--name", podName, "-p", "8080:80"})
+ create.WaitWithDefaultTimeout()
+ Expect(create.ExitCode()).To(Equal(0))
+
+ inspectOut := podmanTest.Podman([]string{"pod", "inspect", podName})
+ inspectOut.WaitWithDefaultTimeout()
+ Expect(inspectOut.ExitCode()).To(Equal(0))
+
+ inspectJSON := new(define.InspectPodData)
+ err := json.Unmarshal(inspectOut.Out.Contents(), inspectJSON)
+ Expect(err).To(BeNil())
+ Expect(inspectJSON.InfraConfig).To(Not(BeNil()))
+ Expect(len(inspectJSON.InfraConfig.PortBindings["80/tcp"])).To(Equal(1))
+ Expect(inspectJSON.InfraConfig.PortBindings["80/tcp"][0].HostPort).To(Equal("8080"))
+ })
})
diff --git a/test/e2e/run_entrypoint_test.go b/test/e2e/run_entrypoint_test.go
index 76e021552..741019770 100644
--- a/test/e2e/run_entrypoint_test.go
+++ b/test/e2e/run_entrypoint_test.go
@@ -101,6 +101,11 @@ ENTRYPOINT ["grep", "Alpine", "/etc/os-release"]
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.LineInOuputStartsWith("Linux")).To(BeTrue())
+
+ session = podmanTest.Podman([]string{"run", "--entrypoint", "", "foobar.com/entrypoint:latest", "uname"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.LineInOuputStartsWith("Linux")).To(BeTrue())
})
It("podman run user entrypoint with command overrides image entrypoint and image cmd", func() {
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index 5a463d46f..467d0c5ef 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -88,6 +88,20 @@ var _ = Describe("Podman run networking", func() {
Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
})
+ It("podman run -p 8080:80/TCP", func() {
+ name := "testctr"
+ // "TCP" in upper characters
+ session := podmanTest.Podman([]string{"create", "-t", "-p", "8080:80/TCP", "--name", name, ALPINE, "/bin/sh"})
+ session.WaitWithDefaultTimeout()
+ inspectOut := podmanTest.InspectContainer(name)
+ Expect(len(inspectOut)).To(Equal(1))
+ Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1))
+ // "tcp" in lower characters
+ Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("8080"))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
+ })
+
It("podman run -p 80/udp", func() {
name := "testctr"
session := podmanTest.Podman([]string{"create", "-t", "-p", "80/udp", "--name", name, ALPINE, "/bin/sh"})
diff --git a/test/e2e/systemd_test.go b/test/e2e/systemd_test.go
index a1cdff70e..7b9be2275 100644
--- a/test/e2e/systemd_test.go
+++ b/test/e2e/systemd_test.go
@@ -112,5 +112,40 @@ WantedBy=multi-user.target
systemctl.WaitWithDefaultTimeout()
Expect(systemctl.ExitCode()).To(Equal(0))
Expect(strings.Contains(systemctl.OutputToString(), "State:")).To(BeTrue())
+
+ result := podmanTest.Podman([]string{"inspect", ctrName})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ conData := result.InspectContainerToJSON()
+ Expect(len(conData)).To(Equal(1))
+ Expect(conData[0].Config.SystemdMode).To(BeTrue())
+ })
+
+ It("podman create container with systemd entrypoint triggers systemd mode", func() {
+ ctrName := "testCtr"
+ run := podmanTest.Podman([]string{"create", "--name", ctrName, "--entrypoint", "/sbin/init", ubi_init})
+ run.WaitWithDefaultTimeout()
+ Expect(run.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"inspect", ctrName})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ conData := result.InspectContainerToJSON()
+ Expect(len(conData)).To(Equal(1))
+ Expect(conData[0].Config.SystemdMode).To(BeTrue())
+ })
+
+ It("podman create container with systemd=always triggers systemd mode", func() {
+ ctrName := "testCtr"
+ run := podmanTest.Podman([]string{"create", "--name", ctrName, "--systemd", "always", ALPINE})
+ run.WaitWithDefaultTimeout()
+ Expect(run.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"inspect", ctrName})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ conData := result.InspectContainerToJSON()
+ Expect(len(conData)).To(Equal(1))
+ Expect(conData[0].Config.SystemdMode).To(BeTrue())
})
})
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index bc6347012..13fec20ad 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -242,4 +242,46 @@ echo $rand | 0 | $rand
run_podman rmi myi
}
+# #6735 : complex interactions with multiple user namespaces
+# The initial report has to do with bind mounts, but that particular
+# symptom only manifests on a fedora container image -- we have no
+# reproducer on alpine. Checking directory ownership is good enough.
+@test "podman run : user namespace preserved root ownership" {
+ for priv in "" "--privileged"; do
+ for user in "--user=0" "--user=100"; do
+ for keepid in "" "--userns=keep-id"; do
+ opts="$priv $user $keepid"
+
+ for dir in /etc /usr;do
+ run_podman run --rm $opts $IMAGE stat -c '%u:%g:%n' $dir
+ remove_same_dev_warning # grumble
+ is "$output" "0:0:$dir" "run $opts ($dir)"
+ done
+ done
+ done
+ done
+}
+
+# #6829 : add username to /etc/passwd inside container if --userns=keep-id
+@test "podman run : add username to /etc/passwd if --userns=keep-id" {
+ # Default: always run as root
+ run_podman run --rm $IMAGE id -un
+ is "$output" "root" "id -un on regular container"
+
+ # This would always work on root, but is new behavior on rootless: #6829
+ # adds a user entry to /etc/passwd
+ run_podman run --rm --userns=keep-id $IMAGE id -un
+ is "$output" "$(id -un)" "username on container with keep-id"
+
+ # --privileged should make no difference
+ run_podman run --rm --privileged --userns=keep-id $IMAGE id -un
+ remove_same_dev_warning # grumble
+ is "$output" "$(id -un)" "username on container with keep-id"
+
+ # ...but explicitly setting --user should override keep-id
+ run_podman run --rm --privileged --userns=keep-id --user=0 $IMAGE id -un
+ remove_same_dev_warning # grumble
+ is "$output" "root" "--user=0 overrides keep-id"
+}
+
# vim: filetype=sh
diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats
index f8c7f2766..945bcfa2d 100644
--- a/test/system/075-exec.bats
+++ b/test/system/075-exec.bats
@@ -6,8 +6,6 @@
load helpers
@test "podman exec - basic test" {
- skip_if_remote
-
rand_filename=$(random_string 20)
rand_content=$(random_string 50)
@@ -80,4 +78,24 @@ load helpers
run_podman rm $cid
}
+# #6829 : add username to /etc/passwd inside container if --userns=keep-id
+# #6593 : doesn't actually work with podman exec
+@test "podman exec - with keep-id" {
+ skip "Please enable once #6593 is fixed"
+
+ run_podman run -d --userns=keep-id $IMAGE sh -c \
+ "echo READY;while [ ! -f /stop ]; do sleep 1; done"
+ cid="$output"
+ wait_for_ready $cid
+
+ run_podman exec $cid id -un
+ is "$output" "$(id -un)" "container is running as current user"
+
+ # Until #6593 gets fixed, this just hangs. The server process barfs with:
+ # unable to find user <username>: no matching entries in passwd file
+ run_podman exec --user=$(id -un) $cid touch /stop
+ run_podman wait $cid
+ run_podman rm $cid
+}
+
# vim: filetype=sh
diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats
index 0e9d9132e..0ad555305 100644
--- a/test/system/200-pod.bats
+++ b/test/system/200-pod.bats
@@ -165,9 +165,13 @@ function random_ip() {
# Create a pod with all the desired options
# FIXME: --ip=$ip fails:
# Error adding network: failed to allocate all requested IPs
+ local mac_option="--mac-address=$mac"
+ if is_rootless; then
+ mac_option=
+ fi
run_podman pod create --name=mypod \
--pod-id-file=$pod_id_file \
- --mac-address=$mac \
+ $mac_option \
--hostname=$hostname \
--add-host "$add_host_n:$add_host_ip" \
--dns "$dns_server" \
@@ -181,7 +185,7 @@ function random_ip() {
is "$(<$pod_id_file)" "$pod_id" "contents of pod-id-file"
# Check each of the options
- if ! is_rootless; then
+ if [ -n "$mac_option" ]; then
run_podman run --rm --pod mypod $IMAGE ip link show
# 'ip' outputs hex in lower-case, ${expr,,} converts UC to lc
is "$output" ".* link/ether ${mac,,} " "requested MAC address was set"
diff --git a/test/system/400-unprivileged-access.bats b/test/system/400-unprivileged-access.bats
index 98f8b8211..1384c0ab8 100644
--- a/test/system/400-unprivileged-access.bats
+++ b/test/system/400-unprivileged-access.bats
@@ -1,6 +1,7 @@
#!/usr/bin/env bats -*- bats -*-
#
# Tests #2730 - regular users are not able to read/write container storage
+# Tests #6957 - /sys/dev (et al) are masked from unprivileged containers
#
load helpers
@@ -97,4 +98,68 @@ EOF
run_podman rm c_uidmap c_uidmap_v
}
+
+# #6957 - mask out /proc/acpi, /sys/dev, and other sensitive system files
+@test "sensitive mount points are masked without --privileged" {
+ # FIXME: this should match the list in pkg/specgen/generate/config_linux.go
+ local -a mps=(
+ /proc/acpi
+ /proc/kcore
+ /proc/keys
+ /proc/latency_stats
+ /proc/timer_list
+ /proc/timer_stats
+ /proc/sched_debug
+ /proc/scsi
+ /sys/firmware
+ /sys/fs/selinux
+ /sys/dev
+ )
+
+ # Some of the above may not exist on our host. Find only the ones that do.
+ local -a subset=()
+ for mp in ${mps[@]}; do
+ if [ -e $mp ]; then
+ subset+=($mp)
+ fi
+ done
+
+ # Run 'stat' on all the files, plus /dev/null. Get path, file type,
+ # number of links, major, and minor (see below for why). Do it all
+ # in one go, to avoid multiple podman-runs
+ run_podman run --rm $IMAGE stat -c'%n:%F:%h:%T:%t' /dev/null ${subset[@]}
+ local devnull=
+ for result in "${lines[@]}"; do
+ # e.g. /proc/acpi:character special file:1:3:1
+ local IFS=:
+ read path type nlinks major minor <<<"$result"
+
+ if [[ $path = "/dev/null" ]]; then
+ # /dev/null is our reference point: masked *files* (not directories)
+ # will be created as /dev/null clones.
+ # This depends on 'stat' returning results in argv order,
+ # so /dev/null is first, so we have a reference for others.
+ # If that ever breaks, this test will have to be done in two passes.
+ devnull="$major:$minor"
+ elif [[ $type = "character special file" ]]; then
+ # Container file is a character device: it must match /dev/null
+ is "$major:$minor" "$devnull" "$path: major/minor matches /dev/null"
+ elif [[ $type = "directory" ]]; then
+ # Directories: must be empty (only two links).
+ # FIXME: this is a horrible almost-worthless test! It does not
+ # actually check for files in the directory (expect: zero),
+ # merely for the nonexistence of any subdirectories! It relies
+ # on the observed (by Ed) fact that all the masked directories
+ # contain further subdirectories on the host. If there's ever
+ # a new masked directory that contains only files, this test
+ # will silently pass without any indication of error.
+ # If you can think of a better way to do this check,
+ # please feel free to fix it.
+ is "$nlinks" "2" "$path: directory link count"
+ else
+ die "$path: Unknown file type '$type'"
+ fi
+ done
+}
+
# vim: filetype=sh
diff --git a/test/system/410-selinux.bats b/test/system/410-selinux.bats
index 1769730f0..497e29b3e 100644
--- a/test/system/410-selinux.bats
+++ b/test/system/410-selinux.bats
@@ -19,15 +19,8 @@ function check_label() {
# FIXME: on some CI systems, 'run --privileged' emits a spurious
# warning line about dup devices. Ignore it.
+ remove_same_dev_warning
local context="$output"
- if [ ${#lines[@]} -gt 1 ]; then
- if expr "${lines[0]}" : "WARNING: .* type, major" >/dev/null; then
- echo "# ${lines[0]} [ignored]" >&3
- context="${lines[1]}"
- else
- die "FAILED: too much output, expected one single line"
- fi
- fi
is "$context" ".*_u:system_r:.*" "SELinux role should always be system_r"
diff --git a/test/system/helpers.bash b/test/system/helpers.bash
index 5301644d6..4239ef876 100644
--- a/test/system/helpers.bash
+++ b/test/system/helpers.bash
@@ -392,5 +392,37 @@ function find_exec_pid_files() {
find $storage_path -type f -iname 'exec_pid_*'
fi
}
+
+
+#############################
+# remove_same_dev_warning # Filter out useless warning from output
+#############################
+#
+# On some CI systems, 'podman run --privileged' emits a useless warning:
+#
+# WARNING: The same type, major and minor should not be used for multiple devices.
+#
+# This obviously screws us up when we look at output results.
+#
+# This function removes the warning from $output and $lines
+#
+function remove_same_dev_warning() {
+ # No input arguments. We operate in-place on $output and $lines
+
+ local i=0
+ local -a new_lines=()
+ while [[ $i -lt ${#lines[@]} ]]; do
+ if expr "${lines[$i]}" : 'WARNING: .* same type, major.* multiple' >/dev/null; then
+ :
+ else
+ new_lines+=("${lines[$i]}")
+ fi
+ i=$(( i + 1 ))
+ done
+
+ lines=("${new_lines[@]}")
+ output=$(printf '%s\n' "${lines[@]}")
+}
+
# END miscellaneous tools
###############################################################################
diff --git a/test/system/helpers.t b/test/system/helpers.t
index 7b4e48a84..a022f11c4 100755
--- a/test/system/helpers.t
+++ b/test/system/helpers.t
@@ -23,7 +23,8 @@ rc=0
function check_result {
testnum=$(expr $testnum + 1)
if [ "$1" = "$2" ]; then
- echo "ok $testnum $3 = $1"
+ # Multi-level echo flattens newlines, makes success messages readable
+ echo $(echo "ok $testnum $3 = $1")
else
echo "not ok $testnum $3"
echo "# expected: $2"
@@ -141,5 +142,72 @@ done < <(parse_table "$table")
# END dprint
###############################################################################
+# BEGIN remove_same_dev_warning
+
+# Test-helper function: runs remove_same_dev_warning, compares resulting
+# value of $lines and $output to expected values given on command line
+function check_same_dev() {
+ local testname="$1"; shift
+ local -a expect_lines=("$@")
+ local nl="
+"
+
+ remove_same_dev_warning
+
+ # After processing, check the expected number of lines
+ check_result "${#lines[@]}" "${#@}" "$testname: expected # of lines"
+
+ # ...and each expected line
+ local expect_output=""
+ local i=0
+ while [ $i -lt ${#expect_lines[@]} ]; do
+ check_result "${lines[$i]}" "${expect_lines[$i]}" "$testname: line $i"
+ expect_output+="${expect_lines[$i]}$nl"
+ i=$(( i + 1 ))
+ done
+
+ # ...and the possibly-multi-line $output
+ check_result "$output" "${expect_output%%$nl}" "$testname: output"
+}
+
+# Simplest case: nothing removed.
+declare -a lines=("a b c" "d" "e f")
+check_same_dev "abc" "a b c" "d" "e f"
+
+# Confirm that the warning message is removed from the beginning
+declare -a lines=(
+ "WARNING: The same type, major and minor should not be used for multiple devices."
+ "a"
+ "b"
+ "c"
+)
+check_same_dev "warning is removed" a b c
+
+# ...and from the middle (we do not expect to see this)
+declare -a lines=(
+ "WARNING: The same type, major and minor should not be used for multiple devices."
+ "a"
+ "b"
+ "WARNING: The same type, major and minor should not be used for multiple devices."
+ "c"
+)
+check_same_dev "multiple warnings removed" a b c
+
+# Corner case: two lines of output, only one of which we care about
+declare -a lines=(
+ "WARNING: The same type, major and minor should not be used for multiple devices."
+ "this is the only line we care about"
+)
+check_same_dev "one-line output" "this is the only line we care about"
+
+# Corner case: one line of output, but we expect zero.
+declare -a lines=(
+ "WARNING: The same type, major and minor should not be used for multiple devices."
+)
+check_same_dev "zero-line output"
+
+
+# END remove_same_dev_warning
+###############################################################################
exit $rc