en de

Debian headless installation

Posted on Mo 28 Januar 2019 in Computer & Electronics

TLDR: For a packaged version of the process go to the github repo: github.com/philpagel/debian-headless

After my old Android media server died I was looking for a replacement. I wasn't really happy with the Android solution, anyway: All I really wanted was Kodi and control it from my smartphone. Most of the time, I didn't have a screen connected to it. Android devices only get updates for a very limited time, so you'll eventually end up with a potentially insecure old version.

So let's switch to LINUX, instead. Debian of course. First of all, I looked for hardware and ended up with a Lenovo ThinkCentre m600 tiny. It's a discontinued model and I got it really cheap online. It even looks quite nice and offers everything I need: 128GB SSD, 4GB RAM, a keyboard with a French layout (which I already disposed of) and no monitor. And, of course, no CD drive – who still needs those?

So I thought: Download a Debian-netinst image, put it on a USB stick and off you go. Well – not quite... Because being the stubborn nerd I am, I insisted on installing completely headless. The Debian installer has a nice feature called 'network-console', but out of the box, it doesn't work as expected: Before bringing up the network console, you have to interactively select locale and keymap, configure the network interface and set the hostname. This makes the whole point of a remote installation moot.

Now, the Debian installer has a very handy mechanism called preseeding. I.e. you can configure the answers to all or some of the configuration questions in a file instead of answering them one by one. If desired, you can automate the entire installation/configuration that way. But I'm only after automating everything up to the start of the network console, so that I can continue the installation interactively via ssh.

But how do I present this file to the installer? Quite simple: put it on the installation medium. Which happens to be an ISO image and requires unpacking, first. Then, you can insert your preseed file and pack everything up, again. Finally, make sure that the resulting ISO image is bootable and off you go.

On the web, I found many clever tutorials on how to do this (the best is here), but every single one of them was missing some aspect or other. Either the whole thing didn't want to boot from the USB stick, or it got stuck in the boot menu, or the preseeding was incomplete etc. etc. Bummer! I mucked around with them for a long time and tried out bits and pieces from the different tutorials until I finally got everything working.

Tool box

Before we start, let's make sure that all the necessary tools are installed:

sudo apt-get install bsdtar syslinux syslinux-utils cpio genisoimage coreutils

Unpacking the image

Extracting the ISO image is pretty simple:

# make a tmp folder
mkdir isofiles
# unpack the iso
bsdtar -C isofiles -xf debian-9.5.0-amd64-netinst.iso
# Set write permissions
chmod -R +w isofiles

Now, everything is found in folder isofiles/ and waiting to be messed with.

Boot loader config

Now, we need to create the config file for the bootloader. By default, the bootloader will wait forever for you to manually select the desired boot option: Install, Graphical install, Advanced options etc. But as the network is not up, yet there is no way to do that remotely. I.e. we have to make sure the correct boot option (installer) is selected automatically without user interaction.

So let's cd isofiles, delete the file isolinux/isolinux.cfg and and create a new one with this content:

DEFAULT install
    SAY Now booting the debian installer
LABEL install
    kernel /install.amd/vmlinuz
    append vga=788 initrd=/install.amd/initrd.gz --- quiet

Preseeding

Next, we need a preseeding file (preseed.cfg). For a typical German language installation it looks somewhat like this:

d-i debian-installer/locale select de_DE
d-i console-keymaps-at/keymap select de
d-i keyboard-configuration/xkb-keymap select de

d-i netcfg/choose_interface select auto
d-i netcfg/get_hostname string wintermute
d-i netcfg/get_domain string local
d-i netcfg/hostname string wintermute

d-i hw-detect/load_firmware boolean true

d-i network-console/password password install
d-i network-console/password-again password install
d-i network-console/start select continue

For your preferred locale, language etc. use something appropriate instead of de and de_DE (e.g. en_US, ...).

Note that this file contains only the bare essentials for the installer to make it to the remote console. You can configure a lot more as described in the Debian page linked above.

Now we have to install the preseeding file – and that's a little tricky, because the installer needs access to the file at a very early stage. In other words, we have to get it into the init RAM disk, somehow. This is how to do it:

# unpack initrd 
gunzip install.amd/initrd.gz
# add the preseed file
echo ../preseed.cfg | cpio -H newc -o -A -F install.amd/initrd
# and re-pack initrd
gzip install.amd/initrd

Update md5 sums

In order to keep the installer from complaining we have to update the MD5 sums:

    find ./ -type f -exec md5sum {} \; > md5sum.txt

Done – all required modifications are in place.

Do you want me to wrap this for you?

Finally, we need to create a bootable ISO image. But first, leave the folder (cd ..). There are different ways to build an ISO image. This one works for me:

# create iso
genisoimage -V Debian-headless \
        -r -J -b isolinux/isolinux.bin -c isolinux/boot.cat \
        -no-emul-boot -boot-load-size 4 -boot-info-table \
        -o debian-9.6.0-amd64-netinst-headless.iso isofiles
# fix MBR
isohybrid debian-9.6.0-amd64-netinst-headless.iso

The last step is essential to make the ISO image bootable.

Put it on a stick

Now we should have a bootable ISO image, and all that's left is writing it to a USB thumb drive. To find out which device corresponds to your USB stick, lsblk is your friend. Or check the output of dmesg directly after inserting the stick. Caution! For me /dev/sdc is the device of the USB-Stick – for you it may be something different. Getting this wrong is a great great way of wiping your harddrive! So be careful with your choice of device!

This is how we write to the stick:

sudo dd if=debian-9.6.0-amd64-netinst-headless.iso of=/dev/sdc bs=4k

Give it a final sync for good measure before unplugging.

If you are too cowardly to do this with dd you can use a tool like etcher to prevent disaster.

Ready to roll

Insert the USB stick into your server, power it up and wait for a while. Now you have to find the IP address of the new computer. Your DHCP server would know, e.g. in your router. If you don't use DHCP, you have to modify the preseed.cfg file to configure a static address.

If your server has 192.168.5.56, then you contact it like this:

ssh installer@192.168.5.56

In preseed.cfg we had configured the password install – but you can and should change that, of course. Now you are ready for a complete installation via ssh without physical access. Except, you may have to unplug the usb drive before rebooting, in some cases, to prevent the server from automatically rebooting into the installer instead of starting the freshly installed Linux.

Summary

Now that was a lot of work and I'm sure I will not remember all of it. So I decided to wrap it all up in a Makefile. If you are interested, you can find it here:

github.com/philpagel/debian-headless

For the sake of completeness, it should be noted that all of this only works for the "classic" or boot process. I.e. make sure, your BIOS is set to support 'legacy boot'. It does not work with UEFI boot.

Have fun installing!

Update 2022-03-26

The post is still correct, but in the meantime, I have improved a few things in the process and added support for UEFI. All this has been included in the packaged version in my Github repository. So please use that, for real world installations.

github.com/philpagel/debian-headless