#!/bin/bash # lxc template for debootstrapping in userns # Authors: # Brett Parker # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA MAPPED=no # Only support usage in userns. for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then MAPPED=yes fi done if [ "$MAPPED" == "no" ]; then echo "This template can only be used for unprivileged containers." 1>&2 echo "You might want the \"debian\" template instead." 1>&2 exit 1 fi set -e set -u # Make sure the usual locations are in PATH export PATH=/usr/sbin:/usr/bin:/sbin:/bin:$PATH export GREP_OPTIONS="" usage() { cat < ]: The debian release, e.g. jessie or stretch. Optional arguments: [ -m | --mirror ]: The debian mirror to user [ -n | --network ]: How to configure networking Network spec: [,options] type is one of: dhcp4: v4 dhcp will be enabled. dhcp6: v6 dhcp will be enabled. dhcp: v4 and v6 dhcp will be enabled. static: no dhcp will be enabled options: this is a , seperated list, and sets up static assignments for v4 or v6, regardless of type. The order of arguments is staticv4/staticnetmask staticv6/staticv6netmask gateway v6gateway examples: --network dhcp4 (default) --network dhcp6 (v6 dhcp) --network dhcp (v4 and v6 dhcp) --network dhcp4,,2001:db8:1234:5678::1/64 (dhcp4 and static v6 address) --network static,,2001:db8:1234:5678::5/64,,fe80::1 (static v6) --network static,192.0.2.15/24,,192.0.2.1 (static v4) EOF return 0 } options=$(getopt -o r:m:n:h -l release:,mirror:,network:,help,mapped-uid:,mapped-gid:,name:,path:,rootfs: -- "$@") if [ $? -ne 0 ]; then usage exit 1 fi eval set -- "$options" DEBIAN_MIRROR_DEFAULT="http://mirror.sommitrealweird.co.uk/debian/" DEBIAN_MIRROR=$DEBIAN_MIRROR_DEFAULT DEBIAN_RELEASE="jessie" NETWORK_CONFIG="" disable_initscripts() { cat < "${LXC_ROOTFS}/usr/sbin/policy-rc.d" #!/bin/sh exit 101 EOF chmod 755 "${LXC_ROOTFS}/usr/sbin/policy-rc.d" } enable_initscripts() { if [ -e "${LXC_ROOTFS}/usr/sbin/policy-rc.d" ]; then rm "${LXC_ROOTFS}/usr/sbin/policy-rc.d" fi } while :; do case "$1" in -h|--help) usage && exit 1;; -r|--release) DEBIAN_RELEASE="$2"; shift 2;; -m|--mirror) DEBIAN_MIRROR="$2"; shift 2;; -n|--network) NETWORK_CONFIG="$2"; shift 2;; --mapped-uid) MAPPED_UID="$2"; shift 2;; --mapped-gid) MAPPED_GID="$2"; shift 2;; --name) LXC_NAME="$2"; shift 2;; --path) LXC_PATH="$2"; shift 2;; --rootfs) LXC_ROOTFS="$2"; shift 2;; *) break;; esac done if [ "$DEBIAN_RELEASE" == "squeeze" ] || [ "$DEBIAN_RELEASE" == "lenny" ] || [ "$DEBIAN_RELEASE" == "etch" ]; then if [ "$DEBIAN_MIRROR" == "$DEBIAN_MIRROR_DEFAULT" ]; then DEBIAN_MIRROR="http://archive.debian.org/debian/" fi fi INTERFACE_DEFAULTS="auto eth0 iface eth0 inet dhcp" generate_network_config() { if [ "$NETWORK_CONFIG" == "" ]; then echo "$INTERFACE_DEFAULTS" return 0 fi ETH0_HEADER="auto eth0" ETH0_IPV4="" ETH0_IPV6="" echo "auto eth0" # see if there's a type network_type=${NETWORK_CONFIG/,*} other_params=${NETWORK_CONFIG#*,} v4_configured=no v6_configured=no if [ "$network_type" == "dhcp" ]; then ETH0_IPV4="iface eth0 inet dhcp" ETH0_IPV6="iface eth0 inet6 dhcp" v4_configured=yes v6_configured=yes elif [ "$network_type" == "dhcp4" ]; then ETH0_IPV4="iface eth0 inet dhcp" v4_configured=yes elif [ "$network_type" == "dhcp6" ]; then ETH0_IPV6="iface eth0 inet6 dhcp" v6_configured=yes elif [ "$network_type" != "static" ]; then echo "Unknown network type: $network_type" 1>&2 echo 1>&2 usage 1>&2 exit 1 fi [ "$network_type" == "$other_params" ] && return 0 v4_static=${other_params/,*} other_params=${other_params#*,} if [ "$v4_static" != "" ]; then if [ "$v4_configured" == "yes" ]; then echo "Both v4 DHCP and Static - giving up." 1>&2 echo 1>&2 usage 1>&2 exit 1 fi fi if [ "$v4_static" == "$other_params" ]; then if [ "$v4_static" != "" ]; then echo "iface eth0 inet static" echo " address $v4_static" fi fi v6_static=${other_params/,*} other_params=${other_params#*,} if [ "$v6_static" != "" ]; then if [ "$v6_configured" == "yes" ]; then echo "Both v6 DHCP and Static - giving up." 1>&2 echo 1>&2 usage 1>&2 exit 1 fi fi if [ "$v6_static" == "$other_params" ]; then if [ "$v4_static" ]; then echo "iface eth0 inet static" echo " address $v4_static" echo fi echo "iface eth0 inet6 static" echo " address $v6_static" return 0 fi v4_gateway=${other_params/,*} other_params=${other_params#*,} if [ "$v4_gateway" == "$other_params" ]; then if [ "$v4_static" != "" ]; then echo "iface eth0 inet static" echo " address $v4_static" [ "$v4_gateway" != "" ] && echo " gateway $v4_gateway" if [ "$v6_static" != "" ]; then echo "iface eth0 inet6 static" echo " address $v6_static" fi return 0 fi if [ "$v4_configured" == "yes" ]; then echo "DHCP and static gateway not supported, giving up." 1>&2 echo 1>&2 usage 1>&2 exit 1 fi fi v6_gateway=${other_params/,*} if [ "$v4_static" != "" ]; then echo "iface eth0 inet static" echo " address $v4_static" [ "$v4_gateway" != "" ] && echo " gateway $v4_gateway" echo fi if [ "$v6_static" != "" ]; then echo "iface eth0 inet6 static" echo " address $v6_static" [ "$v6_gateway" != "" ] && echo " gateway $v6_gateway" fi return 0 } INTERFACE_DETAILS="$(generate_network_config)" # rewrite the default config file sed -i -e "/lxc./{w ${LXC_PATH}/config-auto" -e "d}" "${LXC_PATH}/config" sed -i -e '4,$d' "${LXC_PATH}/config" cat <> "${LXC_PATH}/config" # Useful includes lxc.include = /usr/share/lxc/config/debian.common.conf lxc.include = /usr/share/lxc/config/debian.userns.conf # Set hostname lxc.uts.name = $LXC_NAME # Automatic configuration EOF # add back in the auto foo cat "${LXC_PATH}/config-auto" >> "${LXC_PATH}/config" rm "${LXC_PATH}/config-auto" mkdir "${LXC_PATH}/bin" cat < "${LXC_PATH}/bin/mknod" #!/bin/sh # look for the first argument that looks like a path for i do case \$i in /*) exec touch "\$i" ;; esac done EOF chmod 755 "${LXC_PATH}/bin/mknod" export PATH="${LXC_PATH}/bin:$PATH" DEBOOTSTRAPOPTIONS="" STANDARDPACKAGES="debian-archive-keyring,ifupdown,isc-dhcp-client,locales,openssh-server" if [ "$DEBIAN_RELEASE" == "squeeze" ] || [ "$DEBIAN_RELEASE" == "lenny" ] || [ "$DEBIAN_RELEASE" == "etch" ]; then DEBOOTSTRAPOPTIONS="--no-check-gpg" fi if [ "$DEBIAN_RELEASE" == "lenny" ] || [ "$DEBIAN_RELEASE" == "etch" ]; then STANDARDPACKAGES="debian-archive-keyring,ifupdown,locales,openssh-server" fi debootstrap $DEBOOTSTRAPOPTIONS --foreign --include "${STANDARDPACKAGES}" $DEBIAN_RELEASE "${LXC_ROOTFS}" $DEBIAN_MIRROR echo "DEBOOTSTRAP STAGE 1 COMPLETE" # now totally skip that check in the new root, because it sucks. sed -i -e 's#check_sane_mount () {#check_sane_mount () {\n\treturn 0#;' "${LXC_ROOTFS}/debootstrap/functions" # and stop it from bothering to try to setup proc sed -i -e 's#setup_proc () {#setup_proc () {\n\treturn 0#;' "${LXC_ROOTFS}/debootstrap/functions" keyring_dpkg=$(sed -ne "/^debian-archive-keyring/ { s#.* ##; p; }" "${LXC_ROOTFS}/debootstrap/debpaths") # and unpack debian-archive-keyring, because we'll need that (cd "${LXC_ROOTFS}" && dpkg-deb -x ".$keyring_dpkg" .) # replace the tar containing devices with something that doesn't contain any if [ -e "$LXC_ROOTFS/debootstrap/devices.tar.gz" ]; then (cd "$LXC_ROOTFS/debootstrap" && rm devices.tar.gz && tar czvf devices.tar.gz --files-from=/dev/null) fi # if squeeze, which is totally out of date, then ignore release file expired if [ "$DEBIAN_RELEASE" == "squeeze" ]; then echo 'Acquire::Check-Valid-Until "0";' > ${LXC_ROOTFS}/etc/apt/apt.conf.d/squeeze.conf fi # and mount a shedload of things for fun and profit... for file in /var/lib/lxcfs/proc/*; do fname="$(basename $file)" touch "${LXC_ROOTFS}/proc/$fname" mount -n -o bind "$file" "${LXC_ROOTFS}/proc/$fname" done for dev in null random urandom; do touch "${LXC_ROOTFS}/dev/$dev" mount -n -o bind /dev/$dev "${LXC_ROOTFS}/dev/$dev" done # set /proc/cmdline to something echo "debootstrapping" > "${LXC_ROOTFS}/proc/cmdline" # and disable initscripts disable_initscripts # and run the second stage chroot "${LXC_ROOTFS}" /debootstrap/debootstrap --second-stage # make sure that initscripts are still disabled disable_initscripts # configure locales lang=en_GB.UTF-8 enc=UTF-8 if [ ! -z "$LANG" ]; then lang=${LANG} enc=${LANG#*.} fi cat >> "${LXC_ROOTFS}/etc/locale.gen" < "${LXC_ROOTFS}/etc/timezone" elif [ -f /etc/sysconfig/clock ]; then . /etc/sysconfig/clock echo $ZONE > "${LXC_ROOTFS}/etc/timezone" fi chroot "${LXC_ROOTFS}" dpkg-reconfigure -f noninteractive tzdata # "setup" networking NETWORK_FILE=/etc/network/interfaces if [ -e "${LXC_ROOTFS}/etc/network/interfaces.d" ]; then NETWORK_FILE=/etc/network/interfaces.d/eth0 fi # remove some interesting breakages in pam for unpriv foo sed -i -e 's#^\(session.*required.*pam_loginuid.so\)#\#\1#;' "${LXC_ROOTFS}"/etc/pam.d/* # set the hostname echo $LXC_NAME > "${LXC_ROOTFS}/etc/hostname" SECURITY="" if [ "$DEBIAN_RELEASE" != "sid" ] && [ "$DEBIAN_RELEASE" != "unstable" ]; then SECURITY="deb http://security.debian.org/ $DEBIAN_RELEASE/updates main" fi if [ "$DEBIAN_RELEASE" == "squeeze" ]; then SECURITY="deb http://archive.debian.org/debian/ squeeze-lts main" fi if [ "$DEBIAN_RELEASE" == "lenny" ] || [ "$DEBIAN_RELEASE" == "etch" ]; then SECURITY="" fi # setup sources.list cat < "${LXC_ROOTFS}/etc/apt/sources.list" deb $DEBIAN_MIRROR $DEBIAN_RELEASE main $SECURITY EOF # disable bits of systemd / initrd that break things chroot "${LXC_ROOTFS}" /usr/sbin/update-rc.d -f checkroot.sh disable > /dev/null 2>&1 || true chroot "${LXC_ROOTFS}" /usr/sbin/update-rc.d -f umountfs disable > /dev/null 2>&1 || true chroot "${LXC_ROOTFS}" /usr/sbin/update-rc.d -f hwclock.sh disable > /dev/null 2>&1 || true chroot "${LXC_ROOTFS}" /usr/sbin/update-rc.d -f hwclockfirst.sh disable > /dev/null 2>&1 || true if [ -e "${LXC_ROOTFS}/etc/systemd/system/" ]; then touch "${LXC_ROOTFS}/etc/systemd/system/systemd-setup-dgram-qlen.service" touch "${LXC_ROOTFS}/etc/systemd/system/dev-hugepages.mount" touch "${LXC_ROOTFS}/etc/systemd/system/udev.service" touch "${LXC_ROOTFS}/etc/systemd/system/systemd-udevd.service" chroot "${LXC_ROOTFS}" systemctl set-default multi-user.target || true chroot "${LXC_ROOTFS}" ln -s /lib/systemd/system/halt.target /etc/systemd/system/sigpwr.target fi if [ -e "${LXC_ROOTFS}/lib/systemd/system/systemd-journald-audit.socket" ]; then touch "${LXC_ROOTFS}/etc/systemd/system/systemd-journald-audit.socket" fi echo "$INTERFACE_DETAILS" >> "${LXC_ROOTFS}${NETWORK_FILE}" # and update to the latest security chroot "${LXC_ROOTFS}" apt-get update chroot "${LXC_ROOTFS}" apt-get -y upgrade # if we're all good here, unmount things and clean up [ -e "${LXC_ROOTFS}/usr/sbin/policy-rc.d" ] && rm "${LXC_ROOTFS}/usr/sbin/policy-rc.d" rm "${LXC_ROOTFS}/proc/cmdline" for dev in null random urandom; do umount "${LXC_ROOTFS}/dev/$dev" rm "${LXC_ROOTFS}/dev/$dev" done for file in /var/lib/lxcfs/proc/*; do fname="$(basename $file)" umount "${LXC_ROOTFS}/proc/$fname" rm "${LXC_ROOTFS}/proc/$fname" done enable_initscripts rm -r "${LXC_PATH}/bin" cat <