github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/tests/integration/userns.bats (about) 1 #!/usr/bin/env bats 2 3 load helpers 4 5 function setup() { 6 setup_busybox 7 8 # Prepare source folders for bind mount 9 mkdir -p source-{accessible,inaccessible-1,inaccessible-2}/dir 10 touch source-{accessible,inaccessible-1,inaccessible-2}/dir/foo.txt 11 12 # Permissions only to the owner, it is inaccessible to group/others 13 chmod 700 source-inaccessible-{1,2} 14 15 mkdir -p rootfs/tmp/mount-{1,2} 16 17 to_umount_list="$(mktemp "$BATS_RUN_TMPDIR/userns-mounts.XXXXXX")" 18 if [ $EUID -eq 0 ]; then 19 update_config ' .linux.namespaces += [{"type": "user"}] 20 | .linux.uidMappings += [{"hostID": 100000, "containerID": 0, "size": 65534}] 21 | .linux.gidMappings += [{"hostID": 200000, "containerID": 0, "size": 65534}] ' 22 remap_rootfs 23 fi 24 } 25 26 function teardown() { 27 teardown_bundle 28 29 if [ -v to_umount_list ]; then 30 while read -r mount_path; do 31 umount -l "$mount_path" || : 32 rm -f "$mount_path" 33 done <"$to_umount_list" 34 rm -f "$to_umount_list" 35 unset to_umount_list 36 fi 37 } 38 39 @test "userns with simple mount" { 40 update_config ' .process.args += ["-c", "stat /tmp/mount-1/foo.txt"] 41 | .mounts += [{"source": "source-accessible/dir", "destination": "/tmp/mount-1", "options": ["bind"]}] ' 42 43 runc run test_busybox 44 [ "$status" -eq 0 ] 45 } 46 47 # We had bugs where 1 mount worked but not 2+, test with 2 as it is a more 48 # general case. 49 @test "userns with 2 inaccessible mounts" { 50 update_config ' .process.args += ["-c", "stat /tmp/mount-1/foo.txt /tmp/mount-2/foo.txt"] 51 | .mounts += [ { "source": "source-inaccessible-1/dir", "destination": "/tmp/mount-1", "options": ["bind"] }, 52 { "source": "source-inaccessible-2/dir", "destination": "/tmp/mount-2", "options": ["bind"] } 53 ]' 54 55 # When not running rootless, this should work: while 56 # "source-inaccessible-1" can't be read by the uid in the userns, the fd 57 # is opened before changing to the userns and sent over via SCM_RIGHTs 58 # (with env var _LIBCONTAINER_MOUNT_FDS). Idem for 59 # source-inaccessible-2. 60 # On rootless, the owner is the same so it is accessible. 61 runc run test_busybox 62 [ "$status" -eq 0 ] 63 } 64 65 # exec + bindmounts + user ns is a special case in the code. Test that it works. 66 @test "userns with inaccessible mount + exec" { 67 update_config ' .mounts += [ { "source": "source-inaccessible-1/dir", "destination": "/tmp/mount-1", "options": ["bind"] }, 68 { "source": "source-inaccessible-2/dir", "destination": "/tmp/mount-2", "options": ["bind"] } 69 ]' 70 71 runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox 72 [ "$status" -eq 0 ] 73 74 runc exec test_busybox stat /tmp/mount-1/foo.txt /tmp/mount-2/foo.txt 75 [ "$status" -eq 0 ] 76 } 77 78 # Issue fixed by https://github.com/opencontainers/runc/pull/3510. 79 @test "userns with bind mount before a cgroupfs mount" { 80 # This can only be reproduced on cgroup v1 (and no cgroupns) due to the 81 # way it is mounted in such case (a bunch of of bind mounts). 82 requires cgroups_v1 83 84 # Add a bind mount right before the /sys/fs/cgroup mount, 85 # and make sure cgroupns is not enabled. 86 update_config ' .mounts |= map(if .destination == "/sys/fs/cgroup" then ({"source": "source-accessible/dir", "destination": "/tmp/mount-1", "options": ["bind"]}, .) else . end) 87 | .linux.namespaces -= [{"type": "cgroup"}]' 88 89 runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox 90 [ "$status" -eq 0 ] 91 92 # Make sure this is real cgroupfs. 93 runc exec test_busybox cat /sys/fs/cgroup/{pids,memory}/tasks 94 [ "$status" -eq 0 ] 95 } 96 97 @test "userns join other container userns" { 98 # Create a detached container with the id-mapping we want. 99 update_config '.process.args = ["sleep", "infinity"]' 100 runc run -d --console-socket "$CONSOLE_SOCKET" target_userns 101 [ "$status" -eq 0 ] 102 103 # Configure our container to attach to the first container's userns. 104 target_pid="$(__runc state target_userns | jq .pid)" 105 update_config '.linux.namespaces |= map(if .type == "user" then (.path = "/proc/'"$target_pid"'/ns/" + .type) else . end) 106 | del(.linux.uidMappings) 107 | del(.linux.gidMappings)' 108 runc run -d --console-socket "$CONSOLE_SOCKET" in_userns 109 [ "$status" -eq 0 ] 110 111 runc exec in_userns cat /proc/self/uid_map 112 [ "$status" -eq 0 ] 113 if [ $EUID -eq 0 ]; then 114 grep -E '^\s+0\s+100000\s+65534$' <<<"$output" 115 else 116 grep -E '^\s+0\s+'$EUID'\s+1$' <<<"$output" 117 fi 118 119 runc exec in_userns cat /proc/self/gid_map 120 [ "$status" -eq 0 ] 121 if [ $EUID -eq 0 ]; then 122 grep -E '^\s+0\s+200000\s+65534$' <<<"$output" 123 else 124 grep -E '^\s+0\s+'$EUID'\s+1$' <<<"$output" 125 fi 126 } 127 128 @test "userns join other container userns [bind-mounted nsfd]" { 129 requires root 130 131 # Create a detached container with the id-mapping we want. 132 update_config '.process.args = ["sleep", "infinity"]' 133 runc run -d --console-socket "$CONSOLE_SOCKET" target_userns 134 [ "$status" -eq 0 ] 135 136 # Bind-mount the first containers userns nsfd to a different path, to 137 # exercise the non-fast-path (where runc has to join the userns to get the 138 # mappings). 139 target_pid="$(__runc state target_userns | jq .pid)" 140 userns_path=$(mktemp "$BATS_RUN_TMPDIR/userns.XXXXXX") 141 mount --bind "/proc/$target_pid/ns/user" "$userns_path" 142 echo "$userns_path" >>"$to_umount_list" 143 144 # Configure our container to attach to the first container's userns. 145 update_config '.linux.namespaces |= map(if .type == "user" then (.path = "'"$userns_path"'") else . end) 146 | del(.linux.uidMappings) 147 | del(.linux.gidMappings)' 148 runc run -d --console-socket "$CONSOLE_SOCKET" in_userns 149 [ "$status" -eq 0 ] 150 151 runc exec in_userns cat /proc/self/uid_map 152 [ "$status" -eq 0 ] 153 if [ $EUID -eq 0 ]; then 154 grep -E '^\s+0\s+100000\s+65534$' <<<"$output" 155 else 156 grep -E '^\s+0\s+'$EUID'\s+1$' <<<"$output" 157 fi 158 159 runc exec in_userns cat /proc/self/gid_map 160 [ "$status" -eq 0 ] 161 if [ $EUID -eq 0 ]; then 162 grep -E '^\s+0\s+200000\s+65534$' <<<"$output" 163 else 164 grep -E '^\s+0\s+'$EUID'\s+1$' <<<"$output" 165 fi 166 }