The Digital Agency for International Development

AfNOG 2012 Setup

By Chris Wilson on 05 May 2012

I'm at the AfNOG Workshop 2012 in Serrekunda, Gambia as one of the instructors in the Scalable Services track (SS-E).

A local TV company has reported about us on the local news, and you can watch the video. (Thanks to Nadejda Loumbeva for finding it on YouTube!)

These are my notes from our setup for 2012, building on the virtualisation setup from last year.

We are hosting on CentOS 6 this year. I had a lot of problems installing the VirtualBox RPM, with this error message:

error: unpacking of archive failed: cpio: read failed - No such file or directory

That almost had me abandon CentOS, but it turned out to be a corrupt download file.

CentOS 6 seems to install headless, which saves us memory, but makes the configuration of VirtualBox more challenging. Luckily we had most of the VBoxManage commands from last year.

Remote headless installation

VirtualBox for CentOS doesn't appear to have VNC support at all. You need to install the extension pack, to enable RDP support, to get nice remote access to the machines to do the installation:

sudo VBoxManage extpack install Oracle_VM_VirtualBox_Extension_Pack-4.1.14-77440.vbox-extpack

Configure the Master virtual machine, ensuring that its first network interface is bridged to the host's physical Ethernet interface and promiscuous mode is enabled:

VBoxManage modifyvm Master \
	--memory 256 \
	--nic1 bridged --bridgeadapter1 p4p1 \
	--nicpromisc1 allow-all --vram 4 \
	--pae off --audio none --usb on --uart1 0x3f8 4 \
	--uartmode1 server /home/inst/.VirtualBox/Master-console.pipe

To attach a 20GB virtual hard disk image to the Master virtual machine for installation:

VBoxManage createhd --filename VirtualBox\ VMs/Master/FreeBSD.vdi \
	--size 20480 --format VDI
VBoxManage storageattach Master --storagectl "IDE Controller" --port 0 \
	--device 0 --type hdd --medium VirtualBox\ VMs/Master/FreeBSD.vdi

To attach a FreeBSD ISO image to the Master virtual machine for installation:

VBoxManage storagectl Master --name "IDE Controller" --add ide
VBoxManage storageattach Master --type dvddrive --storagectl "IDE Controller" \
	--port 1 --device 0 --medium FreeBSD-8.3-RELEASE-i386-dvd1.iso

You can remove the virtual disk from the virtual CD-ROM drive like this:

VBoxManage storageattach Master --type dvddrive --storagectl "IDE Controller" \
	--port 1 --device 0 --medium emptydrive

This seems to trigger a serious bug in FreeBSD that makes the system unbootable. So instead, you can leave it connected but change the boot order so that the VM boots from disk instead of CD:

VBoxManage modifyvm Master --boot1 disk 

To enable an RDP remote display on port 5900, for headless operation:

VBoxManage modifyvm Master --vrde=on --vrdeport=3380

To start the Master virtual machine:

VBoxManage startvm Master --type headless

If your virtual machine refuses to start, with an error like this:

[inst@host1 ~]$ VBoxManage startvm Master
Waiting for VM "Master" to power on...
VBoxManage: error: The virtual machine 'Master' has terminated unexpectedly during startup with exit code 0
VBoxManage: error: Details: code NS_ERROR_FAILURE (0x80004005), component Machine, interface IMachine, callee 

It probably means that you're running the command remotely without a GUI, and you forgot to add --type headless, so the graphical display can't start. Thanks VirtualBox!

Install a caching nameserver such as BIND on the host, and when configuring the Master machine, set the DNS server to to use the host's DNS. This avoids problems using a DNS server on the network while all the clones are using the same IP address, resulting in long delays for SSH logins.

Remote virtual serial console

ConMan does indeed work for host to guest communication over the serial pipe, as an alternative to RDP. The following line enables the UNIX socket for the serial console, if you haven't done this already (requires the virtual machine to be stopped):

VBoxManage modifyvm Master --uartmode1 server /home/inst/.VirtualBox/Master-console.pipe

The following line in /etc/conman.conf on the host enables the conman server:

console name="Master" dev="unix:/home/inst/.VirtualBox/Master-console.pipe"

To enable a serial terminal in a FreeBSD guest, add the following line to /etc/ttys:

ttyu0   "/usr/libexec/getty std.9600"   vt100   on secure

