github.com/containers/podman/v5@v5.1.0-rc1/test/system/400-unprivileged-access.bats (about) 1 #!/usr/bin/env bats -*- bats -*- 2 # 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 5 # 6 7 load helpers 8 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" 12 skip_if_remote 13 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 16 17 run_podman run --name c_mount $IMAGE \ 18 sh -c "echo hi > /myfile;mkdir -p /mydir/mysubdir; chmod 777 /myfile /mydir /mydir/mysubdir" 19 20 run_podman mount c_mount 21 mount_path=$output 22 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" 27 #!/usr/bin/env bash 28 29 path="$1" 30 31 die() { 32 echo "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv" >&2 33 echo "#| FAIL: $*" >&2 34 echo "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" >&2 35 36 # Show permissions of directories from here on up 37 while expr "$path" : "/var/lib/containers" >/dev/null; do 38 echo "#| $(ls -ld $path)" 39 path=$(dirname $path) 40 done 41 42 exit 1 43 } 44 45 parent=$(dirname "$path") 46 if chmod +w $parent; then 47 die "Able to chmod $parent" 48 fi 49 if chmod +w "$path"; then 50 die "Able to chmod $path" 51 fi 52 53 EOF 54 55 # Under overlay, and presumably any future storage drivers, we 56 # should never be able to read or write $path. 57 # 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" 65 fi 66 if echo hi >"$path"/test; then 67 die "Able to write to file under $path" 68 fi 69 else 70 # Plain file 71 if cat "$path" >/dev/null; then 72 die "Able to read $path" 73 fi 74 if echo hi >"$path"; then 75 die "Able to write to $path" 76 fi 77 fi 78 79 EOF 80 fi 81 echo "exit 0" >>$test_script 82 chmod 755 $PODMAN_TMPDIR $test_script 83 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" 87 GRAPH_ROOT="$output" 88 run_podman info --format '{{.Store.RunRoot}}' 89 is "$output" ".*/run/containers/storage" "RunRoot in expected place" 90 RUN_ROOT="$output" 91 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 94 # access each path. 95 find $GRAPH_ROOT $RUN_ROOT \! -type l -perm -o+w -print | while read i; do 96 dprint " o+w: $i" 97 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" 101 done 102 103 # Done. Clean up. 104 rm -f $test_script 105 106 run_podman umount c_mount 107 run_podman rm c_mount 108 109 run_podman rm c_uidmap c_uidmap_v 110 run_podman volume rm foo 111 } 112 113 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 117 local -a mps=( 118 /proc/acpi 119 /proc/kcore 120 /proc/keys 121 /proc/latency_stats 122 /proc/timer_list 123 /proc/timer_stats 124 /proc/sched_debug 125 /proc/scsi 126 /sys/firmware 127 /sys/fs/selinux 128 /sys/dev/block 129 ) 130 131 # Some of the above may not exist on our host. Find only the ones that do. 132 local -a subset=() 133 for mp in "${mps[@]}"; do 134 if [ -e $mp ]; then 135 subset+=($mp) 136 fi 137 done 138 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" 144 145 local devnull= 146 for result in "${lines[@]}"; do 147 # e.g. /proc/acpi:character special file:1:3:1 148 local IFS=: 149 read path type nlinks major minor <<<"$result" 150 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. 177 : 178 else 179 die "$path: Unknown file type '$type'" 180 fi 181 done 182 } 183 184 # vim: filetype=sh