]> git.proxmox.com Git - mirror_lxc.git/blob - templates/lxc-busybox.in
lxc-busybox: add OpenSSH support
[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 install_busybox()
42 {
43 rootfs=$1
44 name=$2
45 res=0
46 tree="\
47 $rootfs/selinux \
48 $rootfs/dev \
49 $rootfs/home \
50 $rootfs/root \
51 $rootfs/etc \
52 $rootfs/etc/init.d \
53 $rootfs/bin \
54 $rootfs/usr/bin \
55 $rootfs/sbin \
56 $rootfs/usr/sbin \
57 $rootfs/proc \
58 $rootfs/sys \
59 $rootfs/mnt \
60 $rootfs/tmp \
61 $rootfs/var/log \
62 $rootfs/usr/share/udhcpc \
63 $rootfs/dev/pts \
64 $rootfs/dev/shm \
65 $rootfs/lib \
66 $rootfs/usr/lib \
67 $rootfs/lib64 \
68 $rootfs/usr/lib64"
69
70 mkdir -p $tree || return 1
71 chmod 755 $tree || return 1
72
73 pushd $rootfs/dev > /dev/null || return 1
74
75 # minimal devices needed for busybox
76 if [ $in_userns -eq 1 ]; then
77 for dev in tty console tty0 tty1 ram0 null urandom; do
78 echo "lxc.mount.entry = /dev/$dev dev/$dev none bind,optional,create=file 0 0" >> $path/config
79 done
80 else
81 mknod -m 666 tty c 5 0 || res=1
82 mknod -m 666 console c 5 1 || res=1
83 mknod -m 666 tty0 c 4 0 || res=1
84 mknod -m 666 tty1 c 4 0 || res=1
85 mknod -m 666 tty5 c 4 0 || res=1
86 mknod -m 600 ram0 b 1 0 || res=1
87 mknod -m 666 null c 1 3 || res=1
88 mknod -m 666 zero c 1 5 || res=1
89 mknod -m 666 urandom c 1 9 || res=1
90 fi
91
92 popd > /dev/null
93
94 # root user defined
95 cat <<EOF >> $rootfs/etc/passwd
96 root:x:0:0:root:/root:/bin/sh
97 EOF
98
99 cat <<EOF >> $rootfs/etc/group
100 root:x:0:root
101 EOF
102
103 # mount everything
104 cat <<EOF >> $rootfs/etc/init.d/rcS
105 #!/bin/sh
106 /bin/syslogd
107 /bin/mount -a
108 /bin/udhcpc
109 EOF
110
111 # executable
112 chmod 744 $rootfs/etc/init.d/rcS || return 1
113
114 # launch rcS first then make a console available
115 # and propose a shell on the tty, the last one is
116 # not needed
117 cat <<EOF >> $rootfs/etc/inittab
118 ::sysinit:/etc/init.d/rcS
119 tty1::respawn:/bin/getty -L tty1 115200 vt100
120 console::askfirst:/bin/sh
121 EOF
122 # writable and readable for other
123 chmod 644 $rootfs/etc/inittab || return 1
124
125 cat <<EOF >> $rootfs/usr/share/udhcpc/default.script
126 #!/bin/sh
127 case "\$1" in
128 deconfig)
129 ip addr flush dev \$interface
130 ;;
131
132 renew|bound)
133 # flush all the routes
134 if [ -n "\$router" ]; then
135 ip route del default 2> /dev/null
136 fi
137
138 # check broadcast
139 if [ -n "\$broadcast" ]; then
140 broadcast="broadcast \$broadcast"
141 fi
142
143 # add a new ip address
144 ip addr add \$ip/\$mask \$broadcast dev \$interface
145
146 if [ -n "\$router" ]; then
147 ip route add default via \$router dev \$interface
148 fi
149
150 [ -n "\$domain" ] && echo search \$domain > /etc/resolv.conf
151 for i in \$dns ; do
152 echo nameserver \$i >> /etc/resolv.conf
153 done
154 ;;
155 esac
156 exit 0
157 EOF
158
159 chmod 744 $rootfs/usr/share/udhcpc/default.script
160
161 return $res
162 }
163
164 install_dropbear()
165 {
166 # copy dropbear binary
167 cp $(which dropbear) $rootfs/usr/sbin
168 if [ $? -ne 0 ]; then
169 echo "Failed to copy dropbear in the rootfs"
170 return 1
171 fi
172
173 # make symlinks to various ssh utilities
174 utils="\
175 $rootfs/usr/bin/dbclient \
176 $rootfs/usr/bin/scp \
177 $rootfs/usr/bin/ssh \
178 $rootfs/usr/sbin/dropbearkey \
179 $rootfs/usr/sbin/dropbearconvert \
180 "
181 echo $utils | xargs -n1 ln -s /usr/sbin/dropbear
182
183 # add necessary config files
184 mkdir $rootfs/etc/dropbear
185 dropbearkey -t rsa -f $rootfs/etc/dropbear/dropbear_rsa_host_key > /dev/null 2>&1
186 dropbearkey -t dss -f $rootfs/etc/dropbear/dropbear_dss_host_key > /dev/null 2>&1
187
188 echo "'dropbear' ssh utility installed"
189
190 return 0
191 }
192
193 install_openssh()
194 {
195 # tools to be installed
196 server_utils="sshd"
197 client_utils="\
198 ssh \
199 scp \
200 sftp \
201 ssh-add \
202 ssh-agent \
203 ssh-keygen \
204 ssh-keyscan \
205 ssh-argv0 \
206 ssh-copy-id \
207 "
208
209 # new folders used by ssh
210 ssh_tree="\
211 $rootfs/etc/ssh \
212 $rootfs/var/empty/sshd \
213 $rootfs/var/lib/empty/sshd \
214 $rootfs/var/run/sshd \
215 "
216
217 # create folder structure
218 mkdir -p $ssh_tree
219 if [ $? -ne 0 ]; then
220 return 1
221 fi
222
223 # copy binaries
224 for bin in $server_utils $client_utils; do
225 tool_path=`which $bin`
226 cp $tool_path $rootfs/$tool_path
227 if [ $? -ne 0 ]; then
228 echo "Unable to copy $tool_path in the rootfs"
229 return 1
230 fi
231 done
232
233 # add user and group
234 cat <<EOF >> $rootfs/etc/passwd
235 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
236 EOF
237
238 cat <<EOF >> $rootfs/etc/group
239 sshd:x:74:
240 EOF
241
242 # generate container keys
243 ssh-keygen -t rsa -N "" -f $rootfs/etc/ssh/ssh_host_rsa_key >/dev/null 2>&1
244 ssh-keygen -t dsa -N "" -f $rootfs/etc/ssh/ssh_host_dsa_key >/dev/null 2>&1
245
246 # by default setup root password with no password
247 cat <<EOF > $rootfs/etc/ssh/sshd_config
248 Port 22
249 Protocol 2
250 HostKey /etc/ssh/ssh_host_rsa_key
251 HostKey /etc/ssh/ssh_host_dsa_key
252 UsePrivilegeSeparation yes
253 KeyRegenerationInterval 3600
254 ServerKeyBits 768
255 SyslogFacility AUTH
256 LogLevel INFO
257 LoginGraceTime 120
258 PermitRootLogin yes
259 StrictModes yes
260 RSAAuthentication yes
261 PubkeyAuthentication yes
262 IgnoreRhosts yes
263 RhostsRSAAuthentication no
264 HostbasedAuthentication no
265 PermitEmptyPasswords yes
266 ChallengeResponseAuthentication no
267 EOF
268
269 echo "'OpenSSH' utility installed"
270
271 return 0
272 }
273
274 configure_busybox()
275 {
276 rootfs=$1
277
278 which busybox >/dev/null 2>&1
279
280 if [ $? -ne 0 ]; then
281 echo "busybox executable is not accessible"
282 return 1
283 fi
284
285 file -L $(which busybox) | grep -q "statically linked"
286 if [ $? -ne 0 ]; then
287 echo "warning : busybox is not statically linked."
288 echo "warning : The template script may not correctly"
289 echo "warning : setup the container environment."
290 fi
291
292 # copy busybox in the rootfs
293 cp $(which busybox) $rootfs/bin
294 if [ $? -ne 0 ]; then
295 echo "failed to copy busybox in the rootfs"
296 return 1
297 fi
298
299 # symlink busybox for the commands it supports
300 # it would be nice to just use "chroot $rootfs busybox --install -s /bin"
301 # but that only works right in a chroot with busybox >= 1.19.0
302 pushd $rootfs/bin > /dev/null || return 1
303 ./busybox --help | grep 'Currently defined functions:' -A300 | \
304 grep -v 'Currently defined functions:' | tr , '\n' | \
305 xargs -n1 ln -s busybox
306 popd > /dev/null
307
308 # relink /sbin/init
309 ln $rootfs/bin/busybox $rootfs/sbin/init
310
311 # passwd exec must be setuid
312 chmod +s $rootfs/bin/passwd
313 touch $rootfs/etc/shadow
314
315 # setting passwd for root
316 CHPASSWD_FILE=$rootfs/root/chpasswd.sh
317
318 cat <<EOF >$CHPASSWD_FILE
319 echo "setting root password to \"root\""
320
321 mount -n --bind /lib $rootfs/lib
322 if [ \$? -ne 0 ]; then
323 echo "Failed bind-mounting /lib at $rootfs/lib"
324 exit 1
325 fi
326
327 chroot $rootfs chpasswd <<EOFF 2>/dev/null
328 root:root
329 EOFF
330
331
332 if [ \$? -ne 0 ]; then
333 echo "Failed to change root password"
334 exit 1
335 fi
336
337 umount $rootfs/lib
338
339 EOF
340
341 lxc-unshare -s MOUNT -- /bin/sh < $CHPASSWD_FILE
342 rm $CHPASSWD_FILE
343
344 return 0
345 }
346
347 copy_configuration()
348 {
349 path=$1
350 rootfs=$2
351 name=$3
352
353 grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config
354 cat <<EOF >> $path/config
355 lxc.haltsignal = SIGUSR1
356 lxc.rebootsignal = SIGTERM
357 lxc.utsname = $name
358 lxc.tty = 1
359 lxc.pts = 1
360 lxc.cap.drop = sys_module mac_admin mac_override sys_time
361
362 # When using LXC with apparmor, uncomment the next line to run unconfined:
363 #lxc.aa_profile = unconfined
364
365 lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed
366 lxc.mount.entry = shm /dev/shm tmpfs defaults 0 0
367 EOF
368
369 libdirs="\
370 lib \
371 usr/lib \
372 lib64 \
373 usr/lib64"
374
375 for dir in $libdirs; do
376 if [ -d "/$dir" ] && [ -d "$rootfs/$dir" ]; then
377 echo "lxc.mount.entry = /$dir $dir none ro,bind 0 0" >> $path/config
378 fi
379 done
380 echo "lxc.mount.entry = /sys/kernel/security sys/kernel/security none ro,bind,optional 0 0" >>$path/config
381 }
382
383 remap_userns()
384 {
385 path=$1
386
387 if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
388 chown $LXC_MAPPED_UID $path/config >/dev/null 2>&1
389 chown -R root $path/rootfs >/dev/null 2>&1
390 fi
391
392 if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then
393 chgrp $LXC_MAPPED_GID $path/config >/dev/null 2>&1
394 chgrp -R root $path/rootfs >/dev/null 2>&1
395 fi
396 }
397
398 usage()
399 {
400 cat <<EOF
401 $1 -h|--help -p|--path=<path> -s|--ssh={dropbear,openssh}
402 EOF
403 return 0
404 }
405
406 options=$(getopt -o hp:n:s: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid:,ssh: -- "$@")
407 if [ $? -ne 0 ]; then
408 usage $(basename $0)
409 exit 1
410 fi
411 eval set -- "$options"
412
413 while true
414 do
415 case "$1" in
416 -h|--help) usage $0 && exit 0;;
417 -p|--path) path=$2; shift 2;;
418 --rootfs) rootfs=$2; shift 2;;
419 -n|--name) name=$2; shift 2;;
420 --mapped-uid) LXC_MAPPED_UID=$2; shift 2;;
421 --mapped-gid) LXC_MAPPED_GID=$2; shift 2;;
422 -s|--ssh) SSH=$2; shift 2;;
423 --) shift 1; break ;;
424 *) break ;;
425 esac
426 done
427
428 if [ "$(id -u)" != "0" ]; then
429 echo "This script should be run as 'root'"
430 exit 1
431 fi
432
433 if [ -z "$path" ]; then
434 echo "'path' parameter is required"
435 exit 1
436 fi
437
438 # detect rootfs
439 config="$path/config"
440 if [ -z "$rootfs" ]; then
441 if grep -q '^lxc.rootfs' $config 2>/dev/null ; then
442 rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config)
443 else
444 rootfs=$path/rootfs
445 fi
446 fi
447
448 install_busybox $rootfs $name
449 if [ $? -ne 0 ]; then
450 echo "failed to install busybox's rootfs"
451 exit 1
452 fi
453
454 configure_busybox $rootfs
455 if [ $? -ne 0 ]; then
456 echo "failed to configure busybox template"
457 exit 1
458 fi
459
460 copy_configuration $path $rootfs $name
461 if [ $? -ne 0 ]; then
462 echo "failed to write configuration file"
463 exit 1
464 fi
465
466 remap_userns $path
467 if [ $? -ne 0 ]; then
468 echo "failed to remap files to user"
469 exit 1
470 fi
471
472 if [ -n "$SSH" ]; then
473 case "$SSH" in
474 "dropbear")
475 install_dropbear
476 if [ $? -ne 0 ]; then
477 echo "Unable to install 'dropbear' ssh utility"
478 exit 1
479 fi ;;
480 "openssh")
481 install_openssh
482 if [ $? -ne 0 ]; then
483 echo "Unable to install 'OpenSSH' utility"
484 exit 1
485 fi ;;
486 *)
487 echo "$SSH: unrecognized ssh utility"
488 exit 1
489 esac
490 else
491 which dropbear >/dev/null 2>&1
492 if [ $? -eq 0 ]; then
493 install_dropbear
494 fi
495 fi