]>
Commit | Line | Data |
---|---|---|
eb960fea DL |
1 | #!/bin/bash |
2 | ||
3 | # | |
4 | # lxc: linux Container library | |
5 | ||
6 | # Authors: | |
7 | # Daniel Lezcano <daniel.lezcano@free.fr> | |
8 | ||
9 | # This library is free software; you can redistribute it and/or | |
10 | # modify it under the terms of the GNU Lesser General Public | |
11 | # License as published by the Free Software Foundation; either | |
12 | # version 2.1 of the License, or (at your option) any later version. | |
13 | ||
14 | # This library is distributed in the hope that it will be useful, | |
15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | # Lesser General Public License for more details. | |
18 | ||
19 | # You should have received a copy of the GNU Lesser General Public | |
20 | # License along with this library; if not, write to the Free Software | |
250b1eec | 21 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
eb960fea | 22 | |
a542dd3c BP |
23 | LXC_MAPPED_UID= |
24 | LXC_MAPPED_GID= | |
ed52814c | 25 | SSH= |
8ec981fc | 26 | |
207bf0e4 SG |
27 | # Make sure the usual locations are in PATH |
28 | export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin | |
29 | ||
9e214906 SH |
30 | am_in_userns() { |
31 | [ -e /proc/self/uid_map ] || { echo no; return; } | |
32 | [ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || { echo yes; return; } | |
33 | line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map) | |
34 | [ "$line" = "0 0 4294967295" ] && { echo no; return; } | |
35 | echo yes | |
36 | } | |
37 | ||
38 | in_userns=0 | |
39 | [ $(am_in_userns) = "yes" ] && in_userns=1 | |
40 | ||
88e38993 BP |
41 | copy_binary() |
42 | { | |
43 | binary_path=`which $1` | |
44 | if [ $? -ne 0 ]; then | |
45 | echo "Unable to find $1 binary on the system" | |
46 | return 1 | |
47 | fi | |
48 | ||
49 | dir_path="${binary_path%/*}" | |
50 | echo /{,usr/}{,s}bin | grep $dir_path >/dev/null 2>&1 | |
51 | if [ $? -ne 0 ]; then | |
52 | echo "Binary $1 is located at $binary_path and will not be copied" | |
53 | echo "($dir_path not supported)" | |
54 | return 1 | |
55 | fi | |
56 | ||
57 | cp $binary_path $rootfs/$binary_path | |
58 | if [ $? -ne 0 ]; then | |
59 | echo "Failed to copy $binary_path to rootfs" | |
60 | return 1 | |
61 | fi | |
62 | ||
63 | return 0 | |
64 | } | |
65 | ||
eb960fea DL |
66 | install_busybox() |
67 | { | |
68 | rootfs=$1 | |
69 | name=$2 | |
70 | res=0 | |
71 | tree="\ | |
32b37181 | 72 | $rootfs/selinux \ |
eb960fea DL |
73 | $rootfs/dev \ |
74 | $rootfs/home \ | |
75 | $rootfs/root \ | |
76 | $rootfs/etc \ | |
77 | $rootfs/etc/init.d \ | |
78 | $rootfs/bin \ | |
c94e60d1 | 79 | $rootfs/usr/bin \ |
eb960fea | 80 | $rootfs/sbin \ |
c94e60d1 | 81 | $rootfs/usr/sbin \ |
eb960fea | 82 | $rootfs/proc \ |
fefddf9f | 83 | $rootfs/sys \ |
eb960fea DL |
84 | $rootfs/mnt \ |
85 | $rootfs/tmp \ | |
86 | $rootfs/var/log \ | |
87 | $rootfs/usr/share/udhcpc \ | |
10e657e5 | 88 | $rootfs/dev/pts \ |
bf6cc736 DL |
89 | $rootfs/dev/shm \ |
90 | $rootfs/lib \ | |
91 | $rootfs/usr/lib \ | |
92 | $rootfs/lib64 \ | |
93 | $rootfs/usr/lib64" | |
eb960fea DL |
94 | |
95 | mkdir -p $tree || return 1 | |
96 | chmod 755 $tree || return 1 | |
97 | ||
98 | pushd $rootfs/dev > /dev/null || return 1 | |
99 | ||
100 | # minimal devices needed for busybox | |
9e214906 | 101 | if [ $in_userns -eq 1 ]; then |
cfe615f0 | 102 | for dev in tty console tty0 tty1 ram0 null urandom; do |
f24a52d5 | 103 | echo "lxc.mount.entry = /dev/$dev dev/$dev none bind,optional,create=file 0 0" >> $path/config |
9e214906 SH |
104 | done |
105 | else | |
fbbc1539 DE |
106 | mknod -m 666 tty c 5 0 || res=1 |
107 | mknod -m 666 console c 5 1 || res=1 | |
108 | mknod -m 666 tty0 c 4 0 || res=1 | |
109 | mknod -m 666 tty1 c 4 0 || res=1 | |
110 | mknod -m 666 tty5 c 4 0 || res=1 | |
111 | mknod -m 600 ram0 b 1 0 || res=1 | |
112 | mknod -m 666 null c 1 3 || res=1 | |
113 | mknod -m 666 zero c 1 5 || res=1 | |
114 | mknod -m 666 urandom c 1 9 || res=1 | |
9e214906 | 115 | fi |
eb960fea DL |
116 | |
117 | popd > /dev/null | |
118 | ||
119 | # root user defined | |
120 | cat <<EOF >> $rootfs/etc/passwd | |
121 | root:x:0:0:root:/root:/bin/sh | |
122 | EOF | |
123 | ||
124 | cat <<EOF >> $rootfs/etc/group | |
125 | root:x:0:root | |
126 | EOF | |
127 | ||
eb960fea DL |
128 | # mount everything |
129 | cat <<EOF >> $rootfs/etc/init.d/rcS | |
130 | #!/bin/sh | |
b09ecaf3 DL |
131 | /bin/syslogd |
132 | /bin/mount -a | |
133 | /bin/udhcpc | |
eb960fea DL |
134 | EOF |
135 | ||
136 | # executable | |
137 | chmod 744 $rootfs/etc/init.d/rcS || return 1 | |
138 | ||
eb960fea DL |
139 | # launch rcS first then make a console available |
140 | # and propose a shell on the tty, the last one is | |
141 | # not needed | |
142 | cat <<EOF >> $rootfs/etc/inittab | |
143 | ::sysinit:/etc/init.d/rcS | |
0016af97 DL |
144 | tty1::respawn:/bin/getty -L tty1 115200 vt100 |
145 | console::askfirst:/bin/sh | |
eb960fea DL |
146 | EOF |
147 | # writable and readable for other | |
148 | chmod 644 $rootfs/etc/inittab || return 1 | |
149 | ||
150 | cat <<EOF >> $rootfs/usr/share/udhcpc/default.script | |
151 | #!/bin/sh | |
eb960fea | 152 | case "\$1" in |
14d9c0f0 SG |
153 | deconfig) |
154 | ip addr flush dev \$interface | |
155 | ;; | |
156 | ||
157 | renew|bound) | |
158 | # flush all the routes | |
159 | if [ -n "\$router" ]; then | |
160 | ip route del default 2> /dev/null | |
161 | fi | |
162 | ||
163 | # check broadcast | |
164 | if [ -n "\$broadcast" ]; then | |
165 | broadcast="broadcast \$broadcast" | |
166 | fi | |
167 | ||
168 | # add a new ip address | |
169 | ip addr add \$ip/\$mask \$broadcast dev \$interface | |
170 | ||
171 | if [ -n "\$router" ]; then | |
172 | ip route add default via \$router dev \$interface | |
173 | fi | |
174 | ||
175 | [ -n "\$domain" ] && echo search \$domain > /etc/resolv.conf | |
176 | for i in \$dns ; do | |
177 | echo nameserver \$i >> /etc/resolv.conf | |
178 | done | |
179 | ;; | |
eb960fea DL |
180 | esac |
181 | exit 0 | |
182 | EOF | |
183 | ||
184 | chmod 744 $rootfs/usr/share/udhcpc/default.script | |
185 | ||
186 | return $res | |
187 | } | |
188 | ||
ed52814c BP |
189 | install_dropbear() |
190 | { | |
191 | # copy dropbear binary | |
88e38993 | 192 | copy_binary dropbear || return 1 |
ed52814c BP |
193 | |
194 | # make symlinks to various ssh utilities | |
195 | utils="\ | |
196 | $rootfs/usr/bin/dbclient \ | |
197 | $rootfs/usr/bin/scp \ | |
198 | $rootfs/usr/bin/ssh \ | |
199 | $rootfs/usr/sbin/dropbearkey \ | |
200 | $rootfs/usr/sbin/dropbearconvert \ | |
201 | " | |
202 | echo $utils | xargs -n1 ln -s /usr/sbin/dropbear | |
203 | ||
204 | # add necessary config files | |
205 | mkdir $rootfs/etc/dropbear | |
206 | dropbearkey -t rsa -f $rootfs/etc/dropbear/dropbear_rsa_host_key > /dev/null 2>&1 | |
207 | dropbearkey -t dss -f $rootfs/etc/dropbear/dropbear_dss_host_key > /dev/null 2>&1 | |
208 | ||
209 | echo "'dropbear' ssh utility installed" | |
210 | ||
211 | return 0 | |
212 | } | |
213 | ||
214 | install_openssh() | |
215 | { | |
216 | # tools to be installed | |
217 | server_utils="sshd" | |
218 | client_utils="\ | |
219 | ssh \ | |
220 | scp \ | |
4432b512 BP |
221 | " |
222 | client_optional_utils="\ | |
ed52814c BP |
223 | sftp \ |
224 | ssh-add \ | |
225 | ssh-agent \ | |
226 | ssh-keygen \ | |
227 | ssh-keyscan \ | |
228 | ssh-argv0 \ | |
229 | ssh-copy-id \ | |
230 | " | |
231 | ||
232 | # new folders used by ssh | |
233 | ssh_tree="\ | |
234 | $rootfs/etc/ssh \ | |
235 | $rootfs/var/empty/sshd \ | |
236 | $rootfs/var/lib/empty/sshd \ | |
237 | $rootfs/var/run/sshd \ | |
238 | " | |
239 | ||
240 | # create folder structure | |
241 | mkdir -p $ssh_tree | |
242 | if [ $? -ne 0 ]; then | |
243 | return 1 | |
244 | fi | |
245 | ||
246 | # copy binaries | |
247 | for bin in $server_utils $client_utils; do | |
88e38993 | 248 | copy_binary $bin || return 1 |
ed52814c BP |
249 | done |
250 | ||
4432b512 | 251 | for bin in $client_optional_utils; do |
88e38993 | 252 | tool_path=`which $bin` && copy_binary $bin |
4432b512 BP |
253 | done |
254 | ||
ed52814c BP |
255 | # add user and group |
256 | cat <<EOF >> $rootfs/etc/passwd | |
257 | sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin | |
258 | EOF | |
259 | ||
260 | cat <<EOF >> $rootfs/etc/group | |
261 | sshd:x:74: | |
262 | EOF | |
263 | ||
264 | # generate container keys | |
265 | ssh-keygen -t rsa -N "" -f $rootfs/etc/ssh/ssh_host_rsa_key >/dev/null 2>&1 | |
266 | ssh-keygen -t dsa -N "" -f $rootfs/etc/ssh/ssh_host_dsa_key >/dev/null 2>&1 | |
267 | ||
268 | # by default setup root password with no password | |
269 | cat <<EOF > $rootfs/etc/ssh/sshd_config | |
270 | Port 22 | |
271 | Protocol 2 | |
272 | HostKey /etc/ssh/ssh_host_rsa_key | |
273 | HostKey /etc/ssh/ssh_host_dsa_key | |
274 | UsePrivilegeSeparation yes | |
275 | KeyRegenerationInterval 3600 | |
276 | ServerKeyBits 768 | |
277 | SyslogFacility AUTH | |
278 | LogLevel INFO | |
279 | LoginGraceTime 120 | |
280 | PermitRootLogin yes | |
281 | StrictModes yes | |
282 | RSAAuthentication yes | |
283 | PubkeyAuthentication yes | |
284 | IgnoreRhosts yes | |
285 | RhostsRSAAuthentication no | |
286 | HostbasedAuthentication no | |
287 | PermitEmptyPasswords yes | |
288 | ChallengeResponseAuthentication no | |
289 | EOF | |
290 | ||
291 | echo "'OpenSSH' utility installed" | |
292 | ||
293 | return 0 | |
294 | } | |
295 | ||
eb960fea DL |
296 | configure_busybox() |
297 | { | |
298 | rootfs=$1 | |
299 | ||
169bf5e0 | 300 | which busybox >/dev/null 2>&1 |
7674618c | 301 | |
eb960fea | 302 | if [ $? -ne 0 ]; then |
14d9c0f0 SG |
303 | echo "busybox executable is not accessible" |
304 | return 1 | |
eb960fea DL |
305 | fi |
306 | ||
307 | # copy busybox in the rootfs | |
308 | cp $(which busybox) $rootfs/bin | |
309 | if [ $? -ne 0 ]; then | |
14d9c0f0 SG |
310 | echo "failed to copy busybox in the rootfs" |
311 | return 1 | |
eb960fea DL |
312 | fi |
313 | ||
6902a6c6 DE |
314 | # symlink busybox for the commands it supports |
315 | # it would be nice to just use "chroot $rootfs busybox --install -s /bin" | |
316 | # but that only works right in a chroot with busybox >= 1.19.0 | |
317 | pushd $rootfs/bin > /dev/null || return 1 | |
318 | ./busybox --help | grep 'Currently defined functions:' -A300 | \ | |
319 | grep -v 'Currently defined functions:' | tr , '\n' | \ | |
320 | xargs -n1 ln -s busybox | |
321 | popd > /dev/null | |
eb960fea DL |
322 | |
323 | # relink /sbin/init | |
324 | ln $rootfs/bin/busybox $rootfs/sbin/init | |
325 | ||
6ab1ca03 BP |
326 | # /etc/fstab must exist for "mount -a" |
327 | touch $rootfs/etc/fstab | |
328 | ||
eb960fea DL |
329 | # passwd exec must be setuid |
330 | chmod +s $rootfs/bin/passwd | |
32b37181 | 331 | touch $rootfs/etc/shadow |
19d618b1 | 332 | |
eb960fea DL |
333 | return 0 |
334 | } | |
335 | ||
336 | copy_configuration() | |
337 | { | |
338 | path=$1 | |
339 | rootfs=$2 | |
340 | name=$3 | |
341 | ||
7a96a068 | 342 | grep -q "^lxc.rootfs.path" $path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs" >> $path/config |
eb960fea | 343 | cat <<EOF >> $path/config |
55c84efc | 344 | lxc.signal.halt = SIGUSR1 |
fd998241 | 345 | lxc.signal.reboot = SIGTERM |
b67771bc | 346 | lxc.uts.name = $name |
fe1c5887 | 347 | lxc.tty.max = 1 |
232763d6 | 348 | lxc.pty.max = 1 |
eee3ba81 | 349 | lxc.cap.drop = sys_module mac_admin mac_override sys_time |
69d66f1e SG |
350 | |
351 | # When using LXC with apparmor, uncomment the next line to run unconfined: | |
a1d5fdfd | 352 | #lxc.apparmor.profile = unconfined |
f24a52d5 SG |
353 | |
354 | lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed | |
355 | lxc.mount.entry = shm /dev/shm tmpfs defaults 0 0 | |
1881820a SH |
356 | EOF |
357 | ||
6bc424b5 | 358 | libdirs="\ |
5d01f616 SG |
359 | lib \ |
360 | usr/lib \ | |
361 | lib64 \ | |
362 | usr/lib64" | |
6bc424b5 SY |
363 | |
364 | for dir in $libdirs; do | |
5d01f616 | 365 | if [ -d "/$dir" ] && [ -d "$rootfs/$dir" ]; then |
eba7df9e | 366 | echo "lxc.mount.entry = /$dir $dir none ro,bind 0 0" >> $path/config |
6bc424b5 SY |
367 | fi |
368 | done | |
7f1dea04 | 369 | echo "lxc.mount.entry = /sys/kernel/security sys/kernel/security none ro,bind,optional 0 0" >>$path/config |
eb960fea DL |
370 | } |
371 | ||
a542dd3c BP |
372 | remap_userns() |
373 | { | |
374 | path=$1 | |
375 | ||
376 | if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then | |
f24a52d5 | 377 | chown $LXC_MAPPED_UID $path/config >/dev/null 2>&1 |
a542dd3c BP |
378 | chown -R root $path/rootfs >/dev/null 2>&1 |
379 | fi | |
380 | ||
381 | if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then | |
f24a52d5 | 382 | chgrp $LXC_MAPPED_GID $path/config >/dev/null 2>&1 |
a542dd3c BP |
383 | chgrp -R root $path/rootfs >/dev/null 2>&1 |
384 | fi | |
385 | } | |
386 | ||
eb960fea DL |
387 | usage() |
388 | { | |
389 | cat <<EOF | |
ed52814c | 390 | $1 -h|--help -p|--path=<path> -s|--ssh={dropbear,openssh} |
eb960fea DL |
391 | EOF |
392 | return 0 | |
393 | } | |
394 | ||
ed52814c | 395 | options=$(getopt -o hp:n:s: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid:,ssh: -- "$@") |
eb960fea | 396 | if [ $? -ne 0 ]; then |
14d9c0f0 SG |
397 | usage $(basename $0) |
398 | exit 1 | |
eb960fea DL |
399 | fi |
400 | eval set -- "$options" | |
401 | ||
402 | while true | |
403 | do | |
404 | case "$1" in | |
405 | -h|--help) usage $0 && exit 0;; | |
406 | -p|--path) path=$2; shift 2;; | |
1897e3bc | 407 | --rootfs) rootfs=$2; shift 2;; |
14d9c0f0 | 408 | -n|--name) name=$2; shift 2;; |
a542dd3c BP |
409 | --mapped-uid) LXC_MAPPED_UID=$2; shift 2;; |
410 | --mapped-gid) LXC_MAPPED_GID=$2; shift 2;; | |
ed52814c | 411 | -s|--ssh) SSH=$2; shift 2;; |
eb960fea DL |
412 | --) shift 1; break ;; |
413 | *) break ;; | |
414 | esac | |
415 | done | |
416 | ||
417 | if [ "$(id -u)" != "0" ]; then | |
418 | echo "This script should be run as 'root'" | |
419 | exit 1 | |
420 | fi | |
421 | ||
422 | if [ -z "$path" ]; then | |
423 | echo "'path' parameter is required" | |
424 | exit 1 | |
425 | fi | |
426 | ||
1881820a SH |
427 | # detect rootfs |
428 | config="$path/config" | |
1897e3bc | 429 | if [ -z "$rootfs" ]; then |
7a96a068 CB |
430 | if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then |
431 | rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) | |
1897e3bc SH |
432 | else |
433 | rootfs=$path/rootfs | |
434 | fi | |
1881820a | 435 | fi |
eb960fea DL |
436 | |
437 | install_busybox $rootfs $name | |
438 | if [ $? -ne 0 ]; then | |
439 | echo "failed to install busybox's rootfs" | |
440 | exit 1 | |
441 | fi | |
442 | ||
443 | configure_busybox $rootfs | |
444 | if [ $? -ne 0 ]; then | |
445 | echo "failed to configure busybox template" | |
446 | exit 1 | |
447 | fi | |
448 | ||
449 | copy_configuration $path $rootfs $name | |
450 | if [ $? -ne 0 ]; then | |
451 | echo "failed to write configuration file" | |
452 | exit 1 | |
453 | fi | |
a542dd3c BP |
454 | |
455 | remap_userns $path | |
456 | if [ $? -ne 0 ]; then | |
457 | echo "failed to remap files to user" | |
458 | exit 1 | |
459 | fi | |
ed52814c BP |
460 | |
461 | if [ -n "$SSH" ]; then | |
462 | case "$SSH" in | |
463 | "dropbear") | |
464 | install_dropbear | |
465 | if [ $? -ne 0 ]; then | |
466 | echo "Unable to install 'dropbear' ssh utility" | |
467 | exit 1 | |
468 | fi ;; | |
469 | "openssh") | |
470 | install_openssh | |
471 | if [ $? -ne 0 ]; then | |
472 | echo "Unable to install 'OpenSSH' utility" | |
473 | exit 1 | |
474 | fi ;; | |
475 | *) | |
476 | echo "$SSH: unrecognized ssh utility" | |
477 | exit 1 | |
478 | esac | |
479 | else | |
480 | which dropbear >/dev/null 2>&1 | |
481 | if [ $? -eq 0 ]; then | |
482 | install_dropbear | |
483 | fi | |
484 | fi |