3 # lxc template for debootstrapping in userns
6 # Brett Parker <iDunno@sommitrealweird.co.uk>
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.
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.
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
24 # Only support usage in userns.
26 [ "$arg" = "--" ] && break
27 if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then
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
41 # Make sure the usual locations are in PATH
42 export PATH=/usr/sbin:/usr/bin:/sbin:/bin:$PATH
43 export GREP_OPTIONS=""
47 LXC debootstrap in user namespace for unprivileged containers
50 [ -h | --help ]: Print this help message and exit.
53 [ -r | --release <release> ]: The debian release, e.g. jessie or stretch.
56 [ -m | --mirror <mirrorurl> ]: The debian mirror to user
57 [ -n | --network <networkspec> ]: How to configure networking
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
69 this is a , seperated list, and sets up static assignments for v4 or v6,
71 The order of arguments is
72 staticv4/staticnetmask
73 staticv6/staticv6netmask
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)
90 options=$(getopt -o r:m:n:h -l release:,mirror:,network:,help,mapped-uid:,mapped-gid:,name:,path:,rootfs: -- "$@")
97 eval set -- "$options"
99 DEBIAN_MIRROR="http://mirror.mythic-beasts.com/debian/"
100 DEBIAN_RELEASE="jessie"
103 disable_initscripts() {
104 cat <<EOF > "${LXC_ROOTFS}/usr/sbin/policy-rc.d"
109 chmod 755 "${LXC_ROOTFS}/usr/sbin/policy-rc.d"
112 enable_initscripts() {
113 if [ -e "${LXC_ROOTFS}/usr/sbin/policy-rc.d" ]; then
114 rm "${LXC_ROOTFS}/usr/sbin/policy-rc.d"
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;;
133 INTERFACE_DEFAULTS="auto eth0
134 iface eth0 inet dhcp"
136 generate_network_config() {
137 if [ "$NETWORK_CONFIG" == "" ]; then
138 echo "$INTERFACE_DEFAULTS"
142 ETH0_HEADER="auto eth0"
147 # see if there's a type
148 network_type=${NETWORK_CONFIG/,*}
149 other_params=${NETWORK_CONFIG#*,}
154 if [ "$network_type" == "dhcp" ]; then
155 ETH0_IPV4="iface eth0 inet dhcp"
156 ETH0_IPV6="iface eth0 inet6 dhcp"
159 elif [ "$network_type" == "dhcp4" ]; then
160 ETH0_IPV4="iface eth0 inet dhcp"
162 elif [ "$network_type" == "dhcp6" ]; then
163 ETH0_IPV6="iface eth0 inet6 dhcp"
165 elif [ "$network_type" != "static" ]; then
166 echo "Unknown network type: $network_type" 1>&2
172 [ "$network_type" == "$other_params" ] && return 0
174 v4_static=${other_params/,*}
175 other_params=${other_params#*,}
177 if [ "$v4_static" != "" ]; then
178 if [ "$v4_configured" == "yes" ]; then
179 echo "Both v4 DHCP and Static - giving up." 1>&2
186 if [ "$v4_static" == "$other_params" ]; then
187 if [ "$v4_static" != "" ]; then
188 echo "iface eth0 inet static"
189 echo " address $v4_static"
193 v6_static=${other_params/,*}
194 other_params=${other_params#*,}
196 if [ "$v6_static" != "" ]; then
197 if [ "$v6_configured" == "yes" ]; then
198 echo "Both v6 DHCP and Static - giving up." 1>&2
205 if [ "$v6_static" == "$other_params" ]; then
206 if [ "$v4_static" ]; then
207 echo "iface eth0 inet static"
208 echo " address $v4_static"
212 echo "iface eth0 inet6 static"
213 echo " address $v6_static"
218 v4_gateway=${other_params/,*}
219 other_params=${other_params#*,}
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"
227 if [ "$v6_static" != "" ]; then
228 echo "iface eth0 inet6 static"
229 echo " address $v6_static"
235 if [ "$v4_configured" == "yes" ]; then
236 echo "DHCP and static gateway not supported, giving up." 1>&2
243 v6_gateway=${other_params/,*}
245 if [ "$v4_static" != "" ]; then
246 echo "iface eth0 inet static"
247 echo " address $v4_static"
248 [ "$v4_gateway" != "" ] && echo " gateway $v4_gateway"
252 if [ "$v6_static" != "" ]; then
253 echo "iface eth0 inet6 static"
254 echo " address $v6_static"
255 [ "$v6_gateway" != "" ] && echo " gateway $v6_gateway"
261 INTERFACE_DETAILS="$(generate_network_config)"
263 # rewrite the default config file
265 sed -i -e "/lxc./{w ${LXC_PATH}/config-auto" -e "d}" "${LXC_PATH}/config"
266 sed -i -e '4,$d' "${LXC_PATH}/config"
268 cat <<EOF >> "${LXC_PATH}/config"
271 lxc.include = /usr/share/lxc/config/debian.common.conf
272 lxc.include = /usr/share/lxc/config/debian.userns.conf
275 lxc.utsname = $LXC_NAME
277 # Automatic configuration
280 # add back in the auto foo
281 cat "${LXC_PATH}/config-auto" >> "${LXC_PATH}/config"
282 rm "${LXC_PATH}/config-auto"
284 mkdir "${LXC_PATH}/bin"
285 cat <<EOF > "${LXC_PATH}/bin/mknod"
288 # look for the first argument that looks like a path
299 chmod 755 "${LXC_PATH}/bin/mknod"
301 export PATH="${LXC_PATH}/bin:$PATH"
303 debootstrap --foreign --include debian-archive-keyring,ifupdown,isc-dhcp-client,locales,openssh-server $DEBIAN_RELEASE "${LXC_ROOTFS}" $DEBIAN_MIRROR
305 echo "DEBOOTSTRAP STAGE 1 COMPLETE"
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"
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"
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" .)
317 # replace the tar containing devices with something that doesn't contain any
318 (cd "$LXC_ROOTFS/debootstrap" && rm devices.tar.gz && tar czvf devices.tar.gz --files-from=/dev/null)
320 # and mount a shitload of things for fun and profit...
321 for file in /var/lib/lxcfs/proc/*; do
322 fname="$(basename $file)"
323 touch "${LXC_ROOTFS}/proc/$fname"
324 mount -n -o bind "$file" "${LXC_ROOTFS}/proc/$fname"
327 for dev in null random urandom; do
328 touch "${LXC_ROOTFS}/dev/$dev"
329 mount -n -o bind /dev/$dev "${LXC_ROOTFS}/dev/$dev"
332 # set /proc/cmdline to something
333 echo "debootstrapping" > "${LXC_ROOTFS}/proc/cmdline"
335 # and disable initscripts
338 # and run the second stage
339 chroot "${LXC_ROOTFS}" /debootstrap/debootstrap --second-stage
341 # make sure that initscripts are still disabled
347 if [ ! -z "$LANG" ]; then
352 cat >> "${LXC_ROOTFS}/etc/locale.gen" <<EOF
356 chroot "${LXC_ROOTFS}" /usr/sbin/locale-gen $lang $enc
357 chroot "${LXC_ROOTFS}" /usr/sbin/update-locale LANG=$LANG
360 if [ -f /etc/timezone ]; then
361 cat /etc/timezone > "${LXC_ROOTFS}/etc/timezone"
362 elif [ -f /etc/sysconfig/clock ]; then
363 . /etc/sysconfig/clock
364 echo $ZONE > "${LXC_ROOTFS}/etc/timezone"
366 chroot "${LXC_ROOTFS}" dpkg-reconfigure -f noninteractive tzdata
369 NETWORK_FILE=/etc/network/interfaces
370 if [ -e "${LXC_ROOTFS}/etc/network/interfaces.d" ]; then
371 NETWORK_FILE=/etc/network/interfaces.d/eth0
374 # remove some interesting breakages in pam for unpriv foo
375 sed -i -e 's#^\(session.*required.*pam_loginuid.so\)#\#\1#;' "${LXC_ROOTFS}"/etc/pam.d/*
378 echo $LXC_NAME > "${LXC_ROOTFS}/etc/hostname"
381 if [ "$DEBIAN_RELEASE" != "sid" ] && [ "$DEBIAN_RELEASE" != "unstable" ]; then
382 SECURITY="deb http://security.debian.org/ $DEBIAN_RELEASE/updates main"
386 cat <<EOF > "${LXC_ROOTFS}/etc/apt/sources.list"
387 deb $DEBIAN_MIRROR $DEBIAN_RELEASE main
391 # disable bits of systemd / initrd that break things
392 chroot "${LXC_ROOTFS}" /usr/sbin/update-rc.d -f checkroot.sh disable > /dev/null 2>&1 || true
393 chroot "${LXC_ROOTFS}" /usr/sbin/update-rc.d -f umountfs disable > /dev/null 2>&1 || true
394 chroot "${LXC_ROOTFS}" /usr/sbin/update-rc.d -f hwclock.sh disable > /dev/null 2>&1 || true
395 chroot "${LXC_ROOTFS}" /usr/sbin/update-rc.d -f hwclockfirst.sh disable > /dev/null 2>&1 || true
397 if [ -e "${LXC_ROOTFS}/etc/systemd/system/" ]; then
398 touch "${LXC_ROOTFS}/etc/systemd/system/systemd-setup-dgram-qlen.service"
399 touch "${LXC_ROOTFS}/etc/systemd/system/dev-hugepages.mount"
400 touch "${LXC_ROOTFS}/etc/systemd/system/udev.service"
401 touch "${LXC_ROOTFS}/etc/systemd/system/systemd-udevd.service"
402 chroot "${LXC_ROOTFS}" systemctl set-default multi-user.target
403 chroot "${LXC_ROOTFS}" ln -s /lib/systemd/system/halt.target /etc/systemd/system/sigpwr.target
406 if [ -e "${LXC_ROOTFS}/lib/systemd/system/systemd-journald-audit.socket" ]; then
407 touch "${LXC_ROOTFS}/etc/systemd/system/systemd-journald-audit.socket"
410 echo "$INTERFACE_DETAILS" >> "${LXC_ROOTFS}${NETWORK_FILE}"
412 # and update to the latest security
413 chroot "${LXC_ROOTFS}" apt-get update
414 chroot "${LXC_ROOTFS}" apt-get -y upgrade
416 # if we're all good here, unmount things and clean up
417 [ -e "${LXC_ROOTFS}/usr/sbin/policy-rc.d" ] && rm "${LXC_ROOTFS}/usr/sbin/policy-rc.d"
418 rm "${LXC_ROOTFS}/proc/cmdline"
420 for dev in null random urandom; do
421 umount "${LXC_ROOTFS}/dev/$dev"
422 rm "${LXC_ROOTFS}/dev/$dev"
425 for file in /var/lib/lxcfs/proc/*; do
426 fname="$(basename $file)"
427 umount "${LXC_ROOTFS}/proc/$fname"
428 rm "${LXC_ROOTFS}/proc/$fname"
433 rm -r "${LXC_PATH}/bin"
437 You have successfully created a new debian container, ${LXC_NAME} running ${DEBIAN_RELEASE}.
439 You should start the new container, and use:
441 lxc-attach -n ${LXC_NAME} -- su -
443 To create a user account / set the root password.
445 Note, by default, it's likely only to be the console that can login as root, so that'd be:
447 lxc-console -n ${LXC_NAME} -t 0