Hic sunt dracones!
Why am I doing all of this? Over the past few years, a lot of people have asked me to explain them what MAAS is and what is netboot. So I thought it would be a good idea to write about the problem itself and how solutions evolved over time. Before tackling things like quadratic equations, you start by learning numbers and basic arithmetic, right?
In the previous post we’ve covered RARP and got to the state where our machine was requesting /tftpboot/kernel.10.166.116.76
over TFTP.
Can we just serve a regular kernel? Not really. We need a special tagged image. It’s essentially a standard kernel with an additional header information that instructs Etherboot how to load bytes into memory and which address in needs to jump to.
Just to make things a bit retro and fun I decided to use 2.2.17
kernel from Debian Potato (that was my very first Debian around 2001-2002).
Using kernel 1.x
could be even more fun, but maybe next time.
Setting up the environment
Let’s start our QEMU VM and install Debian Potato.
❯ qemu-img create -f qcow2 potato.qcow2 5G
❯ qemu-system-i386 \
-m 512M \
-cdrom debian-2.2r0-i386-CD1.iso \
-hda potato.qcow2 \
-vga std \
-netdev user,id=net0 \
-device ne2k_pci,netdev=net0
After several minutes I got immersed in nostalgia.
Building the kernel
There is plenty of information related to kernel compilation, so I am not going to explain all the details here.
❯ apt-get install -y make gcc bin86 build-essential kernel-source-2.2.17
❯ cd /usr/src
❯ bzip2 -d kernel-source-2.2.17.tar.bz2
❯ tar xf kernel-source-2.2.17.tar
❯ cd kernel-source-2.2.17
❯ make config
❯ make dep
❯ make bzImage
Make sure to enable the following options:
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=4096
CONFIG_BLK_DEV_INITRD=y
Full kernel config
# CONFIG_EXPERIMENTAL is not set
#
# Processor type and features
#
CONFIG_M386=y
# CONFIG_M486 is not set
# CONFIG_M586 is not set
# CONFIG_M586TSC is not set
# CONFIG_M686 is not set
CONFIG_1GB=y
# CONFIG_2GB is not set
# CONFIG_MATH_EMULATION is not set
# CONFIG_MTRR is not set
CONFIG_SMP=y
#
# Loadable module support
#
CONFIG_MODULES=y
# CONFIG_MODVERSIONS is not set
# CONFIG_KMOD is not set
#
# General setup
#
CONFIG_NET=y
# CONFIG_PCI is not set
# CONFIG_MCA is not set
# CONFIG_VISWS is not set
CONFIG_X86_IO_APIC=y
CONFIG_X86_LOCAL_APIC=y
# CONFIG_SYSVIPC is not set
# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_SYSCTL=y
CONFIG_BINFMT_AOUT=y
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_MISC=y
# CONFIG_PARPORT is not set
# CONFIG_APM is not set
#
# Plug and Play support
#
# CONFIG_PNP is not set
#
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
# CONFIG_BLK_DEV_IDE is not set
#
# Please see Documentation/ide.txt for help/info on IDE drives
#
# CONFIG_BLK_DEV_HD_ONLY is not set
#
# Additional Block Devices
#
CONFIG_BLK_DEV_LOOP=y
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=4096
CONFIG_BLK_DEV_INITRD=y
# CONFIG_BLK_DEV_XD is not set
CONFIG_PARIDE_PARPORT=y
# CONFIG_PARIDE is not set
# CONFIG_BLK_DEV_HD is not set
#
# Networking options
#
CONFIG_PACKET=y
# CONFIG_NETLINK is not set
# CONFIG_FIREWALL is not set
# CONFIG_FILTER is not set
CONFIG_UNIX=y
CONFIG_INET=y
# CONFIG_IP_MULTICAST is not set
# CONFIG_IP_ADVANCED_ROUTER is not set
# CONFIG_IP_PNP is not set
# CONFIG_IP_ROUTER is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
# CONFIG_IP_ALIAS is not set
# CONFIG_SYN_COOKIES is not set
#
# (it is safe to leave these untouched)
#
CONFIG_INET_RARP=y
CONFIG_SKB_LARGE=y
#
#
#
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
#
# Telephony Support
#
# CONFIG_PHONE is not set
# CONFIG_PHONE_IXJ is not set
#
# SCSI support
#
# CONFIG_SCSI is not set
#
# I2O device support
#
# CONFIG_I2O is not set
# CONFIG_I2O_PCI is not set
# CONFIG_I2O_BLOCK is not set
# CONFIG_I2O_SCSI is not set
#
# Network device support
#
CONFIG_NETDEVICES=y
#
# ARCnet devices
#
# CONFIG_ARCNET is not set
CONFIG_DUMMY=y
# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_NET_SB1000 is not set
#
# Ethernet (10 or 100Mbit)
#
CONFIG_NET_ETHERNET=y
# CONFIG_NET_VENDOR_3COM is not set
# CONFIG_LANCE is not set
# CONFIG_NET_VENDOR_SMC is not set
# CONFIG_NET_VENDOR_RACAL is not set
CONFIG_NET_ISA=y
# CONFIG_AT1700 is not set
# CONFIG_E2100 is not set
# CONFIG_DEPCA is not set
# CONFIG_EWRK3 is not set
# CONFIG_EEXPRESS is not set
# CONFIG_EEXPRESS_PRO is not set
# CONFIG_FMV18X is not set
# CONFIG_HPLAN_PLUS is not set
# CONFIG_HPLAN is not set
# CONFIG_HP100 is not set
CONFIG_NE2000=y
# CONFIG_SK_G16 is not set
CONFIG_NET_EISA=y
# CONFIG_PCNET32 is not set
# CONFIG_APRICOT is not set
# CONFIG_CS89x0 is not set
# CONFIG_DE4X5 is not set
# CONFIG_DEC_ELCP is not set
# CONFIG_DEC_ELCP_OLD is not set
# CONFIG_DGRS is not set
# CONFIG_EEXPRESS_PRO100 is not set
CONFIG_NE2K_PCI=y
# CONFIG_TLAN is not set
# CONFIG_VIA_RHINE is not set
# CONFIG_SIS900 is not set
# CONFIG_NET_POCKET is not set
#
# Ethernet (1000 Mbit)
#
# CONFIG_SK98LIN is not set
# CONFIG_FDDI is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
# CONFIG_NET_RADIO is not set
#
# Token ring devices
#
# CONFIG_TR is not set
# CONFIG_NET_FC is not set
#
# Wan interfaces
#
# CONFIG_HOSTESS_SV11 is not set
# CONFIG_COSA is not set
# CONFIG_SEALEVEL_4021 is not set
# CONFIG_SYNCLINK_SYNCPPP is not set
# CONFIG_LANMEDIA is not set
# CONFIG_COMX is not set
# CONFIG_DLCI is not set
# CONFIG_WAN_DRIVERS is not set
# CONFIG_SBNI is not set
#
# Amateur Radio support
#
# CONFIG_HAMRADIO is not set
#
# IrDA (infrared) support
#
# CONFIG_IRDA is not set
#
# ISDN subsystem
#
# CONFIG_ISDN is not set
#
# Old CD-ROM drivers (not SCSI, not IDE)
#
# CONFIG_CD_NO_IDESCSI is not set
#
# Character devices
#
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_SERIAL=y
CONFIG_SERIAL_CONSOLE=y
CONFIG_SERIAL_EXTENDED=y
# CONFIG_SERIAL_MANY_PORTS is not set
# CONFIG_SERIAL_SHARE_IRQ is not set
# CONFIG_SERIAL_DETECT_IRQ is not set
# CONFIG_SERIAL_MULTIPORT is not set
# CONFIG_HUB6 is not set
CONFIG_SERIAL_NONSTANDARD=y
# CONFIG_COMPUTONE is not set
# CONFIG_ROCKETPORT is not set
# CONFIG_CYCLADES is not set
# CONFIG_DIGIEPCA is not set
# CONFIG_DIGI is not set
# CONFIG_ESPSERIAL is not set
# CONFIG_MOXA_INTELLIO is not set
# CONFIG_MOXA_SMARTIO is not set
# CONFIG_RISCOM8 is not set
# CONFIG_SPECIALIX is not set
# CONFIG_SX is not set
# CONFIG_RIO is not set
# CONFIG_STALDRV is not set
# CONFIG_SYNCLINK is not set
# CONFIG_N_HDLC is not set
CONFIG_UNIX98_PTYS=y
CONFIG_UNIX98_PTY_COUNT=256
# CONFIG_MOUSE is not set
#
# Joysticks
#
# CONFIG_JOYSTICK is not set
# CONFIG_QIC02_TAPE is not set
# CONFIG_WATCHDOG is not set
# CONFIG_NVRAM is not set
# CONFIG_RTC is not set
#
# Video For Linux
#
# CONFIG_VIDEO_DEV is not set
# CONFIG_DTLK is not set
#
# Ftape, the floppy tape device driver
#
# CONFIG_FTAPE is not set
#
# Filesystems
#
# CONFIG_QUOTA is not set
CONFIG_AUTOFS_FS=y
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_FAT_FS is not set
# CONFIG_MSDOS_FS is not set
# CONFIG_UMSDOS_FS is not set
# CONFIG_VFAT_FS is not set
CONFIG_ISO9660_FS=y
# CONFIG_JOLIET is not set
# CONFIG_MINIX_FS is not set
# CONFIG_NTFS_FS is not set
# CONFIG_HPFS_FS is not set
CONFIG_PROC_FS=y
CONFIG_DEVPTS_FS=y
# CONFIG_ROMFS_FS is not set
CONFIG_EXT2_FS=y
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
#
# Network File Systems
#
# CONFIG_CODA_FS is not set
CONFIG_NFS_FS=y
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
# CONFIG_SMB_FS is not set
# CONFIG_NCP_FS is not set
#
# Partition Types
#
# CONFIG_BSD_DISKLABEL is not set
# CONFIG_MAC_PARTITION is not set
# CONFIG_SMD_DISKLABEL is not set
# CONFIG_SOLARIS_X86_PARTITION is not set
# CONFIG_NLS is not set
#
# Console drivers
#
CONFIG_VGA_CONSOLE=y
# CONFIG_VIDEO_SELECT is not set
#
# Sound
#
# CONFIG_SOUND is not set
#
# Kernel hacking
#
# CONFIG_MAGIC_SYSRQ is not set
Building initrd (initial RAM disk)
The boot RAM disk is a temporary root file system loaded into memory during the early stages of the boot process. Bundled with the kernel, it contains essential drivers and modules needed to mount the real root file system. Once the real root is accessible, the system switches from the initrd to it as the main root file system.
You can learn more about the process by reading /usr/src/kernel-source-2.2.17/Documentation/initrd.txt
. For modern kernels things are much easier and starting from 2.6
kernel initrd
was replaced by initramfs
For our image we want all well-known utils to be available. One way to do it is to simply copy them from Debian Potato, but what if we want to have them without any dynamic dependencies? BusyBox to the rescue!
- Get a statically compiled
busybox
binary. Becausewget
is too old and doesn’t recognizehttps
schema. I had to download archive to my machine and serve it overhttp
with Caddy.
# https://www.busybox.net/downloads/busybox-0.60.5.tar.bz2
wget http://172.16.3.111/busybox-0.60.5.tar.bz2
bunzip2 -k busybox-0.60.5.tar.bz2
tar xf busybox-0.60.5.tar.bz2
cd busybox-0.60.5
make DOSTATIC=true
cp busybox /tmp/busybox
- Create
initrd.img.gz
mke2fs -m0 /dev/ram 4096
mount -t ext2 /dev/ram /mnt
mkdir -p /mnt/{bin,sys,dev,proc,lib}
mknod -m 622 /mnt/dev/console c 5 1
mknod -m 660 /mnt/dev/ttyS0 c 4 64
mknod -m 666 /mnt/dev/null c 1 3
cp /tmp/busybox /mnt/bin
chmod +x /mnt/bin/busybox
pushd /mnt/bin
for cmd in init cat dmesg echo ls ps whoami sh mount umount uname; do
ln -s busybox $cmd
done
popd
umount /mnt
dd if=/dev/ram of=/tmp/initrd.img bs=1k count=4096
gzip -9 /tmp/initrd.img
cp /tmp/initrd.img.gz .
Building a tagged image
In order to build a proper image we need mknbi-linux
utility from netboot
package.
You might need to insert Debian Potato CD 2 before proceeding :-)
❯ apt-cdrom add -d /cdrom
❯ apt-get update
❯ apt-get install -y netboot
❯ mknbi-linux \
--kernel=bzImage \
--ramdisk-image=/tmp/initrd.img.gz \
--append="root=/dev/ram0 rw console=ttyS0" \
--outfile=/usr/src/kernel-source-2.2.17/arch/i386/boot/kernel
Challenge: how to get the kernel out of the VM without scp
?
There are multiple ways, but I did the following on my host:
❯ modprobe nbd max_part=8
# Better to stop QEMU VM first
❯ qemu-nbd -c /dev/nbd0 /tmp/potato.qcow2
# Your partition might be different, use fdisk -l /dev/nbd0 to check
❯ mount /dev/nbd0p1 /mnt/
❯ cp /mnt/usr/src/kernel-source-2.2.17/arch/i386/boot/kernel /tmp/kernel
❯ umount /mnt
❯ qemu-nbd --disconnect /dev/nbd0
TFTP quick setup
The final step is to configure TFTP server. This configuration is not secure at all, but we are not building a production system here. I decided to install it on my host and just remove it afterwards.
❯ apt install tftpd-hpa
❯ cat /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/tftpboot"
TFTP_ADDRESS=":69"
❯ service tftpd-hpa restart
❯ service tftpd-hpa status
● tftpd-hpa.service - LSB: HPA's tftp server
Loaded: loaded (/etc/init.d/tftpd-hpa; generated)
Active: active (running)
❯ mkdir -p /tftpboot
❯ chown -R tftp:tftp /tftpboot
❯ cp /tmp/kernel /tftpboot/kernel.10.166.116.76
The moment of truth!
Let’s reuse previously created ROM and start our QEMU VM.
❯ qemu-system-i386 \
-m 128M \
-boot n \
-option-rom ne.rom \
-net nic,model=ne2k_isa \
-net tap,ifname=tap0 \
-nographic
If your RARP config, network bridge and TFTP are configured correctly, and if your tagged image is properly built, you should see the kernel being booted.
Linux Net Boot Image Loader Version 0.8.1 (netboot)
Copyright (C) 1996,1997 G. Kuhlmann and M. Gutschke
Copyright (C) 1995-1998 G. KuhlmanLinux version 2.2.17 (root@debian) (gcc versi5
...
...
RAMDISK: Compressed image found at block 0
VFS: Mounted root (ext2 filesystem).
Freeing unused kernel memory: 40k freed
init started: BusyBox v0.60.5 (2025.05.12-19:10+0000) multi-call binary
Please press Enter to activate this console.
BusyBox v0.60.5 (2025.05.12-19:10+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.
# whoami
0
For some reason initrd with i386 emulation was extremely slow for me and I failed to configure network using RARP. If you will be able to get this workining, please leave a comment!
Nevertheless thats essentially how you can prepare a bootable image. While the process has evolved over time, the core principles remain the same.
P.S. I don’t know how you feel about running a kernel that was downloaded over an unsecured network, but I personally have zero confidence in it. Hopefully next time I’ll do a post about Secure Boot and meanwhile you can check a nice project named netboot.xyz.