#!/bin/sh
VERSION='0.3.4'
#
# Copyright (C) 2002, Mark Lawrence <nomad@null.net>
# Copyright (C) 2002, Paul Sladen <vserver@paul.sladen.org>
#
# Install a virtual debian server (vserver) from a debian HTTP/FTP archive
#
# ----------------------------------------------------------------
# Configurable items:
# Root directory of your virtual servers (probably shouldn't change this)
VSERVER_ROOT="/home/vservers"
# Packages to install in addition to the base defaults
# MUST INCLUDE ALL DEPENDENCIES (seperated by "," commas)
INSTALL_PACKAGES="less,dnsutils,wget,bzip2,ssh,rsync,libssl0.9.6,libdns5,libisc4,liblwres1,bind9-host,zlib1g,libbz2-1.0,traceroute"
# Packages installed from within the vserver after it has
# been setup (seperated by "," commas)
EXTRA_PACKAGES="emacs20,lynx-ssl"
# 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"
# sysvinit services relating to hardware access to remove
REMOVE_LINKS="klogd hwclock.sh setserial urandom networking umountfs halt reboot"
# 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)
DIST="sarge"
# Local or nearest location of a debian mirror (must include the `/debian')
MIRROR="http://mirrors.kernel.org/debian"
# Default network interface for vservers:
INTERFACE="eth0"
# Copy vreboot/vhalt/vshutdown utility into /usr/local/sbin/
COPY_VREBOOT="true"
# ----------------------------------------------------------------
# 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
Creates a new Debian vserver by downloading packages via HTTP/FTP
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)
--copy-vreboot install "vreboot/vshutdown/vhalt"
--no-copy-vreboot don't install "vreboot/vshutdown/vhalt"
--dist defaults to "woody", passed to debootstrap.
--fakeinit use "/sbin/init" to boot vserver
--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"
-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
EOF
}
full_version ()
{
cat << EOF
${0##*/} version $VERSION
Copyright (c) 2002 Mark Lawrence <nomad@null.net>
Copyright (c) 2002 Paul Sladen <vserver@paul.sladen.org>
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
;;
--copy-vreboot)
COPY_VREBOOT="true"
shift
;;
--no-copy-vreboot)
COPY_VREBOOT=""
shift
;;
--dist)
case "$2" in
[a-z]*)
DIST="$2"
if [ "woody" != "$2" ]; then
echo "${0##*/} warning: I only know how to do \"woody\", be careful!" 1>&2
fi
;;
*)
echo "${0##*/} error: $1 requires a Debian distribution" 1>&2
echo 'e.g. "woody"' 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
;;
--hostname)
case "$2" in
[a-z]*[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"
;;
*)
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
VSERVER_ROOT="$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%/}"
VSERVER_ROOT="${VSERVER_ROOT%/}"
# Check we've got debootstrap available
if [ ! -x /usr/sbin/debootstrap ]; then
cat << EOF 1>&2
${0##*/}: Requires the debootstrap package to bootstrap Debian
Debian Host: apt-get install debootstrap
RPM Host: rpm -i http://people.debian.org/~blade/install/debootstrap/debootstrap-0.1.17.3-2.i386.rpm
EOF
exit 1
fi
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
# check for /vserver/$VHOST/etc/ incase we are on pre-mounted LVM partition
# (used to just check for "/vserver/$VHOST/" existing
if [ -d "$VSERVER_ROOT/$VHOST/etc/" -o -f "/etc/vservers/$VHOST.conf" ] ; then
cat << EOF 1>&2
${0##*/} error: Virtual Server "$VHOST" appears to already exist
check "/etc/vservers/$VHOST.conf" or "/vservers/$VHOST/etc/";
EOF
exit 1
fi
# This is used to keep a cache of the downloaded .deb packges for next install
if [ -d "$VSERVER_ROOT/ARCHIVES" ]; then
mkdir -p "$VSERVER_ROOT/$VHOST/var/cache/apt/archives"
cp -a "$VSERVER_ROOT/ARCHIVES/"*.deb "$VSERVER_ROOT/$VHOST/var/cache/apt/archives"
fi
# We only want to pass the Architecture if we need to (autodectected otherwise)
if [ -n "$ARCH" ]; then
ARCH_ARGUMENT="--arch $ARCH"
fi
# Fire off `debootstrap' to do all the hard work
# like downloading and installing
if ! /usr/sbin/debootstrap $ARCH_ARGUMENT \
"--include=$INSTALL_PACKAGES" "--exclude=$REMOVE_PACKAGES" \
"$DIST" "$VSERVER_ROOT/$VHOST" "$MIRROR" ; then
echo "${0##*/}: error: debootstrap failure. Cannot continue."
exit 1
fi
# Make it so that apt and friends work
cat << EOF > "$VSERVER_ROOT/$VHOST/etc/apt/sources.list"
deb $MIRROR/ stable main non-free contrib
deb-src $MIRROR/ stable main non-free contrib
deb http://non-us.debian.org/debian-non-US stable/non-US main contrib non-free
deb-src http://non-us.debian.org/debian-non-US stable/non-US main contrib non-free
deb http://security.debian.org stable/updates main contrib non-free
EOF
# Fix up the available device nodes, for security
if cd "$VSERVER_ROOT/$VHOST/dev"; then
tar cfp /tmp/dev.tar.$$ full null ptmx random tty urandom zero
rm -rf $VSERVER_ROOT/$VHOST/dev/*
tar xfp /tmp/dev.tar.$$
rm /tmp/dev.tar.$$
mkdir pts
mkdir shm
fi
# Give the new host a hostname
echo "$VHOST" > "$VSERVER_ROOT/$VHOST/etc/hostname"
# Set up the /etc/hosts file (needed for some parts of the base-config)
cat << EOF > "$VSERVER_ROOT/$VHOST/etc/hosts"
# /etc/hosts
127.0.0.1 localhost
$IP $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) \
> "$VSERVER_ROOT/$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 $VSERVER_ROOT/$VHOST/etc/apt/apt.conf
fi
# This old uname was putting the name of the *host* into the vserver motd
# uname -a > $VSERVER_ROOT/$VHOST/etc/motd
# Create a shorter motd (uname -a would give name of host-server)
echo "Debian GNU/Linux ($DIST/$(uname -m)) $VHOST.$VDOMAIN" \
> "$VSERVER_ROOT/$VHOST/etc/motd"
# Create a dummy fstab
cat << EOF > "$VSERVER_ROOT/$VHOST/etc/fstab"
# /etc/fstab: static file system information.
#
# <file system> <mount point> <type> <options> <dump> <pass>
proc /proc proc defaults 0 0
EOF
# The new vserver patch now automatically mounts /proc
# Debian needs /dev/pts mounting "gid=5,mode=620" by vserver
cat << EOF > "$VSERVER_ROOT/$VHOST/etc/mtab"
/dev/hdv1 / vfs none 0 0
proc /proc proc rw 0 0
devpts /dev/pts devpts rw,gid=5,mode=620 0 0
EOF
# Create a reduced inittab that doesn't start getty on the consoles
cat << EOF > "$VSERVER_ROOT/$VHOST/etc/inittab"
# /etc/inittab: init(8) configuration.
# The default runlevel.
id:2:initdefault:
# Boot-time system configuration/initialization script.
# This is run first except when booting in emergency (-b) mode.
si::sysinit:/etc/init.d/rcS
# What to do in single-user mode.
~~:S:wait:/sbin/sulogin
# /etc/init.d executes the S and K scripts upon change
# of runlevel.
#
# Runlevel 0 is halt.
# Runlevel 1 is single-user.
# Runlevels 2-5 are multi-user.
# Runlevel 6 is reboot.
l0:0:wait:/etc/init.d/rc 0
l1:1:wait:/etc/init.d/rc 1
l2:2:wait:/etc/init.d/rc 2
l3:3:wait:/etc/init.d/rc 3
l4:4:wait:/etc/init.d/rc 4
l5:5:wait:/etc/init.d/rc 5
l6:6:wait:/etc/init.d/rc 6
EOF
# 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 > "$VSERVER_ROOT/$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
# Create default /etc/vservers entry
cat << EOF > /etc/vservers/$VHOST.conf
S_HOSTNAME="$VHOST"
IPROOT="$IP"
IPROOTDEV="$INTERFACE"
ONBOOT="yes"
S_NICE=""
S_FLAGS="lock nproc$FAKEINIT"
ULIMIT="-H -u 256 -n 1024"
S_CAPS="CAP_NET_RAW"
# *NOT* DNS domain name, for NIS only
S_DOMAINNAME=""
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 > $VSERVER_ROOT/$VHOST/vserver-config.sh
#!/bin/sh
dselect update
tzsetup -y
dpkg-reconfigure passwd
tasksel
rm -f /etc/exim/exim.conf
eximconfig
# because the --exclude flag doesn\'t seem to work on debootstrap
apt-get --assume-yes --purge remove `echo $REMOVE_PACKAGES | sed -e 's/,/ /g'`
for link in $REMOVE_LINKS
do
update-rc.d -f \$link remove
done
$EXTRA_PACKAGES_INSTALL
EOF
# Run the above commands from within the server
chmod 755 $VSERVER_ROOT/$VHOST/vserver-config.sh
vserver $VHOST exec /vserver-config.sh
rm -f $VSERVER_ROOT/$VHOST/vserver-config.sh
# If you need to install your SSH management keys into the vserver
if [ -f "$SSH_KEYS" ]; then
mkdir -p "$VSERVER_ROOT/$VHOST/root/.ssh"
chmod 700 "$VSERVER_ROOT/$VHOST/root/.ssh/"
cat "$SSH_KEYS" >> "$VSERVER_ROOT/$VHOST/root/.ssh/authorized_keys"
chmod 600 "$VSERVER_ROOT/$VHOST/root/.ssh/authorized_keys"
fi
# Install the vreboot/rebootmgr utility--hopefully this will disappear soon
VREBOOT_LOCATION="/usr/lib/vserver/vreboot"
if [ "$COPY_VREBOOT" == "true" -a -x "$VREBOOT_LOCATION" ]; then
cp -a "$VREBOOT_LOCATION" "$VSERVER_ROOT/$VHOST/usr/local/sbin/"
chmod 750 "$VSERVER_ROOT/$VHOST/usr/local/sbin/vreboot"
# ln -s "$VSERVER_ROOT/$VHOST/usr/local/sbin/"{vshutdown,vreboot}
# ln -s "$VSERVER_ROOT/$VHOST/usr/local/sbin/"{vhalt,vreboot}
fi
# Stop all the processes that were started inside the server
export PREVLEVEL=2
vserver $VHOST exec /etc/init.d/rc 0
vserver $VHOST stop
# Populate the archive for future virtual servers
if [ ! -d $VSERVER_ROOT/ARCHIVES ]; then
mkdir $VSERVER_ROOT/ARCHIVES
fi
cp $VSERVER_ROOT/$VHOST/var/cache/apt/archives/*.deb $VSERVER_ROOT/ARCHIVES
echo
echo "You should now adjust /etc/vservers/$VHOST.conf 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