@@ -29,7 +29,7 @@ function teardown() {
2929 if [ -v to_umount_list ]; then
3030 while read -r mount_path; do
3131 umount -l " $mount_path " || :
32- rm -f " $mount_path "
32+ rm -rf " $mount_path "
3333 done < " $to_umount_list "
3434 rm -f " $to_umount_list "
3535 unset to_umount_list
@@ -184,3 +184,65 @@ function teardown() {
184184 grep -E ' ^\s+0\s+' $EUID ' \s+1$' <<< " $output"
185185 fi
186186}
187+
188+ # <https://github.com/opencontainers/runc/issues/4390>
189+ @test " userns join external namespaces [wrong userns owner]" {
190+ requires root
191+
192+ # Create an external user namespace for us to join. It seems on some
193+ # operating systems (AlmaLinux in particular) "unshare -U" will
194+ # automatically use an identity mapping (which breaks this test) so we need
195+ # to use runc to create the userns.
196+ update_config ' .process.args = ["sleep", "infinity"]'
197+ runc run -d --console-socket " $CONSOLE_SOCKET " target_userns
198+ [ " $status " -eq 0 ]
199+
200+ # Bind-mount the first containers userns nsfd to a different path, to
201+ # exercise the non-fast-path (where runc has to join the userns to get the
202+ # mappings).
203+ userns_pid=" $( __runc state target_userns | jq .pid) "
204+ userns_path=" $( mktemp " $BATS_RUN_TMPDIR /userns.XXXXXX" ) "
205+ mount --bind " /proc/$userns_pid /ns/user" " $userns_path "
206+ echo " $userns_path " >> " $to_umount_list "
207+
208+ # Kill the container -- we have the userns bind-mounted.
209+ runc delete -f target_userns
210+ [ " $status " -eq 0 ]
211+
212+ # Configure our container to attach to the external userns.
213+ update_config ' .linux.namespaces |= map(if .type == "user" then (.path = "' " $userns_path " ' ") else . end)
214+ | del(.linux.uidMappings)
215+ | del(.linux.gidMappings)'
216+
217+ # Also create a network namespace that *is not owned* by the above userns.
218+ # NOTE: Having no permissions in a namespaces makes it necessary to modify
219+ # the config so that we don't get mount errors (for reference: no netns
220+ # permissions == no sysfs mounts, no pidns permissoins == no procfs mounts,
221+ # no utsns permissions == no sethostname(2), no ipc permissions == no
222+ # mqueue mounts, etc).
223+ netns_path=" $( mktemp " $BATS_RUN_TMPDIR /netns.XXXXXX" ) "
224+ unshare -i -- mount --bind " /proc/self/ns/net" " $netns_path "
225+ echo " $netns_path " >> " $to_umount_list "
226+ # Configure our container to attach to the external netns.
227+ update_config ' .linux.namespaces |= map(if .type == "network" then (.path = "' " $netns_path " ' ") else . end)'
228+
229+ # Convert sysfs mounts to a bind-mount from the host, to avoid permission
230+ # issues due to the netns setup we have.
231+ update_config ' .mounts |= map((select(.type == "sysfs") | { "source": "/sys", "destination": .destination, "type": "bind", "options": ["rbind"] }) // .)'
232+
233+ # Create a detached container to verify the namespaces are correct.
234+ update_config ' .process.args = ["sleep", "infinity"]'
235+ runc --debug run -d --console-socket " $CONSOLE_SOCKET " ctr
236+ [ " $status " -eq 0 ]
237+
238+ userns_id=" user:[$( stat -c " %i" " $userns_path " ) ]"
239+ netns_id=" net:[$( stat -c " %i" " $netns_path " ) ]"
240+
241+ runc exec ctr readlink /proc/self/ns/user
242+ [ " $status " -eq 0 ]
243+ [[ " $output " == " $userns_id " ]]
244+
245+ runc exec ctr readlink /proc/self/ns/net
246+ [ " $status " -eq 0 ]
247+ [[ " $output " == " $netns_id " ]]
248+ }
0 commit comments