]>
Commit | Line | Data |
---|---|---|
b62671d8 | 1 | #!/bin/sh |
eb960fea | 2 | |
b62671d8 | 3 | # Client script for LXC container images. |
eb960fea | 4 | # |
b62671d8 CB |
5 | # Copyright @ Daniel Lezcano <daniel.lezcano@free.fr> |
6 | # Copyright © 2018 Christian Brauner <christian.brauner@ubuntu.com> | |
7 | # | |
8 | # This library is free software; you can redistribute it and/or | |
9 | # modify it under the terms of the GNU Lesser General Public | |
10 | # License as published by the Free Software Foundation; either | |
11 | # version 2.1 of the License, or (at your option) any later version. | |
eb960fea | 12 | |
b62671d8 CB |
13 | # This library is distributed in the hope that it will be useful, |
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | # Lesser General Public License for more details. | |
eb960fea | 17 | |
b62671d8 CB |
18 | # You should have received a copy of the GNU Lesser General Public |
19 | # License along with this library; if not, write to the Free Software | |
20 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 | |
21 | # USA | |
eb960fea | 22 | |
a542dd3c BP |
23 | LXC_MAPPED_UID= |
24 | LXC_MAPPED_GID= | |
8ec981fc | 25 | |
7a767165 | 26 | BUSYBOX_EXE=$(command -v busybox) |
1c9bca6b | 27 | |
207bf0e4 | 28 | # Make sure the usual locations are in PATH |
655d10ed | 29 | export PATH="$PATH:/usr/sbin:/usr/bin:/sbin:/bin" |
207bf0e4 | 30 | |
b62671d8 CB |
31 | in_userns() { |
32 | [ -e /proc/self/uid_map ] || { echo no; return; } | |
33 | while read -r line; do | |
34 | fields="$(echo "$line" | awk '{ print $1 " " $2 " " $3 }')" | |
35 | if [ "${fields}" = "0 0 4294967295" ]; then | |
36 | echo no; | |
37 | return; | |
88e38993 | 38 | fi |
b62671d8 CB |
39 | if echo "${fields}" | grep -q " 0 1$"; then |
40 | echo userns-root; | |
41 | return; | |
88e38993 | 42 | fi |
b62671d8 | 43 | done < /proc/self/uid_map |
88e38993 | 44 | |
16a312e1 LP |
45 | if [ -e /proc/1/uid_map ]; then |
46 | if [ "$(cat /proc/self/uid_map)" = "$(cat /proc/1/uid_map)" ]; then | |
47 | echo userns-root | |
48 | return | |
49 | fi | |
50 | fi | |
b62671d8 | 51 | echo yes |
88e38993 BP |
52 | } |
53 | ||
b62671d8 CB |
54 | USERNS="$(in_userns)" |
55 | ||
eb960fea DL |
56 | install_busybox() |
57 | { | |
b62671d8 CB |
58 | rootfs="${1}" |
59 | name="${2}" | |
60 | res=0 | |
61 | fstree="\ | |
62 | ${rootfs}/selinux \ | |
63 | ${rootfs}/dev \ | |
64 | ${rootfs}/home \ | |
65 | ${rootfs}/root \ | |
66 | ${rootfs}/etc \ | |
67 | ${rootfs}/etc/init.d \ | |
68 | ${rootfs}/bin \ | |
69 | ${rootfs}/usr/bin \ | |
70 | ${rootfs}/sbin \ | |
71 | ${rootfs}/usr/sbin \ | |
72 | ${rootfs}/proc \ | |
73 | ${rootfs}/sys \ | |
74 | ${rootfs}/mnt \ | |
75 | ${rootfs}/tmp \ | |
76 | ${rootfs}/var/log \ | |
c65973ad | 77 | ${rootfs}/var/run \ |
b62671d8 | 78 | ${rootfs}/dev/pts \ |
b62671d8 CB |
79 | ${rootfs}/lib \ |
80 | ${rootfs}/usr/lib \ | |
81 | ${rootfs}/lib64 \ | |
82 | ${rootfs}/usr/lib64" | |
83 | ||
84 | # shellcheck disable=SC2086 | |
85 | mkdir -p ${fstree} || return 1 | |
86 | # shellcheck disable=SC2086 | |
87 | chmod 755 ${fstree} || return 1 | |
88 | ||
a725bbc4 RK |
89 | # make /tmp accessible to any user (with sticky bit) |
90 | chmod 1777 "${rootfs}/tmp" || return 1 | |
b62671d8 CB |
91 | |
92 | # root user defined | |
93 | cat <<EOF >> "${rootfs}/etc/passwd" | |
eb960fea DL |
94 | root:x:0:0:root:/root:/bin/sh |
95 | EOF | |
96 | ||
b62671d8 | 97 | cat <<EOF >> "${rootfs}/etc/group" |
eb960fea DL |
98 | root:x:0:root |
99 | EOF | |
100 | ||
eb960fea | 101 | # mount everything |
b62671d8 | 102 | cat <<EOF >> "${rootfs}/etc/init.d/rcS" |
eb960fea | 103 | #!/bin/sh |
b09ecaf3 DL |
104 | /bin/syslogd |
105 | /bin/mount -a | |
106 | /bin/udhcpc | |
eb960fea DL |
107 | EOF |
108 | ||
b62671d8 CB |
109 | # executable |
110 | chmod 744 "${rootfs}/etc/init.d/rcS" || return 1 | |
eb960fea | 111 | |
b62671d8 CB |
112 | # launch rcS first then make a console available |
113 | # and propose a shell on the tty, the last one is | |
114 | # not needed | |
115 | cat <<EOF >> "${rootfs}/etc/inittab" | |
eb960fea | 116 | ::sysinit:/etc/init.d/rcS |
0016af97 DL |
117 | tty1::respawn:/bin/getty -L tty1 115200 vt100 |
118 | console::askfirst:/bin/sh | |
eb960fea | 119 | EOF |
b62671d8 CB |
120 | # writable and readable for other |
121 | chmod 644 "${rootfs}/etc/inittab" || return 1 | |
eb960fea | 122 | |
1c9bca6b | 123 | # Look for the pathname of "default.script" from the help of udhcpc |
ac46b356 DH |
124 | DEF_SCRIPT=$(${BUSYBOX_EXE} udhcpc --help 2>&1 | egrep -- '-s.*Run PROG' | cut -d'/' -f2- | cut -d')' -f1) |
125 | DEF_SCRIPT_DIR=$(dirname /${DEF_SCRIPT}) | |
1c9bca6b RK |
126 | mkdir -p ${rootfs}/${DEF_SCRIPT_DIR} |
127 | chmod 644 ${rootfs}/${DEF_SCRIPT_DIR} || return 1 | |
128 | ||
129 | cat <<EOF >> ${rootfs}/${DEF_SCRIPT} | |
eb960fea | 130 | #!/bin/sh |
eb960fea | 131 | case "\$1" in |
b62671d8 CB |
132 | deconfig) |
133 | ip addr flush dev \$interface | |
134 | ;; | |
135 | ||
136 | renew|bound) | |
137 | # flush all the routes | |
138 | if [ -n "\$router" ]; then | |
139 | ip route del default 2> /dev/null | |
140 | fi | |
eb960fea | 141 | |
b62671d8 CB |
142 | # check broadcast |
143 | if [ -n "\$broadcast" ]; then | |
144 | broadcast="broadcast \$broadcast" | |
145 | fi | |
eb960fea | 146 | |
b62671d8 CB |
147 | # add a new ip address |
148 | ip addr add \$ip/\$mask \$broadcast dev \$interface | |
eb960fea | 149 | |
b62671d8 CB |
150 | if [ -n "\$router" ]; then |
151 | ip route add default via \$router dev \$interface | |
ed52814c BP |
152 | fi |
153 | ||
b62671d8 CB |
154 | [ -n "\$domain" ] && echo search \$domain > /etc/resolv.conf |
155 | for i in \$dns ; do | |
567f8915 RK |
156 | grep "nameserver \$i" /etc/resolv.conf > /dev/null 2>&1 |
157 | if [ \$? -ne 0 ]; then | |
158 | echo nameserver \$i >> /etc/resolv.conf | |
159 | fi | |
4432b512 | 160 | done |
b62671d8 CB |
161 | ;; |
162 | esac | |
163 | exit 0 | |
ed52814c BP |
164 | EOF |
165 | ||
1c9bca6b | 166 | chmod 744 ${rootfs}/${DEF_SCRIPT} |
ed52814c | 167 | |
b62671d8 | 168 | return "${res}" |
ed52814c BP |
169 | } |
170 | ||
eb960fea DL |
171 | configure_busybox() |
172 | { | |
b62671d8 CB |
173 | rootfs="${1}" |
174 | ||
b62671d8 | 175 | # copy busybox in the rootfs |
4765b926 | 176 | if ! cp "${BUSYBOX_EXE}" "${rootfs}/bin"; then |
634ad935 | 177 | echo "ERROR: Failed to copy busybox binary" 1>&2 |
b62671d8 CB |
178 | return 1 |
179 | fi | |
180 | ||
181 | # symlink busybox for the commands it supports | |
182 | # it would be nice to just use "chroot $rootfs busybox --install -s /bin" | |
183 | # but that only works right in a chroot with busybox >= 1.19.0 | |
184 | ( | |
185 | cd "${rootfs}/bin" || return 1 | |
8b7681f3 | 186 | ./busybox --list | grep -v busybox | xargs -n1 ln -s busybox |
b62671d8 | 187 | ) |
eb960fea | 188 | |
b62671d8 CB |
189 | # relink /sbin/init |
190 | ln "${rootfs}/bin/busybox" "${rootfs}/sbin/init" | |
eb960fea | 191 | |
b62671d8 CB |
192 | # /etc/fstab must exist for "mount -a" |
193 | touch "${rootfs}/etc/fstab" | |
6ab1ca03 | 194 | |
b62671d8 CB |
195 | # passwd exec must be setuid |
196 | chmod +s "${rootfs}/bin/passwd" | |
197 | touch "${rootfs}/etc/shadow" | |
19d618b1 | 198 | |
b62671d8 | 199 | return 0 |
eb960fea DL |
200 | } |
201 | ||
202 | copy_configuration() | |
203 | { | |
b62671d8 CB |
204 | path="${1}" |
205 | rootfs="${2}" | |
206 | name="${3}" | |
eb960fea | 207 | |
b62671d8 CB |
208 | grep -q "^lxc.rootfs.path" "${path}/config" 2>/dev/null || echo "lxc.rootfs.path = ${rootfs}" >> "${path}/config" |
209 | cat <<EOF >> "${path}/config" | |
55c84efc | 210 | lxc.signal.halt = SIGUSR1 |
fd998241 | 211 | lxc.signal.reboot = SIGTERM |
b62671d8 | 212 | lxc.uts.name = "${name}" |
b081cb55 CB |
213 | lxc.autodev = 1 |
214 | lxc.tty.max = 5 | |
232763d6 | 215 | lxc.pty.max = 1 |
eee3ba81 | 216 | lxc.cap.drop = sys_module mac_admin mac_override sys_time |
69d66f1e SG |
217 | |
218 | # When using LXC with apparmor, uncomment the next line to run unconfined: | |
a1d5fdfd | 219 | #lxc.apparmor.profile = unconfined |
f24a52d5 | 220 | |
8829829d | 221 | lxc.mount.auto = cgroup:mixed proc:mixed sys:ro |
83e280f6 | 222 | lxc.mount.entry = shm dev/shm tmpfs defaults,create=dir 0 0 |
bff93997 | 223 | lxc.mount.entry = mqueue dev/mqueue mqueue defaults,optional,create=dir 0 0 |
1881820a SH |
224 | EOF |
225 | ||
b62671d8 CB |
226 | libdirs="\ |
227 | lib \ | |
228 | usr/lib \ | |
229 | lib64 \ | |
230 | usr/lib64" | |
8b7681f3 | 231 | |
b62671d8 CB |
232 | for dir in ${libdirs}; do |
233 | if [ -d "/${dir}" ] && [ -d "${rootfs}/${dir}" ]; then | |
234 | echo "lxc.mount.entry = /${dir} ${dir} none ro,bind 0 0" >> "${path}/config" | |
235 | fi | |
236 | done | |
237 | echo "lxc.mount.entry = /sys/kernel/security sys/kernel/security none ro,bind,optional 0 0" >> "${path}/config" | |
eb960fea DL |
238 | } |
239 | ||
a542dd3c BP |
240 | remap_userns() |
241 | { | |
b62671d8 | 242 | path="${1}" |
a542dd3c | 243 | |
b62671d8 CB |
244 | if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then |
245 | chown "${LXC_MAPPED_UID}" "${path}/config" > /dev/null 2>&1 | |
246 | chown -R root "${path}/rootfs" > /dev/null 2>&1 | |
247 | fi | |
a542dd3c | 248 | |
b62671d8 CB |
249 | if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then |
250 | chgrp "${LXC_MAPPED_GID}" "${path}/config" > /dev/null 2>&1 | |
251 | chgrp -R root "${path}/rootfs" > /dev/null 2>&1 | |
252 | fi | |
a542dd3c BP |
253 | } |
254 | ||
b62671d8 CB |
255 | usage() { |
256 | cat <<EOF | |
257 | LXC busybox image builder | |
258 | ||
259 | Special arguments: | |
e7962394 RK |
260 | |
261 | [ -h | --help ]: Print this help message and exit. | |
262 | ||
263 | LXC internal arguments: | |
264 | ||
265 | [ --name <name> ]: The container name | |
266 | [ --path <path> ]: The path to the container | |
267 | [ --rootfs <rootfs> ]: The path to the container's rootfs (default: config or <path>/rootfs) | |
268 | [ --mapped-uid <map> ]: A uid map (user namespaces) | |
269 | [ --mapped-gid <map> ]: A gid map (user namespaces) | |
270 | ||
271 | BUSYBOX template specific arguments: | |
272 | ||
5f0fb855 | 273 | [ --busybox-path <path> ]: busybox pathname (default: ${BUSYBOX_EXE}) |
e7962394 | 274 | |
eb960fea | 275 | EOF |
b62671d8 | 276 | return 0 |
eb960fea DL |
277 | } |
278 | ||
5f0fb855 | 279 | if ! options=$(getopt -o hp:n: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid:,busybox-path: -- "$@"); then |
b62671d8 CB |
280 | usage |
281 | exit 1 | |
eb960fea DL |
282 | fi |
283 | eval set -- "$options" | |
284 | ||
285 | while true | |
286 | do | |
b62671d8 | 287 | case "$1" in |
634ad935 | 288 | -h|--help) usage && exit 0;; |
b62671d8 CB |
289 | -n|--name) name=$2; shift 2;; |
290 | -p|--path) path=$2; shift 2;; | |
291 | --rootfs) rootfs=$2; shift 2;; | |
292 | --mapped-uid) LXC_MAPPED_UID=$2; shift 2;; | |
293 | --mapped-gid) LXC_MAPPED_GID=$2; shift 2;; | |
5f0fb855 | 294 | --busybox-path) BUSYBOX_EXE=$2; shift 2;; |
b62671d8 CB |
295 | --) shift 1; break ;; |
296 | *) break ;; | |
297 | esac | |
eb960fea DL |
298 | done |
299 | ||
b62671d8 | 300 | # Check that we have all variables we need |
e7962394 RK |
301 | if [ -z "${name}" ] || [ -z "${path}" ]; then |
302 | echo "ERROR: Please pass the name and path for the container" 1>&2 | |
eb960fea DL |
303 | exit 1 |
304 | fi | |
305 | ||
1c9bca6b | 306 | # Make sure busybox is present |
e7962394 RK |
307 | if [ -z "${BUSYBOX_EXE}" ]; then |
308 | echo "ERROR: Please pass a pathname for busybox binary" 1>&2 | |
309 | exit 1 | |
310 | fi | |
311 | if [ ! -x "${BUSYBOX_EXE}" ]; then | |
312 | echo "ERROR: Failed to find busybox binary (${BUSYBOX_EXE})" 1>&2 | |
1c9bca6b RK |
313 | exit 1 |
314 | fi | |
315 | ||
1881820a SH |
316 | # detect rootfs |
317 | config="$path/config" | |
1897e3bc | 318 | if [ -z "$rootfs" ]; then |
b62671d8 CB |
319 | if grep -q '^lxc.rootfs.path' "${config}" 2> /dev/null ; then |
320 | rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' "${config}") | |
321 | else | |
322 | rootfs="${path}/rootfs" | |
323 | fi | |
1881820a | 324 | fi |
eb960fea | 325 | |
b62671d8 | 326 | if ! install_busybox "${rootfs}" "${name}"; then |
634ad935 | 327 | echo "ERROR: Failed to install rootfs" 1>&2 |
b62671d8 | 328 | exit 1 |
eb960fea DL |
329 | fi |
330 | ||
b62671d8 | 331 | if ! configure_busybox "${rootfs}"; then |
634ad935 | 332 | echo "ERROR: Failed to configure busybox" 1>&2 |
b62671d8 | 333 | exit 1 |
eb960fea DL |
334 | fi |
335 | ||
b62671d8 | 336 | if ! copy_configuration "${path}" "${rootfs}" "${name}"; then |
634ad935 | 337 | echo "ERROR: Failed to write config file" 1>&2 |
b62671d8 | 338 | exit 1 |
eb960fea | 339 | fi |
a542dd3c | 340 | |
b62671d8 | 341 | if ! remap_userns "${path}"; then |
634ad935 | 342 | echo "ERROR: Failed to change idmappings" 1>&2 |
b62671d8 | 343 | exit 1 |
ed52814c | 344 | fi |