1
#!/usr/bin/env bats -*- bats -*-
3
# Tests #2730 - regular users are not able to read/write container storage
4
# Tests #6957 - /sys/dev (et al) are masked from unprivileged containers
9
@test "podman container storage is not accessible by unprivileged users" {
10
skip_if_cgroupsv1 "run --uidmap fails on cgroups v1 (issue 15025, wontfix)"
11
skip_if_rootless "test meaningless without suid"
14
run_podman run --name c_uidmap --uidmap 0:10000:10000 $IMAGE true
15
run_podman run --name c_uidmap_v --uidmap 0:10000:10000 -v foo:/foo $IMAGE true
17
run_podman run --name c_mount $IMAGE \
18
sh -c "echo hi > /myfile;mkdir -p /mydir/mysubdir; chmod 777 /myfile /mydir /mydir/mysubdir"
20
run_podman mount c_mount
23
# Do all the work from within a test script. Since we'll be invoking it
24
# as a user, the parent directory must be world-readable.
25
test_script=$PODMAN_TMPDIR/fail-if-writable
26
cat >$test_script <<"EOF"
32
echo "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv" >&2
33
echo "#| FAIL: $*" >&2
34
echo "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" >&2
36
# Show permissions of directories from here on up
37
while expr "$path" : "/var/lib/containers" >/dev/null; do
38
echo "#| $(ls -ld $path)"
45
parent=$(dirname "$path")
46
if chmod +w $parent; then
47
die "Able to chmod $parent"
49
if chmod +w "$path"; then
50
die "Able to chmod $path"
55
# Under overlay, and presumably any future storage drivers, we
56
# should never be able to read or write $path.
58
# Under VFS, though, if podman has *ever* been run with --uidmap,
59
# all images become world-accessible. So don't bother checking.
60
if [[ $(podman_storage_driver) != "vfs" ]]; then
61
cat >>$test_script <<EOF
62
if [ -d "$path" ]; then
63
if ls "$path" >/dev/null; then
64
die "Able to run 'ls $path' without error"
66
if echo hi >"$path"/test; then
67
die "Able to write to file under $path"
71
if cat "$path" >/dev/null; then
72
die "Able to read $path"
74
if echo hi >"$path"; then
75
die "Able to write to $path"
81
echo "exit 0" >>$test_script
82
chmod 755 $PODMAN_TMPDIR $test_script
84
# get podman image and container storage directories
85
run_podman info --format '{{.Store.GraphRoot}}'
86
is "$output" "/var/lib/containers/storage" "GraphRoot in expected place"
88
run_podman info --format '{{.Store.RunRoot}}'
89
is "$output" ".*/run/containers/storage" "RunRoot in expected place"
92
# The main test: find all world-writable files or directories underneath
93
# container storage, run the test script as a nonroot user, and try to
95
find $GRAPH_ROOT $RUN_ROOT \! -type l -perm -o+w -print | while read i; do
98
# use chroot because su fails if uid/gid don't exist or have no shell
99
# For development: test all this by removing the "--userspec x:x"
100
chroot --userspec 1000:1000 / $test_script "$i"
106
run_podman umount c_mount
107
run_podman rm c_mount
109
run_podman rm c_uidmap c_uidmap_v
110
run_podman volume rm foo
114
# #6957 - mask out /proc/acpi, /sys/dev, and other sensitive system files
115
@test "sensitive mount points are masked without --privileged" {
116
# FIXME: this should match the list in pkg/specgen/generate/config_linux.go
131
# Some of the above may not exist on our host. Find only the ones that do.
133
for mp in "${mps[@]}"; do
139
# Run 'stat' on all the files, plus /dev/null. Get path, file type,
140
# number of links, major, and minor (see below for why). Do it all
141
# in one go, to avoid multiple podman-runs
142
run_podman '?' run --rm $IMAGE stat -c'%n:%F:%h:%T:%t' /dev/null "${subset[@]}"
143
assert $status -le 1 "stat exit status: expected 0 or 1"
146
for result in "${lines[@]}"; do
147
# e.g. /proc/acpi:character special file:1:3:1
149
read path type nlinks major minor <<<"$result"
151
if [[ $path = "/dev/null" ]]; then
152
# /dev/null is our reference point: masked *files* (not directories)
153
# will be created as /dev/null clones.
154
# This depends on 'stat' returning results in argv order,
155
# so /dev/null is first, so we have a reference for others.
156
# If that ever breaks, this test will have to be done in two passes.
157
devnull="$major:$minor"
158
elif [[ $type = "character special file" ]]; then
159
# Container file is a character device: it must match /dev/null
160
is "$major:$minor" "$devnull" "$path: major/minor matches /dev/null"
161
elif [[ $type = "directory" ]]; then
162
# Directories: must be empty (only two links).
163
# FIXME: this is a horrible almost-worthless test! It does not
164
# actually check for files in the directory (expect: zero),
165
# merely for the nonexistence of any subdirectories! It relies
166
# on the observed (by Ed) fact that all the masked directories
167
# contain further subdirectories on the host. If there's ever
168
# a new masked directory that contains only files, this test
169
# will silently pass without any indication of error.
170
# If you can think of a better way to do this check,
171
# please feel free to fix it.
172
is "$nlinks" "2" "$path: directory link count"
173
elif [[ $result =~ stat:.*No.such.file.or.directory ]]; then
174
# No matter what the path is, this is OK. It has to do with #8949
175
# and RHEL8 and rootless and cgroups v1. Bottom line, what we care
176
# about is that the path not be available inside the container.
179
die "$path: Unknown file type '$type'"