Then, after you start (or restart) conmand, you should be able to connect to the Master's serial console like this:

conman Master

The escape sequence to terminate the conman session is &. But this still isn't a good way to mass-control multiple machines because conman doesn't like commands being piped into it.

Remote port-forwarded SSH management

You can set up NAT port forwarding to a second virtual network interface in the guest, configured by DHCP, like this:

VBoxManage modifyvm Master --nic2 nat --natpf2 ssh,tcp,,4400,,22

This is really helpful when all your virtual machines start off with the same IP address. As long as you have SSH keys, you can iterate over all of them, using the host IP addresses, and update their network configurations to give them unique, preassigned IP addresses.

FreeBSD proxy server configuration

To save bandwidth and speed up package installation, I added the following to /etc/profile:

export FTP_PROXY=
export HTTP_PROXY=
export http_proxy=

Bulk virtual machine management

This is a shorter and more efficient script to create the guest virtual machines by snapshotting and cloning the master image, resulting in less disk usage (faster host and guest cloning) and more efficient disk cache usage (faster guest operation):

set -e set -x

master=Master snapshot="Clone Source `date --rfc-3339=seconds`" VBoxManage snapshot "$master" take "$snapshot" --pause

cd ~/"VirtualBox VMs" for i in {1..7}; do # echo $i vmname=VM0$i if [ "$1" = "-f" ]; then VBoxManage unregistervm "$vmname" --delete || true fi VBoxManage clonevm "$master" --name "$vmname" --options link \ --options keepdisknames --snapshot "$snapshot" --register VBoxManage modifyvm "$vmname" --memory 256 \ --natpf2 "ssh-$vmname",tcp,,$[4400+$i],,22 \ --uartmode1 server /home/inst/.VirtualBox/"$vmname"-console.pipe \ --vrde=on --vrdeport=$[3380+$i] done

Note that you have to use a unique snapshot name for each new snapshot of the master image. VirtualBox doesn't stop you from reusing an old name, but when you clone the master, you get a copy of the oldest snapshot with that name, not the latest one.

This script logs into each VM using the NAT port forwarding from the host, which works even when their IP address is not configured yet, sets their hostname and IP address and reboots them:

# set -e set -x


for host in 1; do hostname="196.200.219.$[200+$host]" for guest in `seq 1 $guests_per_host`; do nat_ssh_port=$[4400+$guest] guest_ip_suffix="$[($host-1)*$guests_per_host + $guest]" guest_ip="196.200.219.$guest_ip_suffix" guest_name="vm0$guest-host$host-ip$" ssh root@$hostname -p $nat_ssh_port \ -o "StrictHostKeyChecking no" << EOF set -x sed -e 's/hostname=.*/hostname="$guest_name"/' \ -e 's/ifconfig_em0=.*/ifconfig_em0="inet $guest_ip netmask"/' \ -i '' /etc/rc.conf reboot EOF done done

Starting virtual machines automatically

We want all the virtual machines to start at boot time. My usual sudo trick doesn't work in /etc/rc.local, but thanks to this blog post I found a way:

su --session-command "VBoxManage startvm VM01 --type headless" inst
su --session-command "VBoxManage startvm VM02 --type headless" inst
su --session-command "VBoxManage startvm VM03 --type headless" inst
su --session-command "VBoxManage startvm VM04 --type headless" inst
su --session-command "VBoxManage startvm VM05 --type headless" inst
su --session-command "VBoxManage startvm VM06 --type headless" inst
su --session-command "VBoxManage startvm VM07 --type headless" inst

We also want the host to shut them down cleanly:


Assign unique MAC addresses

After creating the remaining virtual machine hosts, by cloning the first one, and if you update and copy out the virtual machine disk images again, it's important to randomise all the MAC addresses of all the hosts. If two virtual machines have the same MAC address, routers will have problems learning their addresses. You can do that in bulk with this command:

for i in `seq 201 208`; do
ssh -t inst@196.200.219.$i '
./ controlvm acpipowerbutton
./ modifyvm --macaddress1 auto
./ startvm --type headless'

Automatic Ubuntu desktop installation

We received 12 desktops with Windows in various states of repair, and decided to reformat them all and install Ubuntu. Since there were several hardware variations, I did this using Debian/Ubuntu installation preseeding instead of system imaging.

