...
1#!/usr/bin/env bats
2
3load helpers
4
5function 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/{proc,sys,tmp}
16 mkdir -p rootfs/tmp/mount-{1,2}
17
18 to_umount_list="$(mktemp "$BATS_RUN_TMPDIR/userns-mounts.XXXXXX")"
19 if [ "$ROOTLESS" -eq 0 ]; then
20 update_config ' .linux.namespaces += [{"type": "user"}]
21 | .linux.uidMappings += [{"hostID": 100000, "containerID": 0, "size": 65534}]
22 | .linux.gidMappings += [{"hostID": 200000, "containerID": 0, "size": 65534}] '
23 fi
24}
25
26function 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}
View as plain text