Angry robot logo

Mumbling about computers

Automated Debian install

So far, anything that I've deployed in my lab has been running in a container, but when I wanted to deploy a VM I realized that I had to click through the debian netinstall and likely forget about the settings in the future.
After some investigation I remembered the classic preseeding method.

Preseeding

This method consists in pre-selecting all the answers required for the debian installer. It is a bit shitty that I am not simply using a base image and re-generating the ssh-keys and hostname on first boot, but this is widely supported as far as I know.

Choosing the presets

Presets are quite straight-forward, the customized values are:

  • Locale (keyboard, language)
  • Apt mirror & proxy
  • Disk partitioning

An interesting gotcha was that this uses /dev/vda and the defaults and docs always talk about /dev/sda; I did not know that this would be /dev/vda when using qemu, so it took me a while to figure out why the installer was complaining about an invalid rootfs.

# US locale/kbd map
d-i debian-installer/locale string en_US
d-i keyboard-configuration/xkb-keymap select us

# automatically select network interface
d-i netcfg/choose_interface select auto

# set host and domain
d-i netcfg/get_hostname string debian-pxe
d-i netcfg/get_domain string localdomain

# disable WEP dialogue
d-i netcfg/wireless_wep string

# use http.us.debian.org as mirror with no proxy
d-i mirror/country string manual
d-i mirror/http/hostname string ftp.nl.debian.org
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string  http://proxies.labs:3142

# don't make a regular user / set root password
d-i passwd/make-user boolean false
# mkpasswd -m sha-512 <pwd>
d-i passwd/root-password-crypted password $6$HDEYl/aS5d$LPmzSLK2rvbvA8C5HjavBTST8XZrXPAu2P6EPgnB5RPxpWDhrnDk7qouJ.0XSSWAeEFyl459m2zwj1N1D2NPL1

d-i clock-setup/utc boolean true
d-i time/zone string Europe/Amsterdam
d-i clock-setup/ntp boolean true

### Partitioning

# use lvm partitioning
d-i partman-auto/method string lvm
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true

# make lvm the max size
d-i partman-auto-lvm/guided_size string max
d-i partman-auto-lvm/new_vg_name string debian

# use the following partition scheme on /dev/vda
d-i partman-auto/disk string /dev/vda
d-i partman-auto/choose_recipe select boot-lvm

# /boot 500M ext4
# /var/log 500M ext4
# / 2G+ ext4
d-i partman-auto/expert_recipe string               \
    boot-lvm ::                                     \
        500 500 500 ext4                            \
            $primary{ } $bootable{ }                \
            method{ format } format{ }              \
            use_filesystem{ } filesystem{ ext4 }    \
            mountpoint{ /boot }                     \
        .                                           \
        500 500 500 ext4                            \
            $lvmok{ }                               \
            lv_name{ var_log } in_vg { debian }     \
            $primary{ }                             \
            method{ format } format{ }              \
            use_filesystem{ } filesystem{ ext4 }    \
            mountpoint{ /var/log }                  \
        .                                           \
        2048 2048 -1 ext4                           \
            $lvmok{ }                               \
            lv_name{ root } in_vg { debian }        \
            $primary{ }                             \
            method{ format } format{ }              \
            use_filesystem{ } filesystem{ ext4 }    \
            mountpoint{ / }                         \
        .

# remove any RAID partitioning
d-i partman-md/device_remove_md boolean true

# don't confirm anything
d-i partman-basicfilesystems/no_mount_point boolean false
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
# disable swap warning
d-i partman-basicfilesystems/no_swap boolean false


# install standard system with ssh-server
tasksel tasksel/first multiselect standard, ssh-server

# also install the htop package
d-i pkgsel/include string htop

# upgrade all packages
d-i pkgsel/upgrade select full-upgrade

# disable popularity contest
popularity-contest popularity-contest/participate boolean false

# force grub install to /dev/vda
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
d-i grub-installer/bootdev  string /dev/vda

# don't wait for confirm, just reboot when finished
d-i finish-install/reboot_in_progress note

Booting the autoinstall

The easiest way to boot this automated setup is to simply go to Advanced -> Automated setup in the wizard, then typing in the path to a webserver that can provide this file.. but this defeats the point of automating the setup, I will forget about the procedure in some time, and the webserver might stop existing in the meantime.

Baking the config into the image

It would be easier for me to not have to remember anything, and simply have the config values baked into the image. Here are condensed instructions from the official docs:

## Populate preseed.cfg with your defaults prior to this
mkdir -p isofiles
bsdtar -C isofiles -xf debian-9.9.0-amd64-netinst.iso 
gunzip isofiles/install.amd/initrd.gz
echo preseed.cfg | cpio -H newc -o -A -F isofiles/install.amd/initrd
gzip isofiles/install.amd/initrd
cd isofiles
md5sum $(find -follow -type f) > md5sum.txt
cd ..
genisoimage -r -J -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -o preseed-debian-9.9.0-amd64-netinst.iso isofiles/

At this point, you've added your preseed.cfg into debian-9.9.0-amd64-netinst.iso, creating preseed-debian-9.9.0-amd64-netinst.iso, but you still have to go to Advanced -> Automated setup in the wizard (although you don't need to type the webserver's address anymore).

Automatically picking an entry from the menu

isolinux.cfg: set timeout to 1
menu.cfg: remove all includes except stdmenu.cfg and txt.cfg
txt.cfg: replace contents with

label install
        menu label ^Install
        menu default
        kernel /install.amd/vmlinuz
        append vga=788 auto=true priority=critical file=/preseed.cfg initrd=/install.amd/initrd.gz --- quiet 
default install

Now, re-package the iso:

md5sum $(find -follow -type f) > md5sum.txt
cd ..
genisoimage -r -J -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -o preseed-debian-9.9.0-amd64-netinst.iso isofiles/

Source

You can find the script to apply these changes to an existing ISO in my github.

Demo

You can take a look at a recording of an install with these values: