diff --git a/boxes/quantal-64/lxc-template b/boxes/quantal-64/lxc-template new file mode 100644 index 0000000..5b1e238 --- /dev/null +++ b/boxes/quantal-64/lxc-template @@ -0,0 +1,718 @@ +#!/bin/bash + +# +# template script for generating ubuntu container for LXC +# +# This script consolidates and extends the existing lxc ubuntu scripts +# + +# Copyright © 2011 Serge Hallyn +# Copyright © 2010 Wilhelm Meier +# Author: Wilhelm Meier +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2, as +# published by the Free Software Foundation. + +# This program 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 General Public License for more details. + +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +set -e + +if [ -r /etc/default/lxc ]; then + . /etc/default/lxc +fi + +configure_ubuntu() +{ + rootfs=$1 + hostname=$2 + release=$3 + + # configure the network using the dhcp + cat < $rootfs/etc/network/interfaces +# This file describes the network interfaces available on your system +# and how to activate them. For more information, see interfaces(5). + +# The loopback network interface +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp +EOF + + # set the hostname + cat < $rootfs/etc/hostname +$hostname +EOF + # set minimal hosts + cat < $rootfs/etc/hosts +127.0.0.1 localhost +127.0.1.1 $hostname + +# The following lines are desirable for IPv6 capable hosts +::1 ip6-localhost ip6-loopback +fe00::0 ip6-localnet +ff00::0 ip6-mcastprefix +ff02::1 ip6-allnodes +ff02::2 ip6-allrouters +EOF + + if [ ! -f $rootfs/etc/init/container-detect.conf ]; then + # suppress log level output for udev + sed -i "s/=\"err\"/=0/" $rootfs/etc/udev/udev.conf + + # remove jobs for consoles 5 and 6 since we only create 4 consoles in + # this template + rm -f $rootfs/etc/init/tty{5,6}.conf + fi + + if [ -z "$bindhome" ]; then + chroot $rootfs useradd --create-home -s /bin/bash ubuntu + echo "ubuntu:ubuntu" | chroot $rootfs chpasswd + fi + + return 0 +} + +# finish setting up the user in the container by injecting ssh key and +# adding sudo group membership. +# passed-in user is either 'ubuntu' or the user to bind in from host. +finalize_user() +{ + user=$1 + + sudo_version=$(chroot $rootfs dpkg-query -W -f='${Version}' sudo) + + if chroot $rootfs dpkg --compare-versions $sudo_version gt "1.8.3p1-1"; then + groups="sudo" + else + groups="sudo admin" + fi + + for group in $groups; do + chroot $rootfs groupadd --system $group >/dev/null 2>&1 || true + chroot $rootfs adduser ${user} $group >/dev/null 2>&1 || true + done + + if [ -n "$auth_key" -a -f "$auth_key" ]; then + u_path="/home/${user}/.ssh" + root_u_path="$rootfs/$u_path" + + mkdir -p $root_u_path + cp $auth_key "$root_u_path/authorized_keys" + chroot $rootfs chown -R ${user}: "$u_path" + + echo "Inserted SSH public key from $auth_key into /home/${user}/.ssh/authorized_keys" + fi + return 0 +} + +write_sourceslist() +{ + # $1 => path to the rootfs + # $2 => architecture we want to add + # $3 => whether to use the multi-arch syntax or not + + case $2 in + amd64|i386) + MIRROR=${MIRROR:-http://archive.ubuntu.com/ubuntu} + SECURITY_MIRROR=${SECURITY_MIRROR:-http://security.ubuntu.com/ubuntu} + ;; + *) + MIRROR=${MIRROR:-http://ports.ubuntu.com/ubuntu-ports} + SECURITY_MIRROR=${SECURITY_MIRROR:-http://ports.ubuntu.com/ubuntu-ports} + ;; + esac + if [ -n "$3" ]; then + cat >> "$1/etc/apt/sources.list" << EOF +deb [arch=$2] $MIRROR ${release} main restricted universe multiverse +deb [arch=$2] $MIRROR ${release}-updates main restricted universe multiverse +deb [arch=$2] $SECURITY_MIRROR ${release}-security main restricted universe multiverse +EOF + else + cat >> "$1/etc/apt/sources.list" << EOF +deb $MIRROR ${release} main restricted universe multiverse +deb $MIRROR ${release}-updates main restricted universe multiverse +deb $SECURITY_MIRROR ${release}-security main restricted universe multiverse +EOF + fi +} + +cleanup() +{ + rm -rf $cache/partial-$arch + rm -rf $cache/rootfs-$arch +} + +suggest_flush() +{ + echo "Container upgrade failed. The container cache may be out of date," + echo "in which case flushing the case (see -F in the hep output) may help." +} + +download_ubuntu() +{ + cache=$1 + arch=$2 + release=$3 + + packages=vim,ssh + echo "installing packages: $packages" + + trap cleanup EXIT SIGHUP SIGINT SIGTERM + # check the mini ubuntu was not already downloaded + mkdir -p "$cache/partial-$arch" + if [ $? -ne 0 ]; then + echo "Failed to create '$cache/partial-$arch' directory" + return 1 + fi + + # download a mini ubuntu into a cache + echo "Downloading ubuntu $release minimal ..." + if [ -n "$(which qemu-debootstrap)" ]; then + qemu-debootstrap --verbose --components=main,universe --arch=$arch --include=$packages $release $cache/partial-$arch $MIRROR + else + debootstrap --verbose --components=main,universe --arch=$arch --include=$packages $release $cache/partial-$arch $MIRROR + fi + + if [ $? -ne 0 ]; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + + # Serge isn't sure whether we should avoid doing this when + # $release == `distro-info -d` + echo "Installing updates" + > $cache/partial-$arch/etc/apt/sources.list + write_sourceslist $cache/partial-$arch/ $arch + + chroot "$1/partial-${arch}" apt-get update + if [ $? -ne 0 ]; then + echo "Failed to update the apt cache" + return 1 + fi + cat > "$1/partial-${arch}"/usr/sbin/policy-rc.d << EOF +#!/bin/sh +exit 101 +EOF + chmod +x "$1/partial-${arch}"/usr/sbin/policy-rc.d + + lxc-unshare -s MOUNT -- chroot "$1/partial-${arch}" apt-get dist-upgrade -y || { suggest_flush; false; } + rm -f "$1/partial-${arch}"/usr/sbin/policy-rc.d + + chroot "$1/partial-${arch}" apt-get clean + + mv "$1/partial-$arch" "$1/rootfs-$arch" + trap EXIT + trap SIGINT + trap SIGTERM + trap SIGHUP + echo "Download complete" + return 0 +} + +copy_ubuntu() +{ + cache=$1 + arch=$2 + rootfs=$3 + + # make a local copy of the miniubuntu + echo "Copying rootfs to $rootfs ..." + mkdir -p $rootfs + rsync -a $cache/rootfs-$arch/ $rootfs/ || return 1 + return 0 +} + +install_ubuntu() +{ + rootfs=$1 + release=$2 + flushcache=$3 + cache="/var/cache/lxc/$release" + mkdir -p /var/lock/subsys/ + + ( + flock -x 200 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi + + + if [ $flushcache -eq 1 ]; then + echo "Flushing cache..." + rm -rf "$cache/partial-$arch" + rm -rf "$cache/rootfs-$arch" + fi + + echo "Checking cache download in $cache/rootfs-$arch ... " + if [ ! -e "$cache/rootfs-$arch" ]; then + download_ubuntu $cache $arch $release + if [ $? -ne 0 ]; then + echo "Failed to download 'ubuntu $release base'" + return 1 + fi + fi + + echo "Copy $cache/rootfs-$arch to $rootfs ... " + copy_ubuntu $cache $arch $rootfs + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi + + return 0 + + ) 200>/var/lock/subsys/lxc + + return $? +} + +copy_configuration() +{ + path=$1 + rootfs=$2 + name=$3 + arch=$4 + release=$5 + + if [ $arch = "i386" ]; then + arch="i686" + fi + + ttydir="" + if [ -f $rootfs/etc/init/container-detect.conf ]; then + ttydir=" lxc" + fi + + # if there is exactly one veth network entry, make sure it has an + # associated hwaddr. + nics=`grep -e '^lxc\.network\.type[ \t]*=[ \t]*veth' $path/config | wc -l` + if [ $nics -eq 1 ]; then + grep -q "^lxc.network.hwaddr" $path/config || cat <> $path/config +lxc.network.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//') +EOF + fi + + grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config + cat <> $path/config +lxc.utsname = $name + +lxc.devttydir =$ttydir +lxc.tty = 4 +lxc.pts = 1024 +lxc.mount = $path/fstab +lxc.arch = $arch +lxc.cap.drop = sys_module mac_admin mac_override +lxc.pivotdir = lxc_putold + +# uncomment the next line to run the container unconfined: +#lxc.aa_profile = unconfined + +lxc.cgroup.devices.deny = a +# Allow any mknod (but not using the node) +lxc.cgroup.devices.allow = c *:* m +lxc.cgroup.devices.allow = b *:* m +# /dev/null and zero +lxc.cgroup.devices.allow = c 1:3 rwm +lxc.cgroup.devices.allow = c 1:5 rwm +# consoles +lxc.cgroup.devices.allow = c 5:1 rwm +lxc.cgroup.devices.allow = c 5:0 rwm +#lxc.cgroup.devices.allow = c 4:0 rwm +#lxc.cgroup.devices.allow = c 4:1 rwm +# /dev/{,u}random +lxc.cgroup.devices.allow = c 1:9 rwm +lxc.cgroup.devices.allow = c 1:8 rwm +lxc.cgroup.devices.allow = c 136:* rwm +lxc.cgroup.devices.allow = c 5:2 rwm +# rtc +lxc.cgroup.devices.allow = c 254:0 rwm +#fuse +lxc.cgroup.devices.allow = c 10:229 rwm +#tun +lxc.cgroup.devices.allow = c 10:200 rwm +#full +lxc.cgroup.devices.allow = c 1:7 rwm +#hpet +lxc.cgroup.devices.allow = c 10:228 rwm +#kvm +lxc.cgroup.devices.allow = c 10:232 rwm +EOF + + cat < $path/fstab +proc proc proc nodev,noexec,nosuid 0 0 +sysfs sys sysfs defaults 0 0 +EOF + + if [ $? -ne 0 ]; then + echo "Failed to add configuration" + return 1 + fi + + return 0 +} + +trim() +{ + rootfs=$1 + release=$2 + + # provide the lxc service + cat < $rootfs/etc/init/lxc.conf +# fake some events needed for correct startup other services + +description "Container Upstart" + +start on startup + +script + rm -rf /var/run/*.pid + rm -rf /var/run/network/* + /sbin/initctl emit stopped JOB=udevtrigger --no-wait + /sbin/initctl emit started JOB=udev --no-wait +end script +EOF + + # fix buggus runlevel with sshd + cat < $rootfs/etc/init/ssh.conf +# ssh - OpenBSD Secure Shell server +# +# The OpenSSH server provides secure shell access to the system. + +description "OpenSSH server" + +start on filesystem +stop on runlevel [!2345] + +expect fork +respawn +respawn limit 10 5 +umask 022 +# replaces SSHD_OOM_ADJUST in /etc/default/ssh +oom never + +pre-start script + test -x /usr/sbin/sshd || { stop; exit 0; } + test -e /etc/ssh/sshd_not_to_be_run && { stop; exit 0; } + test -c /dev/null || { stop; exit 0; } + + mkdir -p -m0755 /var/run/sshd +end script + +# if you used to set SSHD_OPTS in /etc/default/ssh, you can change the +# 'exec' line here instead +exec /usr/sbin/sshd +EOF + + cat < $rootfs/etc/init/console.conf +# console - getty +# +# This service maintains a console on tty1 from the point the system is +# started until it is shut down again. + +start on stopped rc RUNLEVEL=[2345] +stop on runlevel [!2345] + +respawn +exec /sbin/getty -8 38400 /dev/console +EOF + + cat < $rootfs/lib/init/fstab +# /lib/init/fstab: cleared out for bare-bones lxc +EOF + + # reconfigure some services + if [ -z "$LANG" ]; then + chroot $rootfs locale-gen en_US.UTF-8 + chroot $rootfs update-locale LANG=en_US.UTF-8 + else + chroot $rootfs locale-gen $LANG + chroot $rootfs update-locale LANG=$LANG + fi + + # remove pointless services in a container + chroot $rootfs /usr/sbin/update-rc.d -f ondemand remove + + chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls u*.conf); do mv $f $f.orig; done' + chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls tty[2-9].conf); do mv $f $f.orig; done' + chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls plymouth*.conf); do mv $f $f.orig; done' + chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls hwclock*.conf); do mv $f $f.orig; done' + chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls module*.conf); do mv $f $f.orig; done' + + # if this isn't lucid, then we need to twiddle the network upstart bits :( + if [ $release != "lucid" ]; then + sed -i 's/^.*emission handled.*$/echo Emitting lo/' $rootfs/etc/network/if-up.d/upstart + fi +} + +post_process() +{ + rootfs=$1 + release=$2 + trim_container=$3 + + if [ $trim_container -eq 1 ]; then + trim $rootfs $release + elif [ ! -f $rootfs/etc/init/container-detect.conf ]; then + # Make sure we have a working resolv.conf + cresolvonf="${rootfs}/etc/resolv.conf" + mv $cresolvonf ${cresolvonf}.lxcbak + cat /etc/resolv.conf > ${cresolvonf} + + # for lucid, if not trimming, then add the ubuntu-virt + # ppa and install lxcguest + if [ $release = "lucid" ]; then + chroot $rootfs apt-get update + chroot $rootfs apt-get install --force-yes -y python-software-properties + chroot $rootfs add-apt-repository ppa:ubuntu-virt/ppa + fi + + chroot $rootfs apt-get update + chroot $rootfs apt-get install --force-yes -y lxcguest + + # Restore old resolv.conf + rm -f ${cresolvonf} + mv ${cresolvonf}.lxcbak ${cresolvonf} + fi + + # If the container isn't running a native architecture, setup multiarch + if [ -x "$(ls -1 ${rootfs}/usr/bin/qemu-*-static 2>/dev/null)" ]; then + dpkg_version=$(chroot $rootfs dpkg-query -W -f='${Version}' dpkg) + if chroot $rootfs dpkg --compare-versions $dpkg_version ge "1.16.2"; then + chroot $rootfs dpkg --add-architecture ${hostarch} + else + mkdir -p ${rootfs}/etc/dpkg/dpkg.cfg.d + echo "foreign-architecture ${hostarch}" > ${rootfs}/etc/dpkg/dpkg.cfg.d/lxc-multiarch + fi + + # Save existing value of MIRROR and SECURITY_MIRROR + DEFAULT_MIRROR=$MIRROR + DEFAULT_SECURITY_MIRROR=$SECURITY_MIRROR + + # Write a new sources.list containing both native and multiarch entries + > ${rootfs}/etc/apt/sources.list + write_sourceslist $rootfs $arch "native" + + MIRROR=$DEFAULT_MIRROR + SECURITY_MIRROR=$DEFAULT_SECURITY_MIRROR + write_sourceslist $rootfs $hostarch "multiarch" + + # Finally update the lists and install upstart using the host architecture + chroot $rootfs apt-get update + chroot $rootfs apt-get install --force-yes -y --no-install-recommends upstart:${hostarch} mountall:${hostarch} iproute:${hostarch} isc-dhcp-client:${hostarch} + fi + + # rmdir /dev/shm for containers that have /run/shm + # I'm afraid of doing rm -rf $rootfs/dev/shm, in case it did + # get bind mounted to the host's /run/shm. So try to rmdir + # it, and in case that fails move it out of the way. + if [ ! -L $rootfs/dev/shm ] && [ -d $rootfs/run/shm ] && [ -e $rootfs/dev/shm ]; then + mv $rootfs/dev/shm $rootfs/dev/shm.bak + ln -s /run/shm $rootfs/dev/shm + fi +} + +do_bindhome() +{ + rootfs=$1 + user=$2 + + # copy /etc/passwd, /etc/shadow, and /etc/group entries into container + pwd=`getent passwd $user` || { echo "Failed to copy password entry for $user"; false; } + echo $pwd >> $rootfs/etc/passwd + + # make sure user's shell exists in the container + shell=`echo $pwd | cut -d: -f 7` + if [ ! -x $rootfs/$shell ]; then + echo "shell $shell for user $user was not found in the container." + pkg=`dpkg -S $(readlink -m $shell) | cut -d ':' -f1` + echo "Installing $pkg" + chroot $rootfs apt-get --force-yes -y install $pkg + fi + + shad=`getent shadow $user` + echo "$shad" >> $rootfs/etc/shadow + + # bind-mount the user's path into the container's /home + h=`getent passwd $user | cut -d: -f 6` + mkdir -p $rootfs/$h + + # use relative path in container + h2=${h#/} + while [ ${h2:0:1} = "/" ]; do + h2=${h2#/} + done + echo "$h $h2 none bind 0 0" >> $path/fstab + + # Make sure the group exists in container + grp=`echo $pwd | cut -d: -f 4` # group number for $user + grpe=`getent group $grp` || return 0 # if host doesn't define grp, ignore in container + chroot $rootfs getent group "$grpe" || echo "$grpe" >> $rootfs/etc/group +} + +usage() +{ + cat <] [--trim] [-d|--debug] + [-F | --flush-cache] [-r|--release ] [ -S | --auth-key ] +release: the ubuntu release (e.g. precise): defaults to host release on ubuntu, otherwise uses latest LTS +trim: make a minimal (faster, but not upgrade-safe) container +bindhome: bind 's home into the container + The ubuntu user will not be created, and will have + sudo access. +arch: the container architecture (e.g. amd64): defaults to host arch +auth-key: SSH Public key file to inject into container +EOF + return 0 +} + +options=$(getopt -o a:b:hp:r:xn:FS:d -l arch:,bindhome:,help,path:,release:,trim,name:,flush-cache,auth-key:,debug -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi +eval set -- "$options" + +release=precise # Default to the last Ubuntu LTS release for non-Ubuntu systems +if [ -f /etc/lsb-release ]; then + . /etc/lsb-release + if [ "$DISTRIB_ID" = "Ubuntu" ]; then + release=$DISTRIB_CODENAME + fi +fi + +bindhome= +arch=$(arch) + +# Code taken from debootstrap +if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then + arch=`/usr/bin/dpkg --print-architecture` +elif type udpkg >/dev/null 2>&1 && udpkg --print-architecture >/dev/null 2>&1; then + arch=`/usr/bin/udpkg --print-architecture` +else + arch=$(arch) + if [ "$arch" = "i686" ]; then + arch="i386" + elif [ "$arch" = "x86_64" ]; then + arch="amd64" + elif [ "$arch" = "armv7l" ]; then + arch="armel" + fi +fi + +debug=0 +trim_container=0 +hostarch=$arch +flushcache=0 +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -F|--flush-cache) flushcache=1; shift 1;; + -r|--release) release=$2; shift 2;; + -b|--bindhome) bindhome=$2; shift 2;; + -a|--arch) arch=$2; shift 2;; + -x|--trim) trim_container=1; shift 1;; + -S|--auth-key) auth_key=$2; shift 2;; + -d|--debug) debug=1; shift 1;; + --) shift 1; break ;; + *) break ;; + esac +done + +if [ $debug -eq 1 ]; then + set -x +fi + +if [ -n "$bindhome" ]; then + pwd=`getent passwd $bindhome` + if [ $? -ne 0 ]; then + echo "Error: no password entry found for $bindhome" + exit 1 + fi +fi + + +if [ "$arch" == "i686" ]; then + arch=i386 +fi + +if [ $hostarch = "i386" -a $arch = "amd64" ]; then + echo "can't create amd64 container on i386" + exit 1 +fi + +type debootstrap +if [ $? -ne 0 ]; then + echo "'debootstrap' command is missing" + exit 1 +fi + +if [ -z "$path" ]; then + echo "'path' parameter is required" + exit 1 +fi + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +# detect rootfs +config="$path/config" +if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` +else + rootfs=$path/rootfs +fi + +install_ubuntu $rootfs $release $flushcache +if [ $? -ne 0 ]; then + echo "failed to install ubuntu $release" + exit 1 +fi + +configure_ubuntu $rootfs $name $release +if [ $? -ne 0 ]; then + echo "failed to configure ubuntu $release for a container" + exit 1 +fi + +copy_configuration $path $rootfs $name $arch $release +if [ $? -ne 0 ]; then + echo "failed write configuration file" + exit 1 +fi + +post_process $rootfs $release $trim_container + +if [ -n "$bindhome" ]; then + do_bindhome $rootfs $bindhome + finalize_user $bindhome +else + finalize_user ubuntu +fi + +echo "" +echo "##" +if [ -n "$bindhome" ]; then + echo "# Log in as user $bindhome" +else + echo "# The default user is 'ubuntu' with password 'ubuntu'!" + echo "# Use the 'sudo' command to run tasks as root in the container." +fi +echo "##" +echo ""