PXE booting a diskless Ubuntu 7.10 (Gutsy Gibbon) client

Ubuntu at the Library Screenshot I maintain a network with 10 public access computers for the Livingston-Park County Public Library with plans to buy several more in the next few weeks. Until recently all of these computers were running Windows XP and were secured using software provided by the Gates Foundation. Despite being fully patched, locked down and running up-to-date antivirus software, malware still found a way to infect these systems. Applying updates every Patch Tuesday on every computer was consuming most of my time. Even a simple change required me to sit down at each system and make that change, usually fighting the security policy to make it stick.

A few days ago Windows started showing signs of corruption so I decided it was time to reinstall the OS. This time I considered securing them with Microsoft SteadyState, a free public access computer security tool. I recently used SteadyState on the new laptops and immediately remembered the problems with Adobe Flash and JAVA. Instead of rebuilding the systems with Windows, I decided to use Ubuntu as I was already familiar with it and 7.10 (Gutsy Gibbon) was recently released. One hour later I had downloaded, burned, installed and updated Ubuntu on the first computer. At this point I decided to make it boot via PXE. Booting Ubuntu via PXE would allow for one update on one system and all systems would be affected. This means no more wasted time typing the same command on every computer. Hard to argue with that.


Requirements

  • A server (preferrably running Linux) capable of running a nfs server and tftpd server (I chose CentOS 4.5)
  • Plenty of disk space on the server to hold the full client filesystem (recommend at least 10GB)
  • Minimum 100Mbps network connection (1Gbps recommended)
  • A client system with Ubuntu installed and a public user named "public"

Setting up the server
Install the DHCP server, SysLinux and the NFS server as root
yum install dhcp syslinux nfs-utils portmap
Download, compile and install the TFTP server
wget http://www.kernel.org/pub/software/network/tftp/tftp-hpa-0.48.tar.gz
tar -xzvf tftp-hpa-0.48.tar.gz
cd tftp-hpa-0.48
./configure
make
make install
cp tftp-xinetd /etc/xinetd.d/tftp

Configure DHCP server
Your DHCP server needs to be configured to offer the boot file "/tftpboot/pxelinux.0" from the specified TFTP server. In this example, the TFTP server is 192.168.252.1, specified by the "next-server" option.

# /etc/dhcpd.conf
allow booting;
allow bootp;
authoritative;
default-lease-time 86400;
max-lease-time 86400;
get-lease-hostnames false;
use-host-decl-names on;
ddns-update-style none;
subnet 192.168.252.0 netmask 255.255.255.0 {
option domain-name "lpcpublib.local";
option subnet-mask 255.255.255.0;
option broadcast-address 192.168.252.255;
option domain-name-servers 208.67.222.222,208.67.220.220;
option routers 192.168.252.1;
range 192.168.252.100 192.168.252.199;
next-server 192.168.252.1;
filename "/pxelinux.0";
}

Start the DHCP server and set it to start at boot. Watch for any errors when starting dhcpd.

/etc/init.d/dhcpd start
chkconfig dhcpd on

Configure TFTP server
Edit the tftp entry for xinetd to use larger blocks,enable the service and add verbosity.

# /etc/xinetd.d/tftp
service tftp
{
socket_type             = dgram
protocol                = udp
wait                    = yes
user                    = root
server                  = /usr/sbin/in.tftpd
server_args             = -v -B 8192 -s /tftpboot
disable                 = no
per_source              = 11
cps                     = 100 2
flags                   = IPv4
}

Then restart xinetd and make sure it is set to start at boot.

/etc/init.d/xinetd restart
chkconfig xinetd on

Configure SysLinux
We need to download syslinux for the extra components. This step isn't critical but allows you to build a nice menu system. If you don't need a boot menu then you can skip to copying files to the "/tftpboot" directory.

wget http://www.kernel.org/pub/linux/utils/boot/syslinux/syslinux-3.54.tar.gz
tar -xzvf syslinux-3.54.tar.gz
cd syslinux-3.54
make install

Next you need to create your "/tftpboot" directory and place the syslinux components in it.

mkdir -p /tftpboot/pxelinux.cfg
mkdir /tftpboot/com32
cp /usr/lib/syslinux/pxelinux.0 /tftpboot/
cp /usr/lib/syslinux/vesamenu.c32 /tftpboot/com32/
mkdir /tftpboot/libubuntu

Configure NFS server
Create your "/nfsroot" directory and a directory for the new filesystem

mkdir -p /nfsroot/libubuntu

Create the NFS export for the new filesystem. In this example, 192.168.252.0 is the network to be granted access.

