]> git.proxmox.com Git - mirror_lxc.git/blob - templates/lxc-busybox.in
Merge pull request #517 from hkjolhede/master
[mirror_lxc.git] / templates / lxc-busybox.in
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
21 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22
23 LXC_MAPPED_UID=
24 LXC_MAPPED_GID=
25 SSH=
26
27 # Make sure the usual locations are in PATH
28 export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
29
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
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
66 install_busybox()
67 {
68 rootfs=$1
69 name=$2
70 res=0
71 tree="\
72 $rootfs/selinux \
73 $rootfs/dev \
74 $rootfs/home \
75 $rootfs/root \
76 $rootfs/etc \
77 $rootfs/etc/init.d \
78 $rootfs/bin \
79 $rootfs/usr/bin \
80 $rootfs/sbin \
81 $rootfs/usr/sbin \
82 $rootfs/proc \
83 $rootfs/sys \
84 $rootfs/mnt \
85 $rootfs/tmp \
86 $rootfs/var/log \
87 $rootfs/usr/share/udhcpc \
88 $rootfs/dev/pts \
89 $rootfs/dev/shm \
90 $rootfs/lib \
91 $rootfs/usr/lib \
92 $rootfs/lib64 \
93 $rootfs/usr/lib64"
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
101 if [ $in_userns -eq 1 ]; then
102 for dev in tty console tty0 tty1 ram0 null urandom; do
103 echo "lxc.mount.entry = /dev/$dev dev/$dev none bind,optional,create=file 0 0" >> $path/config
104 done
105 else
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
115 fi
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
128 # mount everything
129 cat <<EOF >> $rootfs/etc/init.d/rcS
130 #!/bin/sh
131 /bin/syslogd
132 /bin/mount -a
133 /bin/udhcpc
134 EOF
135
136 # executable
137 chmod 744 $rootfs/etc/init.d/rcS || return 1
138
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
144 tty1::respawn:/bin/getty -L tty1 115200 vt100
145 console::askfirst:/bin/sh
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
152 case "\$1" in
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 ;;
180 esac
181 exit 0
182 EOF
183
184 chmod 744 $rootfs/usr/share/udhcpc/default.script
185
186 return $res
187 }
188
189 install_dropbear()
190 {
191 # copy dropbear binary
192 copy_binary dropbear || return 1
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 \
221 "
222 client_optional_utils="\
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
248 copy_binary $bin || return 1
249 done
250
251 for bin in $client_optional_utils; do
252 tool_path=`which $bin` && copy_binary $bin
253 done
254
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
296 configure_busybox()
297 {
298 rootfs=$1
299
300 which busybox >/dev/null 2>&1
301
302 if [ $? -ne 0 ]; then
303 echo "busybox executable is not accessible"
304 return 1
305 fi
306
307 file -L $(which busybox) | grep -q "statically linked"
308 if [ $? -ne 0 ]; then
309 echo "warning : busybox is not statically linked."
310 echo "warning : The template script may not correctly"
311 echo "warning : setup the container environment."
312 fi
313
314 # copy busybox in the rootfs
315 cp $(which busybox) $rootfs/bin
316 if [ $? -ne 0 ]; then
317 echo "failed to copy busybox in the rootfs"
318 return 1
319 fi
320
321 # symlink busybox for the commands it supports
322 # it would be nice to just use "chroot $rootfs busybox --install -s /bin"
323 # but that only works right in a chroot with busybox >= 1.19.0
324 pushd $rootfs/bin > /dev/null || return 1
325 ./busybox --help | grep 'Currently defined functions:' -A300 | \
326 grep -v 'Currently defined functions:' | tr , '\n' | \
327 xargs -n1 ln -s busybox
328 popd > /dev/null
329
330 # relink /sbin/init
331 ln $rootfs/bin/busybox $rootfs/sbin/init
332
333 # passwd exec must be setuid
334 chmod +s $rootfs/bin/passwd
335 touch $rootfs/etc/shadow
336
337 # setting passwd for root
338 CHPASSWD_FILE=$rootfs/root/chpasswd.sh
339
340 cat <<EOF >$CHPASSWD_FILE
341 echo "setting root password to \"root\""
342
343 mount -n --bind /lib $rootfs/lib
344 if [ \$? -ne 0 ]; then
345 echo "Failed bind-mounting /lib at $rootfs/lib"
346 exit 1
347 fi
348
349 chroot $rootfs chpasswd <<EOFF 2>/dev/null
350 root:root
351 EOFF
352
353
354 if [ \$? -ne 0 ]; then
355 echo "Failed to change root password"
356 exit 1
357 fi
358
359 umount $rootfs/lib
360
361 EOF
362
363 lxc-unshare -s MOUNT -- /bin/sh < $CHPASSWD_FILE
364 rm $CHPASSWD_FILE
365
366 return 0
367 }
368
369 copy_configuration()
370 {
371 path=$1
372 rootfs=$2
373 name=$3
374
375 grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config
376 cat <<EOF >> $path/config
377 lxc.haltsignal = SIGUSR1
378 lxc.rebootsignal = SIGTERM
379 lxc.utsname = $name
380 lxc.tty = 1
381 lxc.pts = 1
382 lxc.cap.drop = sys_module mac_admin mac_override sys_time
383
384 # When using LXC with apparmor, uncomment the next line to run unconfined:
385 #lxc.aa_profile = unconfined
386
387 lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed
388 lxc.mount.entry = shm /dev/shm tmpfs defaults 0 0
389 EOF
390
391 libdirs="\
392 lib \
393 usr/lib \
394 lib64 \
395 usr/lib64"
396
397 for dir in $libdirs; do
398 if [ -d "/$dir" ] && [ -d "$rootfs/$dir" ]; then
399 echo "lxc.mount.entry = /$dir $dir none ro,bind 0 0" >> $path/config
400 fi
401 done
402 echo "lxc.mount.entry = /sys/kernel/security sys/kernel/security none ro,bind,optional 0 0" >>$path/config
403 }
404
405 remap_userns()
406 {
407 path=$1
408
409 if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
410 chown $LXC_MAPPED_UID $path/config >/dev/null 2>&1
411 chown -R root $path/rootfs >/dev/null 2>&1
412 fi
413
414 if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then
415 chgrp $LXC_MAPPED_GID $path/config >/dev/null 2>&1
416 chgrp -R root $path/rootfs >/dev/null 2>&1
417 fi
418 }
419
420 usage()
421 {
422 cat <<EOF
423 $1 -h|--help -p|--path=<path> -s|--ssh={dropbear,openssh}
424 EOF
425 return 0
426 }
427
428 options=$(getopt -o hp:n:s: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid:,ssh: -- "$@")
429 if [ $? -ne 0 ]; then
430 usage $(basename $0)
431 exit 1
432 fi
433 eval set -- "$options"
434
435 while true
436 do
437 case "$1" in
438 -h|--help) usage $0 && exit 0;;
439 -p|--path) path=$2; shift 2;;
440 --rootfs) rootfs=$2; shift 2;;
441 -n|--name) name=$2; shift 2;;
442 --mapped-uid) LXC_MAPPED_UID=$2; shift 2;;
443 --mapped-gid) LXC_MAPPED_GID=$2; shift 2;;
444 -s|--ssh) SSH=$2; shift 2;;
445 --) shift 1; break ;;
446 *) break ;;
447 esac
448 done
449
450 if [ "$(id -u)" != "0" ]; then
451 echo "This script should be run as 'root'"
452 exit 1
453 fi
454
455 if [ -z "$path" ]; then
456 echo "'path' parameter is required"
457 exit 1
458 fi
459
460 # detect rootfs
461 config="$path/config"
462 if [ -z "$rootfs" ]; then
463 if grep -q '^lxc.rootfs' $config 2>/dev/null ; then
464 rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config)
465 else
466 rootfs=$path/rootfs
467 fi
468 fi
469
470 install_busybox $rootfs $name
471 if [ $? -ne 0 ]; then
472 echo "failed to install busybox's rootfs"
473 exit 1
474 fi
475
476 configure_busybox $rootfs
477 if [ $? -ne 0 ]; then
478 echo "failed to configure busybox template"
479 exit 1
480 fi
481
482 copy_configuration $path $rootfs $name
483 if [ $? -ne 0 ]; then
484 echo "failed to write configuration file"
485 exit 1
486 fi
487
488 remap_userns $path
489 if [ $? -ne 0 ]; then
490 echo "failed to remap files to user"
491 exit 1
492 fi
493
494 if [ -n "$SSH" ]; then
495 case "$SSH" in
496 "dropbear")
497 install_dropbear
498 if [ $? -ne 0 ]; then
499 echo "Unable to install 'dropbear' ssh utility"
500 exit 1
501 fi ;;
502 "openssh")
503 install_openssh
504 if [ $? -ne 0 ]; then
505 echo "Unable to install 'OpenSSH' utility"
506 exit 1
507 fi ;;
508 *)
509 echo "$SSH: unrecognized ssh utility"
510 exit 1
511 esac
512 else
513 which dropbear >/dev/null 2>&1
514 if [ $? -eq 0 ]; then
515 install_dropbear
516 fi
517 fi