Only remove devices.tar.gz if it exists
[lxc-debian-unprivileged.git] / templates / lxc-debian-unprivileged
index 33116094095730ce833184ab5e0aecf443853e68..dd7bdafc11e5e862f594dc8378e7697a24836a29 100755 (executable)
@@ -31,11 +31,15 @@ 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
+    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=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin:$PATH
 export GREP_OPTIONS=""
 
 usage() {
@@ -50,11 +54,40 @@ Required arguments:
 
 Optional arguments:
 [ -m | --mirror <mirrorurl> ]: The debian mirror to user
+[ -n | --network <networkspec> ]: How to configure networking
+
+Network spec:
+  <type>[,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:h -l release:,mirror:,help,mapped-uid:,mapped-gid:,name:,path:,rootfs: -- "$@")
+options=$(getopt -o r:m:n:h -l release:,mirror:,network:,help,mapped-uid:,mapped-gid:,name:,path:,rootfs: -- "$@")
 
 if [ $? -ne 0 ]; then
     usage
@@ -65,33 +98,174 @@ eval set -- "$options"
 
 DEBIAN_MIRROR="http://mirror.mythic-beasts.com/debian/"
 DEBIAN_RELEASE="jessie"
+NETWORK_CONFIG=""
+
+disable_initscripts() {
+    cat <<EOF > "${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;;
-        --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;;
+        -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
 
-echo "Mapped UID: $MAPPED_UID"
-echo "Mapped GID: $MAPPED_GID"
-echo "RootFS: $LXC_ROOTFS"
-echo "Name: $LXC_NAME"
-echo "Path: $LXC_PATH"
+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
+sed -i -e "/lxc./{w ${LXC_PATH}/config-auto" -e "d}" "${LXC_PATH}/config"
+sed -i -e '4,$d' "${LXC_PATH}/config"
 
-cat <<EOF >> ${LXC_PATH}/config
+cat <<EOF >> "${LXC_PATH}/config"
 
 # Useful includes
 lxc.include = /usr/share/lxc/config/debian.common.conf
@@ -104,59 +278,70 @@ lxc.utsname = $LXC_NAME
 EOF
 
 # add back in the auto foo
-cat "${LXC_PATH}/config-auto" >> ${LXC_PATH}/config
-rm ${LXC_PATH}/config-auto
+cat "${LXC_PATH}/config-auto" >> "${LXC_PATH}/config"
+rm "${LXC_PATH}/config-auto"
 
-mkdir ${LXC_PATH}/bin
-cat <<EOF > ${LXC_PATH}/bin/mknod
+mkdir "${LXC_PATH}/bin"
+cat <<EOF > "${LXC_PATH}/bin/mknod"
 #!/bin/sh
 
-exec touch "\$1"
+# 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
+chmod 755 "${LXC_PATH}/bin/mknod"
+
+export PATH="${LXC_PATH}/bin:$PATH"
 
-export PATH=${LXC_PATH}/bin:$PATH
+debootstrap --foreign --include debian-archive-keyring,ifupdown,isc-dhcp-client,locales,openssh-server $DEBIAN_RELEASE "${LXC_ROOTFS}" $DEBIAN_MIRROR
 
-debootstrap --foreign --include debian-archive-keyring,ifupdown,isc-dhcp-client,locales,openssh-server $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
+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
+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)
+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 .)
+(cd "${LXC_ROOTFS}" && dpkg-deb -x ".$keyring_dpkg" .)
 
 # replace the tar containing devices with something that doesn't contain any
-(cd $LXC_ROOTFS/debootstrap && rm devices.tar.gz && tar czvf devices.tar.gz --files-from=/dev/null)
+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
 
 # and mount a shitload 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
+    fname="$(basename $file)"
+    touch "${LXC_ROOTFS}/proc/$fname"
+    mount -n -o bind "$file" "${LXC_ROOTFS}/proc/$fname"
 done
 
-for dev in random urandom; do
-    touch ${LXC_ROOTFS}/dev/$dev
-    mount -n -o bind /dev/$dev ${LXC_ROOTFS}/dev/$dev
+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 silly
-echo "debootstrapping - hahaha" > ${LXC_ROOTFS}/proc/cmdline
+# set /proc/cmdline to something
+echo "debootstrapping" > "${LXC_ROOTFS}/proc/cmdline"
 
 # and disable initscripts