# /etc/exports
/nfsroot/libubuntu      192.168.252.0/255.255.255.0(rw,no_root_squash,async)

Export it for use by NFS clients

exportfs -vr

Start your NFS server and set to run at boot

/etc/init.d/nfs start
chkconfig nfs on

Create kernel and initrd images
On the client system, install initramfs-tools and the NFS client utilities

sudo apt-get install initramfs-tools nfs-client

Configure the system to use NFS boot instead of local

# /etc/initramfs-tools/initramfs.conf
#
# initramfs.conf
# Configuration file for mkinitramfs(8). See initramfs.conf(5).
#
#
# MODULES: [ most | netboot | dep | list ]
#
# most - Add all framebuffer, acpi, filesystem, and harddrive drivers.
#
# dep - Try and guess which modules to load.
#
# netboot - Add the base modules, network modules, but skip block devices.
#
# list - Only include modules from the 'additional modules' list
#
MODULES=most
# BUSYBOX: [ y | n ]
#
# Use busybox if available.
#
BUSYBOX=y
#
# NFS Section of the config.
#
#
# BOOT: [ local | nfs ]
#
# local - Boot off of local media (harddrive, USB stick).
#
# nfs - Boot using an NFS drive as the root of the drive.
#
BOOT=nfs
#
# DEVICE: ...
#
# Specify the network interface, like eth0
#
DEVICE=eth0
#
# NFSROOT: [ auto | HOST:MOUNT ]
#
NFSROOT=auto

Find out what your kernel version, copy it and build the initrd.img.

ls -la /boot/vmlinuz*
-rw-r--r--  1 root root 1764280 Oct 14 19:39 /boot/vmlinuz-2.6.22-14-generic
cp /boot/vmlinuz-2.6.22-14-generic ~/
mkinitramfs -o ~/initrd.img-2.6.22-14-generic

Copy your initrd.img and kernel to the server's "/tftpboot" directory.

scp ~/vmlinuz-2.6.22-14-generic ~/initrd.img-2.6.22-14-generic root@192.168.252.1:/tftpboot/libubuntu

Edit your network interface settings to not use DHCP.

# /etc/network/interfaces
auto lo
iface lo inet loopback
iface eth0 inet manual

Copy the filesystem to the NFS server

sudo mount -tnfs 192.168.252.1:/nfsroot/libubuntu /mnt
sudo cp -avx / /mnt/
sudo cp -avx /dev/ /mnt/dev/

Shutdown the client

sudo shutdown -h now

On the server, change the "/nfsroot/libubuntu/etc/fstab" file. The "tmpfs" entries allow for system-specific config to reside in memory instead of creating race conditions. It also allows anything a public user does to the system to be destroyed on reboot.

# /nfsroot/libubuntu/etc/fstab
#
# <file system> <mount point>           <type>      <options>                                       <dump>  <pass>
proc            /proc                   proc        defaults,noatime                                0       0
/dev/nfs        /                       nfs         defaults,rw,hard,intr,async,noatime,nodiratime  1       1
none            /tmp                    tmpfs       defaults,size=50M                               0       0
none            /var/run                tmpfs       defaults,size=512k                              0       0
none            /var/lib/gdm            tmpfs       defaults,size=100k                              0       0
none            /var/lib/nfs            tmpfs       defaults,size=16k                               0       0
none            /var/lib/xkb            tmpfs       defaults,size=16k                               0       0
none            /var/lib/alsa           tmpfs       defaults,size=16k                               0       0
none            /var/lib/acpi-support   tmpfs       defaults,size=32k                               0       0
none            /var/lock tmpfs       defaults,size=96k                               0       0
none            /var/log                tmpfs       defaults,size=1024k                             0       0
none            /var/tmp                tmpfs       defaults,size=512k                              0       0
none            /home/public tmpfs       defaults,mode=755,uid=1001,gid=1001,size=50M    0       0
none            /media                  tmpfs       defaults,mode=755                               0       0
none            /root                   tmpfs       defaults,mode=755                               0       0
/dev/scd0       /media/cdrom0           udf,iso9660 noauto,users,exec                               0       0
/dev/fd0        /media/disk             ext2,vfat   noauto,users,exec                               0       0

Edit "/tftpboot/pxelinux.cfg/default" and add the new entry for our kernel

