Defcon:Blog Keklkakl blog blag


Debian Lenny based PXE boot setup

In this document I will document my base PXE boot-server setup. It is my intention to have quite a few “features” in my setup, including:

  • Menu-based selection of boot options
  • Booting of installers for several open-source operating systems
  • Booting of Live-environments for several open-source OS.
  • A selection of system-tools, like disk-shredder, partitioning tools, disk-backup and antivirus
  • Support for chainloading other net-boot mechanisms.

The setup is built on Debial Lenny, and is based on pxelinux, a part of the syslinux tools. In general, PXE-booting will be useful for booting x86/ia32-related hardware. Details related to making individual operating systems and distributions PXE bootable are left to separate articles.


Naturally, a boot-server requires some components apart from configuration files and such. Noteable components:

  • An operating system for the boot server. I am using Debian 5 “Lenny” as my server-platform
  • A TFTP daemon. From recommendations of other Debian users, I am switching to atftpd.
  • A DHCP server. As this setup is done for my home-network, this role is filled by my OpenBSD box.
  • An NFS server. I am using the same box for tftpd and nfs, and I am running nfs-kernel-server.

Installing the tftp dæmon

My choice of tftpd-dæmon fell on atftpd. This is a modern TFTP server using multi-threading, supports multicast tftp, option extension, large file-sizes and more. Particularly important for PXE is the support for the RFC2349 Transfer Size option. Installing atftpd is basically as simple as:

apt-get install atftpd

It may be noted that as a dependency, atftpd pulls in “inetutils-inetd”.

As this is done on a Debian system, the package maintainers of atftpd has placed the tftpboot (aka tftp root) directory according to Debian standard at /var/lib/tftpboot. If you are using a different platform, it is highly probable that atftpd will be referring to /tftpboot instead. On the other hand, if you are using Debian, and want /tftpboot in stead of /var/lib/tftpboot, either symlink, or edit /etc/default/atftpd to relocate.

Adding TFTP/bootpd settings to DHCP server

Note that this is from my OpenBSD DHCP server. These settings however should be directly compatible with the DHCPd in most common Linux distributions, and at least ISC DHCPd.

