]> git.sommitrealweird.co.uk Git - lxc-debian-unprivileged.git/blob - templates/lxc-debian-unprivileged
dd7bdafc11e5e862f594dc8378e7697a24836a29
[lxc-debian-unprivileged.git] / templates / lxc-debian-unprivileged
1 #!/bin/bash
2
3 # lxc template for debootstrapping in userns
4
5 # Authors:
6 # Brett Parker <iDunno@sommitrealweird.co.uk>
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.
12
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.
17
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 USA
21
22 MAPPED=no
23
24 # Only support usage in userns.
25 for arg in "$@"; do
26     [ "$arg" = "--" ] && break
27     if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then
28         MAPPED=yes
29     fi
30 done
31
32 if [ "$MAPPED" == "no" ]; then
33     echo "This template can only be used for unprivileged containers." 1>&2
34     echo "You might want the \"debian\" template instead." 1>&2
35     exit 1
36 fi
37
38 set -e
39 set -u
40
41 # Make sure the usual locations are in PATH
42 export PATH=/usr/sbin:/usr/bin:/sbin:/bin:$PATH
43 export GREP_OPTIONS=""
44
45 usage() {
46     cat <<EOF
47 LXC debootstrap in user namespace for unprivileged containers
48
49 Special arguments:
50 [ -h | --help ]: Print this help message and exit.
51
52 Required arguments:
53 [ -r | --release <release> ]: The debian release, e.g. jessie or stretch.
54
55 Optional arguments:
56 [ -m | --mirror <mirrorurl> ]: The debian mirror to user
57 [ -n | --network <networkspec> ]: How to configure networking
58
59 Network spec:
60   <type>[,options]
61
62   type is one of:
63     dhcp4: v4 dhcp will be enabled.
64     dhcp6: v6 dhcp will be enabled.
65     dhcp: v4 and v6 dhcp will be enabled.
66     static: no dhcp will be enabled
67
68   options:
69     this is a , seperated list, and sets up static assignments for v4 or v6,
70     regardless of type.
71     The order of arguments is
72       staticv4/staticnetmask
73       staticv6/staticv6netmask
74       gateway
75       v6gateway
76
77   examples:
78
79     --network dhcp4 (default)
80     --network dhcp6 (v6 dhcp)
81     --network dhcp  (v4 and v6 dhcp)
82     --network dhcp4,,2001:db8:1234:5678::1/64 (dhcp4 and static v6 address)
83     --network static,,2001:db8:1234:5678::5/64,,fe80::1 (static v6)
84     --network static,192.0.2.15/24,,192.0.2.1 (static v4)
85
86 EOF
87     return 0
88 }
89
90 options=$(getopt -o r:m:n:h -l release:,mirror:,network:,help,mapped-uid:,mapped-gid:,name:,path:,rootfs: -- "$@")
91
92 if [ $? -ne 0 ]; then
93     usage
94     exit 1
95 fi
96
97 eval set -- "$options"
98
99 DEBIAN_MIRROR="http://mirror.mythic-beasts.com/debian/"
100 DEBIAN_RELEASE="jessie"
101 NETWORK_CONFIG=""
102
103 disable_initscripts() {
104     cat <<EOF > "${LXC_ROOTFS}/usr/sbin/policy-rc.d"
105 #!/bin/sh
106
107 exit 101
108 EOF
109     chmod 755 "${LXC_ROOTFS}/usr/sbin/policy-rc.d"
110 }
111
112 enable_initscripts() {
113     if [ -e "${LXC_ROOTFS}/usr/sbin/policy-rc.d" ]; then
114         rm "${LXC_ROOTFS}/usr/sbin/policy-rc.d"
115     fi
116 }
117
118 while :; do
119     case "$1" in
120         -h|--help)      usage && exit 1;;
121         -r|--release)   DEBIAN_RELEASE="$2"; shift 2;;
122         -m|--mirror)    DEBIAN_MIRROR="$2"; shift 2;;
123         -n|--network)   NETWORK_CONFIG="$2"; shift 2;;
124         --mapped-uid)   MAPPED_UID="$2"; shift 2;;
125         --mapped-gid)   MAPPED_GID="$2"; shift 2;;
126         --name)         LXC_NAME="$2"; shift 2;;
127         --path)         LXC_PATH="$2"; shift 2;;
128         --rootfs)       LXC_ROOTFS="$2"; shift 2;;
129         *)              break;;
130     esac
131 done
132
133 INTERFACE_DEFAULTS="auto eth0
134 iface eth0 inet dhcp"
135
136 generate_network_config() {
137     if [ "$NETWORK_CONFIG" == "" ]; then
138         echo "$INTERFACE_DEFAULTS"
139         return 0
140     fi
141
142     ETH0_HEADER="auto eth0"
143     ETH0_IPV4=""
144     ETH0_IPV6=""
145
146     echo "auto eth0"
147     # see if there's a type
148     network_type=${NETWORK_CONFIG/,*}
149     other_params=${NETWORK_CONFIG#*,}
150
151     v4_configured=no
152     v6_configured=no
153
154     if [ "$network_type" == "dhcp" ]; then
155         ETH0_IPV4="iface eth0 inet dhcp"
156         ETH0_IPV6="iface eth0 inet6 dhcp"
157         v4_configured=yes
158         v6_configured=yes
159     elif [ "$network_type" == "dhcp4" ]; then
160         ETH0_IPV4="iface eth0 inet dhcp"
161         v4_configured=yes
162     elif [ "$network_type" == "dhcp6" ]; then
163         ETH0_IPV6="iface eth0 inet6 dhcp"
164         v6_configured=yes
165     elif [ "$network_type" != "static" ]; then
166         echo "Unknown network type: $network_type" 1>&2
167         echo 1>&2
168         usage 1>&2
169         exit 1
170     fi
171
172     [ "$network_type" == "$other_params" ] && return 0
173
174     v4_static=${other_params/,*}
175     other_params=${other_params#*,}
176
177     if [ "$v4_static" != "" ]; then
178         if [ "$v4_configured" == "yes" ]; then
179             echo "Both v4 DHCP and Static - giving up." 1>&2
180             echo 1>&2
181             usage 1>&2
182             exit 1
183         fi
184     fi
185
186     if [ "$v4_static" == "$other_params" ]; then
187         if [ "$v4_static" != "" ]; then
188             echo "iface eth0 inet static"
189             echo "  address $v4_static"
190         fi
191     fi
192
193     v6_static=${other_params/,*}
194     other_params=${other_params#*,}
195
196     if [ "$v6_static" != "" ]; then
197         if [ "$v6_configured" == "yes" ]; then
198             echo "Both v6 DHCP and Static - giving up." 1>&2
199             echo 1>&2
200             usage 1>&2
201             exit 1
202         fi
203     fi
204
205     if [ "$v6_static" == "$other_params" ]; then
206         if [ "$v4_static" ]; then
207             echo "iface eth0 inet static"
208             echo "  address $v4_static"
209             echo
210         fi
211
212         echo "iface eth0 inet6 static"
213         echo "  address $v6_static"
214
215         return 0
216     fi
217
218     v4_gateway=${other_params/,*}
219     other_params=${other_params#*,}
220
221     if [ "$v4_gateway" == "$other_params" ]; then
222         if [ "$v4_static" != "" ]; then
223             echo "iface eth0 inet static"
224             echo "  address $v4_static"
225             [ "$v4_gateway" != "" ] && echo "  gateway $v4_gateway"
226
227             if [ "$v6_static" != "" ]; then
228                 echo "iface eth0 inet6 static"
229                 echo "  address $v6_static"
230             fi
231
232             return 0
233         fi
234
235         if [ "$v4_configured" == "yes" ]; then
236             echo "DHCP and static gateway not supported, giving up." 1>&2
237             echo 1>&2
238             usage 1>&2
239             exit 1
240         fi
241     fi
242
243     v6_gateway=${other_params/,*}
244
245     if [ "$v4_static" != "" ]; then
246         echo "iface eth0 inet static"
247         echo "  address $v4_static"
248         [ "$v4_gateway" != "" ] && echo "  gateway $v4_gateway"
249         echo
250     fi
251
252     if [ "$v6_static" != "" ]; then
253         echo "iface eth0 inet6 static"
254         echo "  address $v6_static"
255         [ "$v6_gateway" != "" ] && echo "  gateway $v6_gateway"
256     fi
257
258     return 0
259 }
260
261 INTERFACE_DETAILS="$(generate_network_config)"
262
263 # rewrite the default config file
264
265 sed -i -e "/lxc./{w ${LXC_PATH}/config-auto" -e "d}" "${LXC_PATH}/config"
266 sed -i -e '4,$d' "${LXC_PATH}/config"
267
268 cat <<EOF >> "${LXC_PATH}/config"
269
270 # Useful includes
271 lxc.include = /usr/share/lxc/config/debian.common.conf
272 lxc.include = /usr/share/lxc/config/debian.userns.conf
273
274 # Set our hostname
275 lxc.utsname = $LXC_NAME
276
277 # Automatic configuration
278 EOF
279
280 # add back in the auto foo
281 cat "${LXC_PATH}/config-auto" >> "${LXC_PATH}/config"
282 rm "${LXC_PATH}/config-auto"
283
284 mkdir "${LXC_PATH}/bin"
285 cat <<EOF > "${LXC_PATH}/bin/mknod"
286 #!/bin/sh
287
288 # look for the first argument that looks like a path
289 for i do
290     case \$i in
291         /*)
292             exec touch "\$i"
293             ;;
294     esac
295 done
296
297 EOF
298
299 chmod 755 "${LXC_PATH}/bin/mknod"
300
301 export PATH="${LXC_PATH}/bin:$PATH"
302
303 debootstrap --foreign --include debian-archive-keyring,ifupdown,isc-dhcp-client,locales,openssh-server $DEBIAN_RELEASE "${LXC_ROOTFS}" $DEBIAN_MIRROR
304
305 echo "DEBOOTSTRAP STAGE 1 COMPLETE"
306
307 # now totally skip that check in the new root, because it sucks.
308 sed -i -e 's#check_sane_mount () {#check_sane_mount () {\n\treturn 0#;' "${LXC_ROOTFS}/debootstrap/functions"
309
310 # and stop it from bothering to try to setup proc
311 sed -i -e 's#setup_proc () {#setup_proc () {\n\treturn 0#;' "${LXC_ROOTFS}/debootstrap/functions"
312
313 keyring_dpkg=$(sed -ne "/^debian-archive-keyring/ { s#.* ##; p; }" "${LXC_ROOTFS}/debootstrap/debpaths")
314 # and unpack debian-archive-keyring, because we'll need that
315 (cd "${LXC_ROOTFS}" && dpkg-deb -x ".$keyring_dpkg" .)
316
317 # replace the tar containing devices with something that doesn't contain any
318 if [ -e "$LXC_ROOTFS/debootstrap/devices.tar.gz" ]; then
319     (cd "$LXC_ROOTFS/debootstrap" && rm devices.tar.gz && tar czvf devices.tar.gz --files-from=/dev/null)
320 fi
321
322 # and mount a shitload of things for fun and profit...
323 for file in /var/lib/lxcfs/proc/*; do
324     fname="$(basename $file)"
325     touch "${LXC_ROOTFS}/proc/$fname"
326     mount -n -o bind "$file" "${LXC_ROOTFS}/proc/$fname"
327 done
328
329 for dev in null random urandom; do
330     touch "${LXC_ROOTFS}/dev/$dev"
331     mount -n -o bind /dev/$dev "${LXC_ROOTFS}/dev/$dev"
332 done
333
334 # set /proc/cmdline to something
335 echo "debootstrapping" > "${LXC_ROOTFS}/proc/cmdline"
336
337 # and disable initscripts
338 disable_initscripts
339
340 # and run the second stage
341 chroot "${LXC_ROOTFS}" /debootstrap/debootstrap --second-stage
342
343 # make sure that initscripts are still disabled
344 disable_initscripts
345
346 # configure locales
347 lang=en_GB.UTF-8
348 enc=UTF-8
349 if [ ! -z "$LANG" ]; then
350     lang=${LANG}
351     enc=${LANG#*.}
352 fi
353
354 cat >> "${LXC_ROOTFS}/etc/locale.gen" <<EOF
355 $lang $enc
356 EOF
357
358 chroot "${LXC_ROOTFS}" /usr/sbin/locale-gen $lang $enc
359 chroot "${LXC_ROOTFS}" /usr/sbin/update-locale LANG=$LANG
360
361 # configure timezone
362 if [ -f /etc/timezone ]; then
363     cat /etc/timezone > "${LXC_ROOTFS}/etc/timezone"
364 elif [ -f /etc/sysconfig/clock ]; then
365     . /etc/sysconfig/clock
366     echo $ZONE > "${LXC_ROOTFS}/etc/timezone"
367 fi
368 chroot "${LXC_ROOTFS}" dpkg-reconfigure -f noninteractive tzdata
369
370 # "setup" networking
371 NETWORK_FILE=/etc/network/interfaces
372 if [ -e "${LXC_ROOTFS}/etc/network/interfaces.d" ]; then
373     NETWORK_FILE=/etc/network/interfaces.d/eth0
374 fi
375
376 # remove some interesting breakages in pam for unpriv foo
377 sed -i -e 's#^\(session.*required.*pam_loginuid.so\)#\#\1#;' "${LXC_ROOTFS}"/etc/pam.d/*
378
379 # set the hostname
380 echo $LXC_NAME > "${LXC_ROOTFS}/etc/hostname"
381
382 SECURITY=""
383 if [ "$DEBIAN_RELEASE" != "sid" ] && [ "$DEBIAN_RELEASE" != "unstable" ]; then
384     SECURITY="deb http://security.debian.org/ $DEBIAN_RELEASE/updates main"
385 fi
386
387 # setup sources.list
388 cat <<EOF > "${LXC_ROOTFS}/etc/apt/sources.list"
389 deb $DEBIAN_MIRROR $DEBIAN_RELEASE main
390 $SECURITY
391 EOF
392
393 # disable bits of systemd / initrd that break things
394 chroot "${LXC_ROOTFS}" /usr/sbin/update-rc.d -f checkroot.sh disable > /dev/null 2>&1 || true
395 chroot "${LXC_ROOTFS}" /usr/sbin/update-rc.d -f umountfs disable > /dev/null 2>&1 || true
396 chroot "${LXC_ROOTFS}" /usr/sbin/update-rc.d -f hwclock.sh disable > /dev/null 2>&1 || true
397 chroot "${LXC_ROOTFS}" /usr/sbin/update-rc.d -f hwclockfirst.sh disable > /dev/null 2>&1 || true
398
399 if [ -e "${LXC_ROOTFS}/etc/systemd/system/" ]; then
400     touch "${LXC_ROOTFS}/etc/systemd/system/systemd-setup-dgram-qlen.service"
401     touch "${LXC_ROOTFS}/etc/systemd/system/dev-hugepages.mount"
402     touch "${LXC_ROOTFS}/etc/systemd/system/udev.service"
403     touch "${LXC_ROOTFS}/etc/systemd/system/systemd-udevd.service"
404     chroot "${LXC_ROOTFS}" systemctl set-default multi-user.target
405     chroot "${LXC_ROOTFS}" ln -s /lib/systemd/system/halt.target /etc/systemd/system/sigpwr.target
406 fi
407
408 if [ -e "${LXC_ROOTFS}/lib/systemd/system/systemd-journald-audit.socket" ]; then
409     touch "${LXC_ROOTFS}/etc/systemd/system/systemd-journald-audit.socket"
410 fi
411
412 echo "$INTERFACE_DETAILS" >> "${LXC_ROOTFS}${NETWORK_FILE}"
413
414 # and update to the latest security
415 chroot "${LXC_ROOTFS}" apt-get update
416 chroot "${LXC_ROOTFS}" apt-get -y upgrade
417
418 # if we're all good here, unmount things and clean up
419 [ -e "${LXC_ROOTFS}/usr/sbin/policy-rc.d" ] && rm "${LXC_ROOTFS}/usr/sbin/policy-rc.d"
420 rm "${LXC_ROOTFS}/proc/cmdline"
421
422 for dev in null random urandom; do
423     umount "${LXC_ROOTFS}/dev/$dev"
424     rm "${LXC_ROOTFS}/dev/$dev"
425 done
426
427 for file in /var/lib/lxcfs/proc/*; do
428     fname="$(basename $file)"
429     umount "${LXC_ROOTFS}/proc/$fname"
430     rm "${LXC_ROOTFS}/proc/$fname"
431 done
432
433 enable_initscripts
434
435 rm -r "${LXC_PATH}/bin"
436
437 cat <<EOF
438
439 You have successfully created a new debian container, ${LXC_NAME} running ${DEBIAN_RELEASE}.
440
441 You should start the new container, and use:
442
443   lxc-attach -n ${LXC_NAME} -- su -
444
445 To create a user account / set the root password.
446
447 Note, by default, it's likely only to be the console that can login as root, so that'd be:
448
449   lxc-console -n ${LXC_NAME} -t 0
450
451 EOF
452
453 exit 0