Preseed options which need to be decided before networking is initialised cannot be stored in the preseed file, so they must be passed to the kernel on the command line:

  • debian-installer/locale=en_US
  • netcfg/
  • console-setup/ask_detect=false
  • console-setup/layoutcode=us

This is the pxelinux configuration that we used:

label ubuntu
  menu label Install ^Ubuntu (10.04.4 alternate i386)
  kernel ubuntu-10.04.4-alternate-i386/linux
  append initrd=ubuntu-10.04.4-alternate-i386/initrd.gz url= netcfg/ debian-installer/locale=en_US console-setup/ask_detect=false console-setup/layoutcode=us
  menu default

Using preseeding, we were able to automate almost all of the installation process, providing a nearly unattended install. I wasn't able to preseed the partition selection for some reason; the instructions did not appear to work.

This is the preseed configuration file we used:

d-i netcfg/wireless_wep string
d-i mirror/country string enter information manually
d-i mirror/http/hostname string
d-i mirror/http/directory string /ubuntu-10.04.4-alternate-i386/ubuntu/
d-i mirror/http/proxy string
d-i partman-auto/init_automatically_partition select Guided - use entire disk
d-i partman-auto/disk string /dev/discs/disc0/disc
d-i partman-auto/method string regular
d-i partman-auto/purge_lvm_from_device boolean true
d-i partman-lvm/confirm boolean true
d-i partman-auto/choose_recipe \
       select All files in one partition (recommended for new users)
d-i partman/confirm_write_new_label boolean true
d-i partman/choose_partition \
       select Finish partitioning and write changes to disk
d-i partman/confirm boolean true
d-i clock-setup/utc boolean true
d-i clock-setup/ntp boolean true
d-i time/zone string Africa/Banjul
d-i passwd/user-fullname string AfNOG User
d-i passwd/username string afnog
d-i passwd/user-password password afnog
d-i passwd/user-password-again password afnog
d-i user-setup/allow-password-weak boolean true
d-i user-setup/encrypt-home boolean false
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
tasksel tasksel/first multiselect standard, ubuntu-desktop
popularity-contest popularity-contest/participate boolean true
d-i pkgsel/update-policy select unattended-upgrades
d-i finish-install/reboot_in_progress note
xserver-xorg xserver-xorg/autodetect_monitor boolean true
xserver-xorg xserver-xorg/config/monitor/lcd boolean true

I ran into this issue while preseeding the Lucid desktop install: the installer reports an error and refuses to proceed:

http://server/ubuntu/lucid/dists/lucid/restricted/binary-i386/Packages was corrupt

The solution presented there basically worked. The steps I used were:

sudo cp -a ubuntu-10.04.4-alternate-i386/dists/lucid/restricted/binary-i386/ lucid-restricted-binary-i386.bugfix
cd lucid-restricted-binary-i386.bugfix/
sudo gunzip Packages.gz 
cd ..
sudo mount --bind lucid-restricted-binary-i386.bugfix ubuntu-10.04.4-alternate-i386/dists/lucid/restricted/binary-i386/

If you use a Squid cache in front of your Ubuntu repository web server, for caching security updates, then it will cache the corrupted Packages file that you deleted in the above step, and the error will persist. I recommend you exclude the local web server from your Squid cache like this:

acl localweb dstdomain
cache deny localweb

Oct. 5, 2012, 9:47 a.m. - Ahmet

Excellent post.This obviously is for host port fodiarrwng, but is there an internal mechanism to VBox that would allow guest port fodiarrwng. In my particular case, I run an app server on my host machine, and then use VMs to replay automated test scripts against that appserver. Accessing the app server (host:8080) is easy and doesn't require fodiarrwng, but the automated playback tool also runs on the host machine. The clients are executed in the vm and don't allow host address overriding (hard coded to localhost:XXXX). Is there a way that I can generically instruct the guest vm to forward (guest) localhost:XXXX to host:XXXX thru virtual box, or is that really up to the operating systems of the guests?

Oct. 9, 2012, 9:58 a.m. - Chris Wilson

Hi Ahmet, I think that's up to the operating systems of the guests. VirtualBox can't really interfere with them because it doesn't understand or care about their OS. The guest driver could do it, if installed, but currently doesn't. Also it's not necessary because there are other ways to achieve the same effect, by configuring the guest OS. The host port forwarding described here is necessary because there's no other way to access the ports of a guest OS using NAT networking from outside. That's probably why the feature doesn't exist and won't be added. Cheers, Chris.