github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/tests/integration/run.bats (about) 1 #!/usr/bin/env bats 2 3 load helpers 4 5 function setup() { 6 setup_busybox 7 update_config '.process.args = ["/bin/echo", "Hello World"]' 8 } 9 10 function teardown() { 11 teardown_bundle 12 } 13 14 @test "runc run" { 15 runc run test_hello 16 [ "$status" -eq 0 ] 17 18 runc state test_hello 19 [ "$status" -ne 0 ] 20 } 21 22 @test "runc run --keep" { 23 runc run --keep test_run_keep 24 [ "$status" -eq 0 ] 25 26 testcontainer test_run_keep stopped 27 28 runc state test_run_keep 29 [ "$status" -eq 0 ] 30 31 runc delete test_run_keep 32 33 runc state test_run_keep 34 [ "$status" -ne 0 ] 35 } 36 37 @test "runc run --keep (check cgroup exists)" { 38 # for systemd driver, the unit's cgroup path will be auto removed if container's all processes exited 39 requires no_systemd 40 [ $EUID -ne 0 ] && requires rootless_cgroup 41 42 set_cgroups_path 43 44 runc run --keep test_run_keep 45 [ "$status" -eq 0 ] 46 47 testcontainer test_run_keep stopped 48 49 runc state test_run_keep 50 [ "$status" -eq 0 ] 51 52 # check that cgroup exists 53 check_cgroup_value "pids.max" "max" 54 55 runc delete test_run_keep 56 57 runc state test_run_keep 58 [ "$status" -ne 0 ] 59 } 60 61 @test "runc run [hostname domainname]" { 62 update_config ' .process.args |= ["sh"] 63 | .hostname = "myhostname" 64 | .domainname= "mydomainname"' 65 66 runc run -d --console-socket "$CONSOLE_SOCKET" test_utc 67 [ "$status" -eq 0 ] 68 69 # test hostname 70 runc exec test_utc hostname 71 [ "$status" -eq 0 ] 72 [[ "${lines[0]}" == *'myhostname'* ]] 73 74 # test domainname 75 runc exec test_utc cat /proc/sys/kernel/domainname 76 [ "$status" -eq 0 ] 77 [[ "${lines[0]}" == *'mydomainname'* ]] 78 } 79 80 # https://github.com/opencontainers/runc/issues/3952 81 @test "runc run with tmpfs" { 82 requires root 83 84 chmod 'a=rwx,ug+s,+t' rootfs/tmp # set all bits 85 mode=$(stat -c %A rootfs/tmp) 86 87 # shellcheck disable=SC2016 88 update_config '.process.args = ["sh", "-c", "stat -c %A /tmp"]' 89 update_config '.mounts += [{"destination": "/tmp", "type": "tmpfs", "source": "tmpfs", "options":["noexec","nosuid","nodev","rprivate"]}]' 90 91 runc run test_tmpfs 92 [ "$status" -eq 0 ] 93 [ "${lines[0]}" = "$mode" ] 94 } 95 96 @test "runc run with tmpfs perms" { 97 # shellcheck disable=SC2016 98 update_config '.process.args = ["sh", "-c", "stat -c %a /tmp/test"]' 99 update_config '.mounts += [{"destination": "/tmp/test", "type": "tmpfs", "source": "tmpfs", "options": ["mode=0444"]}]' 100 101 # Directory is to be created by runc. 102 runc run test_tmpfs 103 [ "$status" -eq 0 ] 104 [ "${lines[0]}" = "444" ] 105 106 # Run a 2nd time with the pre-existing directory. 107 # Ref: https://github.com/opencontainers/runc/issues/3911 108 runc run test_tmpfs 109 [ "$status" -eq 0 ] 110 [ "${lines[0]}" = "444" ] 111 112 # Existing directory, custom perms, no mode on the mount, 113 # so it should use the directory's perms. 114 update_config '.mounts[-1].options = []' 115 chmod 0710 rootfs/tmp/test 116 # shellcheck disable=SC2016 117 runc run test_tmpfs 118 [ "$status" -eq 0 ] 119 [ "${lines[0]}" = "710" ] 120 121 # Add back the mode on the mount, and it should use that instead. 122 # Just for fun, use different perms than was used earlier. 123 # shellcheck disable=SC2016 124 update_config '.mounts[-1].options = ["mode=0410"]' 125 runc run test_tmpfs 126 [ "$status" -eq 0 ] 127 [ "${lines[0]}" = "410" ] 128 } 129 130 @test "RUNC_DMZ=true runc run [runc-dmz]" { 131 # centos-7 has an outdated container-selinux (<2.224.0) which means 132 # runc-dmz won't work. 133 exclude_os centos-7 134 135 RUNC_DMZ=true runc --debug run test_hello 136 [ "$status" -eq 0 ] 137 [[ "$output" = *"Hello World"* ]] 138 # We use runc-dmz if we can. 139 [[ "$output" = *"runc-dmz: using runc-dmz"* ]] 140 } 141 142 @test "RUNC_DMZ=true runc run [cap_sys_ptrace -> /proc/self/exe clone]" { 143 # centos-7 has an outdated container-selinux (<2.224.0) which means 144 # runc-dmz won't work. 145 exclude_os centos-7 146 147 # Add CAP_SYS_PTRACE to the bounding set, the minimum needed to indicate a 148 # container process _could_ get CAP_SYS_PTRACE. 149 update_config '.process.capabilities.bounding += ["CAP_SYS_PTRACE"]' 150 151 RUNC_DMZ=true runc --debug run test_hello 152 [ "$status" -eq 0 ] 153 [[ "$output" = *"Hello World"* ]] 154 if [ "$EUID" -ne 0 ] && is_kernel_gte 4.10; then 155 # For Linux 4.10 and later, rootless containers will use runc-dmz 156 # because they are running in a user namespace. See isDmzBinarySafe(). 157 [[ "$output" = *"runc-dmz: using runc-dmz"* ]] 158 else 159 # If the container has CAP_SYS_PTRACE and is not rootless, we use 160 # /proc/self/exe cloning. 161 [[ "$output" = *"runc-dmz: using /proc/self/exe clone"* ]] 162 fi 163 } 164 165 @test "runc run [/proc/self/exe clone]" { 166 runc --debug run test_hello 167 [ "$status" -eq 0 ] 168 [[ "$output" = *"Hello World"* ]] 169 [[ "$output" = *"runc-dmz: using /proc/self/exe clone"* ]] 170 } 171 172 @test "runc run [joining existing container namespaces]" { 173 requires timens 174 175 # Create a detached container with the namespaces we want. We notably want 176 # to include both userns and timens, which require config-related 177 # configuration. 178 if [ $EUID -eq 0 ]; then 179 update_config '.linux.namespaces += [{"type": "user"}] 180 | .linux.uidMappings += [{"containerID": 0, "hostID": 100000, "size": 100}] 181 | .linux.gidMappings += [{"containerID": 0, "hostID": 200000, "size": 200}]' 182 remap_rootfs 183 fi 184 update_config '.linux.namespaces += [{"type": "time"}] 185 | .linux.timeOffsets = { 186 "monotonic": { "secs": 7881, "nanosecs": 2718281 }, 187 "boottime": { "secs": 1337, "nanosecs": 3141519 } 188 }' 189 update_config '.process.args = ["sleep", "infinity"]' 190 191 runc run -d --console-socket "$CONSOLE_SOCKET" target_ctr 192 [ "$status" -eq 0 ] 193 194 # Modify our container's configuration such that it is just going to 195 # inherit all of the namespaces of the target container. 196 # 197 # NOTE: We cannot join the mount namespace of another container because of 198 # some quirks of the runtime-spec. In particular, we MUST pivot_root into 199 # root.path and root.path MUST be set in the config, so runc cannot just 200 # ignore root.path when joining namespaces (and root.path doesn't exist 201 # inside root.path, for obvious reasons). 202 # 203 # We could hack around this (create a copy of the rootfs inside the rootfs, 204 # or use a simpler mount namespace target), but those wouldn't be similar 205 # tests to the other namespace joining tests. 206 target_pid="$(__runc state target_ctr | jq .pid)" 207 update_config '.linux.namespaces |= map_values(.path = if .type == "mount" then "" else "/proc/'"$target_pid"'/ns/" + ({"network": "net", "mount": "mnt"}[.type] // .type) end)' 208 # Remove the userns and timens configuration (they cannot be changed). 209 update_config '.linux |= (del(.uidMappings) | del(.gidMappings) | del(.timeOffsets))' 210 211 runc run -d --console-socket "$CONSOLE_SOCKET" attached_ctr 212 [ "$status" -eq 0 ] 213 214 # Make sure there are two sleep processes in our container. 215 runc exec attached_ctr ps aux 216 [ "$status" -eq 0 ] 217 run -0 grep "sleep infinity" <<<"$output" 218 [ "${#lines[@]}" -eq 2 ] 219 220 # ... that the userns mappings are the same... 221 runc exec attached_ctr cat /proc/self/uid_map 222 [ "$status" -eq 0 ] 223 if [ $EUID -eq 0 ]; then 224 grep -E '^\s+0\s+100000\s+100$' <<<"$output" 225 else 226 grep -E '^\s+0\s+'$EUID'\s+1$' <<<"$output" 227 fi 228 runc exec attached_ctr cat /proc/self/gid_map 229 [ "$status" -eq 0 ] 230 if [ $EUID -eq 0 ]; then 231 grep -E '^\s+0\s+200000\s+200$' <<<"$output" 232 else 233 grep -E '^\s+0\s+'$EUID'\s+1$' <<<"$output" 234 fi 235 236 # ... as well as the timens offsets. 237 runc exec attached_ctr cat /proc/self/timens_offsets 238 grep -E '^monotonic\s+7881\s+2718281$' <<<"$output" 239 grep -E '^boottime\s+1337\s+3141519$' <<<"$output" 240 } 241 242 @test "RUNC_DMZ=true runc run [exec error]" { 243 # centos-7 has an outdated container-selinux (<2.224.0) which means 244 # runc-dmz won't work. 245 exclude_os centos-7 246 247 cat <<EOF >rootfs/run.sh 248 #!/mmnnttbb foo bar 249 sh 250 EOF 251 chmod +x rootfs/run.sh 252 update_config '.process.args = [ "/run.sh" ]' 253 RUNC_DMZ=true runc run test_hello 254 255 # Ensure that the output contains the right error message. For runc-dmz, both 256 # nolibc and libc have the same formatting string (but libc will print the 257 # errno description rather than just the number), and for runc_nodmz the error 258 # message from Go starts with the same string. 259 [ "$status" -ne 0 ] 260 [[ "$output" = *"exec /run.sh: "* ]] 261 } 262 263 @test "runc run [execve error]" { 264 cat <<EOF >rootfs/run.sh 265 #!/mmnnttbb foo bar 266 sh 267 EOF 268 chmod +x rootfs/run.sh 269 update_config '.process.args = [ "/run.sh" ]' 270 runc run test_hello 271 [ "$status" -ne 0 ] 272 273 # After the sync socket closed, we should not send error to parent 274 # process, or else we will get a unnecessary error log(#4171). 275 [ ${#lines[@]} -eq 1 ] 276 [[ ${lines[0]} = "exec /run.sh: no such file or directory" ]] 277 }