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