-cat <<EOF > ${LXC_ROOTFS}/usr/sbin/policy-rc.d
-#!/bin/sh
-
-exit 101
-EOF
+disable_initscripts
 
 # and run the second stage
-chroot ${LXC_ROOTFS} /debootstrap/debootstrap --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
@@ -166,77 +351,103 @@ if [ ! -z "$LANG" ]; then
     enc=${LANG#*.}
 fi
 
-cat >> ${LXC_ROOTFS}/etc/locale.gen <<EOF
+cat >> "${LXC_ROOTFS}/etc/locale.gen" <<EOF
 $lang $enc
 EOF
 
-chroot ${LXC_ROOTFS} /usr/sbin/locale-gen $lang $enc
-chroot ${LXC_ROOTFS} /usr/sbin/update-locale LANG=$LANG
+chroot "${LXC_ROOTFS}" /usr/sbin/locale-gen $lang $enc
+chroot "${LXC_ROOTFS}" /usr/sbin/update-locale LANG=$LANG
 
 # configure timezone
 if [ -f /etc/timezone ]; then
-    cat /etc/timezone > ${LXC_ROOTFS}/etc/timezone
+    cat /etc/timezone > "${LXC_ROOTFS}/etc/timezone"
 elif [ -f /etc/sysconfig/clock ]; then
     . /etc/sysconfig/clock
-    echo $ZONE > ${LXC_ROOTFS}/etc/timezone
+    echo $ZONE > "${LXC_ROOTFS}/etc/timezone"
 fi
-chroot ${LXC_ROOTFS} dpkg-reconfigure -f noninteractive tzdata
+chroot "${LXC_ROOTFS}" dpkg-reconfigure -f noninteractive tzdata
 
 # "setup" networking
 NETWORK_FILE=/etc/network/interfaces
-if [ -e ${LXC_ROOTFS}/etc/network/interfaces.d ]; then
+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/*
+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
+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
 
 # setup sources.list
-cat <<EOF > ${LXC_ROOTFS}/etc/apt/sources.list
+cat <<EOF > "${LXC_ROOTFS}/etc/apt/sources.list"
 deb $DEBIAN_MIRROR $DEBIAN_RELEASE main
-deb http://security.debian.org/ $DEBIAN_RELEASE/updates main
+$SECURITY
 EOF
 
-# disable bits of systemd that we hates
-chroot $LXC_ROOTFS /usr/sbin/update-rc.d -f checkroot.sh disable > /dev/null 2>&1
-chroot $LXC_ROOTFS /usr/sbin/update-rc.d -f umountfs disable > /dev/null 2>&1
-chroot $LXC_ROOTFS /usr/sbin/update-rc.d -f hwclock.sh disable > /dev/null 2>&1
-chroot $LXC_ROOTFS /usr/sbin/update-rc.d -f hwclockfirst.sh disable > /dev/null 2>&1
-
-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
-    chroot ${LXC_ROOTFS} ln -s /lib/systemd/system/halt.target /etc/systemd/system/sigpwr.target
+# 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
+    chroot "${LXC_ROOTFS}" ln -s /lib/systemd/system/halt.target /etc/systemd/system/sigpwr.target
 fi
 
-cat <<EOF >> ${LXC_ROOTFS}${NETWORK_FILE}
-auto eth0
-iface eth0 inet dhcp
-EOF
+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
+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
+[ -e "${LXC_ROOTFS}/usr/sbin/policy-rc.d" ] && rm "${LXC_ROOTFS}/usr/sbin/policy-rc.d"
+rm "${LXC_ROOTFS}/proc/cmdline"
 
-for dev in random urandom; do
-    umount ${LXC_ROOTFS}/dev/$dev
-    rm ${LXC_ROOTFS}/dev/$dev
+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
+    fname="$(basename $file)"
+    umount "${LXC_ROOTFS}/proc/$fname"
+    rm "${LXC_ROOTFS}/proc/$fname"
 done
 
+enable_initscripts
+
+rm -r "${LXC_PATH}/bin"
+
+cat <<EOF
+
+You have successfully created a new debian container, ${LXC_NAME} running ${DEBIAN_RELEASE}.
+
+You should start the new container, and use:
+
+  lxc-attach -n ${LXC_NAME} -- su -
+
+To create a user account / set the root password.
+
+Note, by default, it's likely only to be the console that can login as root, so that'd be:
+
+  lxc-console -n ${LXC_NAME} -t 0
+
+EOF
+
 exit 0