# /tftpboot/pxelinux.cfg/libubuntu
menu title Livingston-Park County Public Library, PXE Main Menu
menu background graphics/main.jpg
menu tabmsgrow 22
menu cmdlinerow 22
menu endrow 24
menu color title 1;34;49 #eea0a0ff #cc333355 std
menu color sel 7;37;40 #ff000000 #bb9999aa all
menu color border 30;44 #ffffffff #00000000 std
menu color pwdheader 31;47 #eeff1010 #20ffffff std
menu color hotkey 35;40 #90ffff00 #00000000 std
menu color hotsel 35;40 #90000000 #bb9999aa all
menu color timeout_msg 35;40 #90ffffff #00000000 none
menu color timeout 31;47 #eeff1010 #00000000 none
prompt 0
noescape 1
allowoptions 0
timeout 50
default com32/vesamenu.c32
label libubuntu
menu label ^Library Ubuntu (GX280)
kernel libubuntu/vmlinuz-2.6.22-14-generic
append root=/dev/nfs initrd=libubuntu/initrd.img-2.6.22-14-generic nfsroot=192.168.252.1:/nfsroot/libubuntu ip=dhcp rw quiet splash

Configure the client hardware
Change the BIOS options on your client computer to boot from NIC/PXE. This option various depending on your hardware.

Test the install
Start the client and you should automatically get an IP then load the initial boot menu. After 5 seconds the Ubuntu kernel should start loading.


8 Responses to “PXE booting a diskless Ubuntu 7.10 (Gutsy Gibbon) client”

  1. fab

    Hi,

    I am going to test this, thanks for sharing your knowledge.

    To understand the global picture, is this correct ?

    1- you need to set up a server (PXE,DHCP,TFTP,NFS…)

    2- you need to install Ubuntu on a machine A, modify some files and copy the files onto the server

    3- diskless machines X,Y,Z can then boot via PXE and run Ubuntu in the RAM memory

    4- to install patches or new packages, you do this on Machine A and copy the files onto the server

    Is that OK ?

    Thanks,
    Fab

  2. I hope you find it useful as it has certainly saved me. To answer your questions:
    1. Yes, set up the server
    2. Install and migrate Ubuntu to a NFS share on the server
    3. They should boot from the network and run exclusively in RAM
    4. Install patches on one of the diskless systems but make sure it is the only system running at the time. All changes on one will take effect on all the others since they share their storage.

  3. Nicolas

    Hi,

    Thanks for sharing this, I work in a university and I will try to use diskless Ubuntu workstations instead of XP.

    I have a few questions:

    When we're on the "template" client, the NFS share is mounted on the server and we're ready to copy the whole system to provide it via NFS.

    -- why do you copy "/" and "/dev" in two steps?

    -- is the "/proc", "/dev", "/sys" directories really necessary on the NFS share?

    -- don't the workstations will create and populate their own "/proc" and "/sys" directory "locally" (in RAM)?

    -- what about hostname? Is the DHCP server providing it? Is it displayed on the GDM welome menu or is it the same for all workstation?

    All the best,

  4. For some reason I'm not certain of, "/dev" failed to copy with the initial copy of "/".

    All directories need to be copied as these systems don't actually run from RAM, but over the network. I should have clarified that in my last comment. If configured correctly, all writing will be done to tmpfs in RAM but it still will access the NFS share for all of it's data just as it would a local hard drive. Because of this, "/proc", "/sys" and "/dev" need to exist in the filesystem.

    The hostname can be the same on all of them, assigned by DHCP or, as I did, assigned by a script at startup. I have it assigned based on the last six of the MAC address.

    After creating this howto I realized there were a few things that didn't quite work and I haven't updated this page with the new info. I hope to do that soon if time permits.

  5. James Kate

    Why you have executed following command.
    sudo mount -tnfs 192.168.252.1:/nfsroot/libubuntu /mnt

    If my nfs server is running on the same machine, that is acing as PXE/DHCP/TFTP server , then also do we need to mount it ?

    Plz advise
    James

  6. Blaine Fleming

    Yes you do need to mount it as that step is when you copy the client filesystem to the server. You aren't copying the server to the server, just the client install. It is only used for this step and then not mounted again.

  7. Alex

    Hi,

    I was wondering if you might be able to help… I'm trying to create a similar setup. Unfortunately I found this guide after I'd got 90% through :(

    I've got everything set up except nfs. When I try to boot the client machine I get "mount call failed: 13" which suggests permission problems. This is confirmed if I try to mount the nfs share from a running box with the message "Permission denied".

    Any suggestions you might have will be greatly apprecicated.

    ty
    Alex

  8. Alex

    Ooops! I found the problem…

    I'd messed up the ip address in /etc/exports -- Just one damn digit out!

    Oh well.. it works now!!

Leave a Reply