#!/bin/bash # # Copyright (C) 2002-2005 Ola Lundqvist # Copyright (C) 2005 Thomas Gelf # Copyright (C) 2005 Daniel Hermann # Copyright (C) 2002 Paul Sladen # Copyright (C) 2002 Mark Lawrence # # This is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free # Software Foundation; either version 2, or (at your option) any later # version. # # This 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 with # the source package as the file COPYING. If not, write to the Free # Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA. # # Install a virtual debian server (vserver) from a debian HTTP/FTP archive # # ---------------------------------------------------------------- # Configurable items: shopt -s extglob # Root directory of your virtual servers (probably shouldn't change this) # Support for 0.30.203 and above of util-vserver VROOTDIR="/etc/vservers/.defaults/vdirbase" # Packages to install in addition to the base defaults # MUST INCLUDE ALL DEPENDENCIES (seperated by "," commas) INSTALL_PACKAGES="locales" # Packages installed from within the vserver after it has # been setup (seperated by "," commas) EXTRA_PACKAGES="" # Packages to remove from the base defaults (seperated by "," commas) #REMOVE_PACKAGES="dhcp-client,lilo,makedev,pcmcia-cs,ppp,pppconfig,pppoe,pppoeconf,setserial,syslinux,nano,fdutils,iptables,libpcap0,pciutils" REMOVE_PACKAGES="makedev,pcmcia-cs,ppp,pppconfig,pppoe,pppoeconf,setserial,syslinux,fdutils,libpcap0,iptables,pciutils" # sysvinit services relating to hardware access to remove REMOVE_LINKS="klogd hwclock.sh setserial urandom networking umountfs halt reboot mountvirtfs mountall.sh mountnfs.sh ifupdown" # Post installation script POST_INSTALL_SCRIPT="" # Architecture: overide on non-Debian host such as Redhat # otherwise dpkg will detect whether we are i386/powerpc/sparc/etc ARCH="" # Which debian distribution # (warning: this has only been tested with woody and sarge) DIST="sarge" # Local or nearest location of a debian mirror (must include the `/debian') MIRROR="http://ftp.uk.debian.org/debian" # Default network interface for vservers: INTERFACE="eth0" # Package caching PKGCACHE=0 # setting context C_CONT="" if [ -r /etc/vservers/newvserver-vars ] ; then . /etc/vservers/newvserver-vars fi # ---------------------------------------------------------------- # Nothing from here on should need changing. # ---------------------------------------------------------------- # NOTE: debootstrap handles multiple MIRRORs, so there is no reason why # we shouldn't too--that way we could just let it build /etc/apt/sources.list usage () { cat << EOF 1>&2 usage: ${0##*/} [OPTIONS] --hostname x --domain y.z --ip 1.2.3.4 (see --help for more information) EOF } full_usage () { cat << EOF Usage: ${0##*/} [OPTIONS] --hostname x --domain y.z --ip 1.2.3.4/24 Creates a new Debian vserver by calling "vserver ... build" Options: -h, --help this help -V, --version copyright and version information --arch set target architecture (eg. --arch "i386") (autodetected on Debian host if dpkg available) --context st context number if desired --dist defaults to "sarge", passed to debootstrap. --fakeinit use "/sbin/init" to boot vserver --conffile extra configuration file to load. --interface interface for IP addresses (if not "eth0") --mirror Debian HTTP/FTP mirror (including the /debian) --sshkeys copy pub-keys to "/root/.ssh/authorized_keys" --pkgcache Package caching. -v, --verbose show extra output during setup --vsroot location of "/vserver/" directory Required: --hostname hostname for new vserver (eg. "alpha") --domain dns domain for new vserver (eg. "example.com") --ip IPv4 address for new vserver (syntax: --ip [/]) You can also set variables in /etc/vservers/newvserver-vars. EOF } full_version () { cat << EOF ${0##*/} Copyright (c) 2002-2005 Ola Lundqvist Copyright (c) 2005 Daniel Hermann Copyright (c) 2002 Paul Sladen Copyright (c) 2002 Mark Lawrence This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. EOF } parse_args () { while [ $# -gt 0 ] ; do #echo "parse_args() doing :$#: :$1: :$*:" case "$1" in --help|-h) full_usage shift exit 0 ;; --version|-V) full_version shift exit 0 ;; --arch) case "$2" in [a-z]*) ARCH="$2" ;; *) echo "${0##*/} error: $1 overrides dpkg autodetect on non-Debian host-servers" 1>&2 echo 'e.g. "i386"' 1>&2 exit 1 ;; esac shift 2 ;; --context) case "$2" in [0-9]*) C_CONT="$2" ;; *) echo "${0##*/} error: $1 must be a number, otherwize leave blanc !" 1>&2 echo 'e.g. "42"' 1>&2 exit 1 ;; esac shift 2 ;; --conffile) if [ -r "$2" ] ; then . "$2" else echo "Error, can not load config file $2." exit 1 fi shift 2 ;; --post-install-script) if [ -r "$2" ] ; then POST_INSTALL_SCRIPT="$2" echo "Script $2 found." else echo "Error, can not locate the script $2." exit 1 fi shift 2 ;; --dist) case "$2" in [a-z]*) DIST="$2" if [ "sarge" != "$2" ] && [ "woody" != "$2" ] ; then echo -e "\n${0##*/} warning: I only know how to do \"sarge\" and \"etch\", be careful!" 1>&2 fi ;; *) echo "${0##*/} error: $1 requires a Debian distribution" 1>&2 echo 'e.g. "woody" or "sarge"' 1>&2 exit 1 ;; esac shift 2 ;; --domain) case "$2" in [a-z]*[a-z]) VDOMAIN="$2" ;; *) echo "${0##*/} error: $1 requires a dns domain-name" 1>&2 echo 'e.g. "example.com"' 1>&2 exit 1 ;; esac shift 2 ;; --fakeinit) # Note space at beginning--this gets tagged straight on FAKEINIT=" fakeinit" shift ;; --pkgcache) PKGCACHE=1 shift ;; --hostname) case "$2" in *([a-z0-9])*([a-z0-9_-])[a-z]*([a-z0-9_-])*([a-z0-9])) VHOST="$2" ;; *) echo "${0##*/} error: $1 must be a hostname for the vserver" 1>&2 echo 'e.g. "alpha"' 1>&2 exit 1 ;; esac shift 2 ;; --interface) case "$2" in [a-z]*) INTERFACE="$2" ;; *) echo "${0##*/} error: $1 must be followed by a network interface" 1>&2 echo 'e.g. "eth1"' 1>&2 exit 1 ;; esac shift 2 ;; --ip) # This does for an octet: ([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]) ;-) case "$2" in [0-9]*.[0-9]*.[0-9]*.[0-9]*) IP="$2" # get netmask IP_NETMASK=${IP##*/} test "$IP_NETMASK" != "$1" || IP_NETMASK= IP_ADDR=${IP%%/${IP_NETMASK}} # if IP_NETMASK contains no ".", it is # interpreted as IP_PREFIX IP_PREFIX= test "${IP_NETMASK%%.*}" != "${IP_NETMASK}" \ || { IP_PREFIX=$IP_NETMASK IP_NETMASK= } ;; *) echo "${0##*/} error: $1 requires a single IPv4 e.g. \"192.168.100.1\"" 1>&2 exit 1 ;; esac shift 2 ;; --mirror) case "$2" in [hf]*:/*) MIRROR="$2" ;; *) echo "${0##*/} error: $1 requires a debian mirror" 1>&2 echo 'e.g. "http://ftp.uk.debian.org/debian"' 1>&2 exit 1 ;; esac shift 2 ;; --verbose|-v) export verbose="true" shift ;; --sshkeys) if [ -f "$2" ]; then SSH_KEYS="$2" else echo "${0##*/} error: $1 must be followed by a valid public-key-file!" 1>&2 echo 'e.g. "/root/.ssh/id_dsa.pub"' 1>&2 exit 1 fi shift 2 ;; --vsroot) case "$2" in /*) if [ -d "$2" ]; then VROOTDIR="$2" else echo "${0##*/} error: $1 needs a valid absolute directory" 1>&2 echo 'e.g. "/vservers"' 1>&2 exit 1 fi ;; *) echo "${0##*/} error: $1 needs a valid absolute directory" 1>&2 echo 'e.g. "/vservers"' 1>&2 exit 1 ;; esac shift 2 ;; -*) usage exit 1 ;; ?*) usage exit 1 ;; esac done } parse_args $@ if ! [ -n "$VHOST" -a -n "$VDOMAIN" -a -n "$IP" ]; then echo "${0##*/} error: --hostname, --domain and --ip are required" 1>&2 usage exit 1 fi # Strip final slashes off a couple of things MIRROR="${MIRROR%/}" VROOTDIR="${VROOTDIR%/}" ############################################################################## # Arch specific issues # Dist specific issues ############################################################################## if ! cat /proc/self/status | grep '^s_context:[^0-9]0$'; then echo "${0##*/} error:" echo " Must be run from the host server (security context 0)" 1>&2 echo ' on a "vserver/ctx-patch" enabled kernel' 1>&2 echo ' See: http://www.solucorp.qc.ca/miscprj/s_context.hc' 1>&2 exit 1 fi if [ -x /usr/bin/id ] && [ `id -u` -ne 0 ]; then echo "${0##*/} error: Must be run as root!" 1>&2 exit 1 fi aptcleanup() { apt-get autoclean -o Dir::Cache::Archives="$1" OLD="" for P in $(find "$1" -name *.deb -printf "%f\n" -mindepth 1 -maxdepth 1 | sed -e "s/_.*//;") ; do if [ "$P" == "$OLD" ] ; then rm -f "$1"/$P_* > /dev/null 2>&1 fi OLD=$P done } # This is used to keep a cache of the downloaded .deb packges for next install if [ -d "$VROOTDIR/ARCHIVES/$DIST" ] && [ $PKGCACHE -eq 1 ] ; then mkdir -p "$VROOTDIR/$VHOST/var/cache/apt/archives" if [ ! -d "$VROOTDIR/ARCHIVES/$DIST/partial" ] ; then # Needed to make apt-get autoclean work fine. mkdir "$VROOTDIR/ARCHIVES/$DIST/partial" fi echo "Clean package cache." aptcleanup "$VROOTDIR/ARCHIVES/$DIST" cp -a "$VROOTDIR/ARCHIVES/$DIST/"*.deb "$VROOTDIR/$VHOST/var/cache/apt/archives" > /dev/null 2>&1 fi # We only want to pass the Architecture if we need to (autodectected otherwise) if [ -n "$ARCH" ]; then ARCH_ARGUMENT="--arch $ARCH" fi # for debian add --resolve-deps for debootstrap # add a default context of 42, this can be changed later echo "/usr/sbin/vserver "$VHOST" build -m debootstrap \ --rootdir "$VROOTDIR" --hostname "$VHOST" --interface "$INTERFACE:$IP" --context "$C_CONT" \ -- -d "$DIST" -m "$MIRROR" \ -- $ARCH_ARGUMENT \ --include="$INSTALL_PACKAGES" --exclude="$REMOVE_PACKAGES" --resolve-deps" ## use "vserver ... build" to build the new vserver if ! /usr/sbin/vserver "$VHOST" build -m debootstrap \ --rootdir "$VROOTDIR" --hostname "$VHOST" --interface "$INTERFACE:$IP" --context "$C_CONT" \ -- -d "$DIST" -m "$MIRROR" \ -- $ARCH_ARGUMENT \ --include="$INSTALL_PACKAGES" --exclude="$REMOVE_PACKAGES" --resolve-deps then echo "${0##*/}: error: vserver-build failure. Cannot continue." exit 1 fi # Make it so that apt and friends work cat << EOF > "$VROOTDIR/$VHOST/etc/apt/sources.list" deb $MIRROR/ $DIST main non-free contrib deb-src $MIRROR/ $DIST main non-free contrib #deb http://non-us.debian.org/debian-non-US $DIST/non-US main contrib non-free #deb-src http://non-us.debian.org/debian-non-US $DIST/non-US main contrib non-free deb http://security.debian.org $DIST/updates main contrib non-free EOF # Fix up the available device nodes (mostly done by vserver-build above) if cd "$VROOTDIR/$VHOST/dev"; then ln -s /proc/self/fd fd ln -s fd/2 stderr ln -s fd/0 stdin ln -s fd/1 stdout fi # Give the new host a hostname echo "$VHOST" > "$VROOTDIR/$VHOST/etc/hostname" # Set up the /etc/hosts file (needed for some parts of the base-config) cat << EOF > "$VROOTDIR/$VHOST/etc/hosts" # /etc/hosts 127.1.0.1 localhost $IP_ADDR $VHOST.$VDOMAIN $VHOST # The following lines are desirable for IPv6 capable hosts # (added automatically by netbase upgrade) ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters ff02::3 ip6-allhosts EOF # grab DNS servers from the host-server for `resolv.conf' (echo search $VDOMAIN ; grep '^nameserver' /etc/resolv.conf) \ > "$VROOTDIR/$VHOST/etc/resolv.conf" # If there is a proxy server statement in-use in the Host server, copy it across if [ -f /etc/apt/apt.conf ]; then cp /etc/apt/apt.conf $VROOTDIR/$VHOST/etc/apt/apt.conf fi # support the apt.conf.d directories if [ -d /etc/apt/apt.conf.d ] ; then cp -a /etc/apt/apt.conf.d/* $VROOTDIR/$VHOST/etc/apt/apt.conf.d/ > /dev/null 2>&1 fi # This old uname was putting the name of the *host* into the vserver motd # uname -a > $VROOTDIR/$VHOST/etc/motd # Create a shorter motd (uname -a would give name of host-server) echo "Debian GNU/Linux ($DIST/$(uname -m)) $VHOST.$VDOMAIN" \ > "$VROOTDIR/$VHOST/etc/motd" # Create a dummy fstab cat << EOF > "$VROOTDIR/$VHOST/etc/fstab" # /etc/fstab: static file system information. # # proc /proc proc defaults 0 0 EOF # Create a reduced inittab that doesn't start getty on the consoles mv "$VROOTDIR/$VHOST/etc/inittab" "$VROOTDIR/$VHOST/etc/inittab.dist" && \ grep -v respawn\:/sbin/getty "$VROOTDIR/$VHOST/etc/inittab.dist" > \ "$VROOTDIR/$VHOST/etc/inittab" && rm -f "$VROOTDIR/$VHOST/etc/inittab.dist" # By default the Debian Install script runs zillions of cron jobs at # 0625 every morning. On a system with lots of vservers all trying to # scan the disk at the same time this causes $MAJOR disk-thrash. So # we randomize it a bit so that they run evenly between 1am and 7am, # avoiding the 5minutes either side of the hour when other stuff tends # to be scheduled. (BTW, this solution is Overkill!) # This looks over complicated--and it probably is...: # # a) We want the DAILY jobs to run between :05 and :55 minutes past # b) We want the WEEKLY job 3-5 minutes after the DAILY. # c) And the MONTHLY job 3-5 minutes after that. # d) Make sure all three jobs are started by 55minutes past (five-to) # ...if they were ever to all run on the same day! d1=$(($RANDOM % 3 + 3)); # between 3 and 5 d2=$(($RANDOM % 3 + 3)); # between 3 and 5 dt=$((50 - $d1 - $d2)); # between 0 and 44 DAILY=$(($RANDOM % $dt + 5)) # between 5 and 49 WEEKLY=$(($DAILY + $d1)) # between 8 and 52 MONTHLY=$(($WEEKLY + $d2)) # between 11 and 55 HOUR=$(($RANDOM % 6 + 1)) # between 1 and 7 (AM localtime) # Create replacement /etc/crontab with randomized times above cat << EOF > "$VROOTDIR/$VHOST/etc/crontab" # /etc/crontab: system-wide crontab # Unlike any other crontab you don't have to run the \`crontab\' # command to install the new version when you edit this file. # This file also has a username field, that none of the other crontabs do. SHELL=/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin # m h dom mon dow user command $DAILY $HOUR * * * root test -e /usr/sbin/anacron || run-parts --report /etc/cron.daily $WEEKLY $HOUR * * 7 root test -e /usr/sbin/anacron || run-parts --report /etc/cron.weekly $MONTHLY $HOUR 1 * * root test -e /usr/sbin/anacron || run-parts --report /etc/cron.monthly EOF if [ -n "$EXTRA_PACKAGES" ]; then EXTRA_PACKAGES_INSTALL="apt-get --assume-yes install ${EXTRA_PACKAGES//,/ }" fi # ------------------------------------------------------------ # From here on we do things live in the server # Generate the script that runs the rest of the setup from within the # virtual server. cat << EOF > "$VROOTDIR/$VHOST/vserver-config.sh" #!/bin/sh dselect update dpkg-reconfigure locales tzsetup -y dpkg-reconfigure passwd tasksel if [ "$DIST" == "woody" ]; then rm -f /etc/exim/exim.conf eximconfig fi # because the --exclude flag doesn\'t seem to work on debootstrap dpkg -P `echo $REMOVE_PACKAGES | sed -e 's/,/ /g'` for link in $REMOVE_LINKS do update-rc.d -f \$link remove update-rc.d -f \$link stop 90 3 . done $EXTRA_PACKAGES_INSTALL EOF ## start vserver before we can exec anything inside it vserver $VHOST start # Run the above commands from within the server chmod 755 $VROOTDIR/$VHOST/vserver-config.sh vserver $VHOST exec /vserver-config.sh rm -f $VROOTDIR/$VHOST/vserver-config.sh # Run the post-installation script from within the server if [ -n "$POST_INSTALL_SCRIPT" ]; then cp "$POST_INSTALL_SCRIPT" $VROOTDIR/$VHOST/post_install_script chmod 755 $VROOTDIR/$VHOST/post_install_script vserver $VHOST exec /post_install_script rm -f $VROOTDIR/$VHOST/post_install_script fi # If you need to install your SSH management keys into the vserver if [ -f "$SSH_KEYS" ]; then mkdir -p "$VROOTDIR/$VHOST/root/.ssh" chmod 700 "$VROOTDIR/$VHOST/root/.ssh/" cat "$SSH_KEYS" >> "$VROOTDIR/$VHOST/root/.ssh/authorized_keys" chmod 600 "$VROOTDIR/$VHOST/root/.ssh/authorized_keys" fi ## stop the vserver vserver $VHOST stop # Populate the archive for future virtual servers if [ $PKGCACHE -eq 1 ] ; then if [ ! -d "$VROOTDIR/ARCHIVES/$DIST/partial" ] ; then mkdir -p "$VROOTDIR/ARCHIVES/$DIST/partial" fi cp "$VROOTDIR/$VHOST/"var/cache/apt/archives/*.deb "$VROOTDIR/ARCHIVES/$DIST" echo "Cleanup package cache." aptcleanup "$VROOTDIR/ARCHIVES/$DIST" fi echo echo "You should now adjust the configuration in /etc/vservers/$VHOST/" echo "to suit your needs," echo "or else just go ahead and type \`vserver $VHOST start' to start" echo "your new virtual server. debian/rules!" echo