subnet netmask {
        option domain-name-servers;
        option routers;
        option tftp-server-name "";
        filename "pxelinux.0";

Installing syslinux to get the pxelinux files...

PXELinux, the PXE Boot environment used in my, and most other linux-oriented, net-boot-setups, is not a stand-alone package, but part of the larger syslinux project. To install, pull in the following packages:

apt-get install syslinux syslinux-common

The files we need to continue will be living in /usr/lib/syslinux. For starters, we will want to have a basic set of syslinux modules/files available. We will be adding on modules as features are added to the setup.

  • pxelinux.0 is the actual PXE environment/loader
  • menu.c32 allows us the creation of text-based menus
  • chain.c32 allows us to have “Boot from hard drive” as a boot option.

These files are to be copied from /usr/lib/syslinux to /var/lib/tftpboot. We also need the directory where pxelinux configurations will be stored.

cp /usr/lib/syslinux/{pxelinux.0,menu.c32,chain.c32} /var/lib/tftpboot
mkdir /var/lib/tftpboot/pxelinux.cfg
chmod a+rx /var/lib/tftpboot/pxelinux.cfg

Setting up a test

Our first test will be to see if our PXE environment works at all. To do this, we'll set up a “welcome message”, a chainload of the first local harddrive and a faux linux boot.

cat > /var/lib/tftpboot/boot.txt <<END
This is a PXELinux bootserver.
To boot linux, type 'linux<enter>'
To boot from local drive, press <enter>

cat > /var/lib/tftpboot/pxelinux.cfg/default <<END
DISPLAY boot.txt
DEFAULT boot_hd0

LABEL boot_hd0
	COM32 chain.c32

LABEL linux
	kernel debian/lenny/i386/linux
        append vga=normal initrd=debian/lenny/i386/initrd.gz  --


So, no menu yet, just a real simple test. Using any PXE-capable client computer, test this setup. You should be presented with quite a bit of pxelinux output, followed but the contents of boot.txt and a prompt. Trying to boot the 'linux' LABEL should naturally fail, but still confirms that things are working. Booting the default option 'boot_hd0' should boot your system normally (assuming the first BIOS drive is your boot device).

First 'real-world' boot setup

Now, we will introduce the simples PXE-bootable installer, the Debian netinstall, and a menu to select it.

We start by fetching the netboot-kit for Lenny:

mkdir /tmp/pxetmp; cd /tmp/pxetmp
tar zxvf netboot.tar.gz

mkdir -p /var/lib/tftpboot/debian/i386/lenny
cp /tmp/pxetmp/debian-installer/i386/initrd.gz var/lib/tftpboot/debian/lenny/i386
cp /tmp/pxetmp/debian-installer/i386/linux /var/lib/tftpboot/debian/lenny/i386

Now, we change /var/lib/tftpboot/pxelinux.cfg/default a little bit to start using menus. Replace the contents:

DEFAULT menu.c32
MENU TITLE PXE Boot menu 0.1

LABEL linux
        MENU LABEL Debian Lenny i386 netinstall
        KERNEL debian/lenny/i386/linux
        APPEND vga=normal initrd=debian/lenny/i386/initrd.gz  --

LABEL boot_hd0
        MENU LABEL Boot from first hard drive
        COM32 chain.c32
        APPEND hd0

ONTIMEOUT boot_hd0

Getting fancy

The setup will now introduce quite a few components:

  • “Graphical” menu using vesamenu.c32
  • A fancy background image
  • Common menu-config with color and layout settings for vesamenu.c32
  • Nested/chained menus.

We will set the basic framework using the two working options we already have in our menu to introduce the concept. After this, all elements of the sub-menus are simply variations on the basic setup we make here, and all specifics for each menu/os/installer/tool will be presented on separate pages.

Preparing vesamenu.c32

A text-based menu is swell, and probably recommended for some older platforms, but I wanted a menu with a tad of “bling” and a “modern” feel. Most of the boot-cds and images for Linux distributions these days use a VESA graphics syslinux/grub setup, and I wanted to do the same.

The menu.c32 comboot module provides the menu we used in the previous step. To move from the text-only menu, to a 'graphical' menu with a background-image and more, we simple need to use vesamenu.c32 in place of menu.c32, and add some vesamenu-specific options.

Pull in vesamenu.c32 from the syslinux packages:

cp /usr/lib/syslinux/vesamenu.c32 /var/lib/tftpboot

Next, get/create a suitable background image to use, and place it in the tftpboot-hierarchy. This is the background image I started out with, stored as /var/lib/tftpboot/menus/background.png


The specs for image-files that vesamenu.c32 accepts are fairly simple: A 640×480 pixels PNG or JPG image, displayable at 16bpp. So, more or less any 640×480 PNG will do.

Some words about directory hierarchy layout

Since I am going to be putting rather large amounts of files in both TFTP and NFS shares, it does not hurt to have a plan for structuring content.

For the tftpboot directory:

Directory Use
/var/lib/tftpboot/ Root of TFTP data, and location for pxelinux/syslinux binaries
/var/lib/tftpboot/pxelinux.cfg/ Contains the default cfg-file
/var/lib/tftpboot/menus/ Location for all files used to create the actual pxelinux-menus
/var/lib/tftpboot/menus/$dist.cfg Sub-menu for a given OS distribution
/var/lib/tftpboot/$dist/$ver/$arch Files needed for TFTP-boot of OS Installer
/var/lib/tftpboot/live/$dist/$ver/$arch Files needed for TFTP boot of Live-system
Directory Use
/srv/boot/install/$dist/$ver/$arch NFS share for OS installer files
/srv/boot/live/${DIST}/$dist/$ver/$arch NFS share for Live-boot OS files

So, a 32-bit Ubuntu 10.4 Live environment will use the paths:

  • /var/lib/tftpboot/menus/ubuntu.cfg for its menu-option
  • /var/lib/tftpboot/live/ubuntu/10.4/i386 for kernel- and initrd
  • /srv/boot/live/ubuntu/10.4/i386 as NFS root for the Live-environment.

Naturally, some installers will need various degrees of “fixed location” for files, but the above is the “standard” that these deviate from.

Common configuration

Aiming to use multiple sub-menus, it will be very useful to gather all common configuration to one location, and avoid having to retype/copy the same settings to all menu-configurations. Luckily the syslinux menu system allows us to include settings from different files, and this is the mechanism we'll use to centralize common settings.

My common configuration looks like this:

# /var/lib/tftpboot/menus/common.cfg
MENU BACKGROUND menus/background.png
MENU COLOR BORDER       30;44   #00000000 #00000000 none
MENU COLOR SEL          7;37;40 #ffffffff #9090a0f0 std
MENU COLOR HOTSEL       7;37;40 #ffffffff #204040f0 std
MENU COLOR TIMEOUT_MSG  37;40   #aaaaaaaa #00000000 std
MENU COLOR TIMEOUT      1;37;40 #ffaaaaff #00000000 std

Line for line description

  • Set the background image (path relative to tftp root)
  • Disable the command/loader prompt
  • Set the width, in characters, of the menu
  • Set the height, in text-rows, of the menu.
  • Set the location of the “Press TAB” text
  • Set the location of the commandline when user presses TAB
  • Set the location of the HELP text/verbose info
  • Add a left-margin of 15 characters on the menu (move right)
  • Shift the menu 4 text-rows down from default location
  • Remove the border of the menu
  • Set the colors and shading of the selection-bar
  • Set the colors and shading of the menu hotkeys when selected
  • Set the color of the “Autoboot in” string
  • Set a different color for the TIME indicator.
  • Add a fixed “space” between the header and the menu items.

Naturally, more look-and-feel options may be added here. Also, settings like password-protection are prime candidates for the common.cfg file.

Nested menus

So, we have our common configuration and COM32 modules ready. Let us modify pxelinux.cfg/default completely, and let it become our main menu. There are quite a few ways to build a complex menu system with syslinux. One approach is to use “MENU TITLE” to create submenus directly in the global configuration. This has the advantage that any LABEL of the menu and submenus can be typed directly on a prompt to boot. But, in my opinion, it also makes the menu vulnerable to errors in INCLUDE'd parts of the config. Another option is to chain-load the menus. This is related, but not as good, as the third option: loading each sub-menu as a COM32 module, with a separate complete config file for each sub-menu.

This is a reliable approach, and a flexible one when debugging. Every time you select a sub-menu, or to return to the main menu, the entire config is re-read and applied. So, you can test changes to the menu by navigating back and forth in it. This naturally implies that the vesamenu.c32 and sub-menu config files will need to be transferred over TFTP every time you change menus, but in regard of the flexibility it gives for changing the menu on-the-fly is in my eyes a big plus, and the final selling point for me is the fact that a borken sub-config will not break my entire setup.

The initial main menu configuration looks like this:

# /var/lib/tftpboot/pxelinux.cfg/default
DEFAULT vesamenu.c32
MENU INCLUDE menus/common.cfg
MENU TITLE PXE Boot menu 0.1

LABEL debian
        MENU LABEL ^1 Debian 
        TEXT HELP
Debian installers and live-boot options.
        COM32 vesamenu.c32
        APPEND menus/debian.cfg


LABEL boot_hd0
        MENU LABEL Boot from first hard drive
        TEXT HELP
Boots your system as normal from the first BIOS drive
        COM32 chain.c32
        APPEND hd0

ONTIMEOUT boot_hd0

I choose to use the COM32 loader-keyword, opposed to KERNEL. Using KERNEL will normally auto-detect what type of image/boot-program it is trying to load by looking at the file suffix or magic number, it just feels more correct to say COM32 when I know that is what I am going to load.

Note that the pxelinux.cfg/default is the only configuration file in my setup that has the line “DEFAULT vesamenu.c32” included. This is because for all the sub-menus, the module is already loaded by being the actual image “booted”.

In all the sub-menus, I include a “Return to Main Menu” option on the top, to loads the default config and thus the main menu.

# /var/lib/tftpboot/menus/debian.cfg
MENU INCLUDE menus/common.cfg
MENU TITLE Debian Installers

LABEL mainmenu
        MENU LABEL ^R Return to Main Menu
        COM32 vesamenu.c32
        APPEND ~


LABEL lenny_i386_install
        MENU LABEL ^1 Debian Lenny i386 netinstall
        KERNEL debian/lenny/i386/linux
        APPEND vga=normal initrd=debian/lenny/i386/initrd.gz  --


In the menus I am extensively using hotkeys. Hotkeys are identified my a caret (^) directly infront of the character used as the hot-key. Hotkeys should be in the A-Z,0-9 range, to make sure that you are independent of misc keyboard layouts. Hotkeys can also (naturally) only be assigned once in a single menu. If you asssign the same hotkey to multiple options, they will display as hot-keys, but none of them will be selected by pressing the key.

Seeing how I load the menus using the COM32 keyword with an APPEND line, you should be able to notice that there is really no limit on how deep you can nest your menus, and that it is fully possible to use the same menu as a “sub” in multiple places.

Comments (0) Trackbacks (0)

No comments yet.

Leave a comment

Trackbacks are disabled.