Automating a Debian Install and Tracking Config Changes

Purpose

My original homelab consisted of a Dell R710 which was a ton of fun to play with, but ultimately the turbojet noises and utility bill led me to bring it in to work and purchase a couple of broken Dell R610s. Between the two of them, I made one working one which has served (pun intended) me well.

In an effort to further lower my utility bill, move the level of ambient noise from annoying to quiet, and play with some new hardware I’ve decided to migrate to a small form factor (SFF) enterprise workstation. I also decided to automate as much of the install as possible and to come up with a better system for keeping track of the changes I make to configuration files.

Debian Preseed

Fortunately my distro of choice, Debian GNU/Linux, has the ability to pre-answer the installer’s questions via a preseed file. You can read more about it here. A few minutes of paring down the default preseed file led me to this:

# localization
d-i debian-installer/locale string en_US
d-i keyboard-configuration/xkb-keymap select us
d-i netcfg/choose_interface select auto

# hostname and domain
d-i netcfg/get_hostname string dungeonman
d-i netcfg/get_domain string local
d-i netcfg/hostname string dungeonman

# mirror setup
d-i mirror/country string manual
d-i mirror/http/hostname string http.us.debian.org
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string

# Users: disable root, enable a user named ryan
# Debian will automatically setup sudo
d-i passwd/root-login boolean false
d-i passwd/user-fullname string Ryan Tolboom
d-i passwd/username string ryan
# password disabled by default, preseed.sh will install SSH keys
d-i passwd/user-password-crypted password !!

# clock and timezone
d-i clock-setup/utc boolean true
d-i time/zone string US/Eastern
d-i clock-setup/ntp boolean true

# partitioning: everything on the nvme drive w/LVM
d-i partman-auto/disk string /dev/nvme0n1
d-i partman-auto/method string lvm
d-i partman-auto-lvm/guided_size string max
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-md/device_remove_md boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-auto/choose_recipe select atomic
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

# apt sources and extra packages
d-i apt-setup/non-free boolean true
tasksel tasksel/first multiselect standard
d-i pkgsel/include string openssh-server neovim dnsmasq git firmware-misc-nonfree

# grub
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
d-i grub-installer/bootdev string /dev/nvme0n1

# final configuration via script
d-i preseed/late_command string /cdrom/preseed.sh

The preseed file allows me to configure everything I need, including the installation of a few extra packages. Note that there is only one account, ryan, and it has an invalid password. This allows me to put my preseed file in a public repo without having to worry about having an exposed password hash. Initially, the only way to log in is via SSH as the authorized_keys files are set in the script that performs the final configuration. Once the system is up, I can SSH in as root to set a password for my user account so that sudo and local logins work appropriately.

I change the /etc/issue file in my config repo (second half of this post) so that it prints out the IP of my machine. You don’t want to have to go digging through DHCP logs to find out the IP of your new install.

The final configuration script simply performs a few git commands in a chroot environment so that my versions of all the config files are in the right spot and are tracked by git. More on how the root git repo is set up later, but for now lets take a look at the script:

#!/bin/sh

chroot /target /bin/bash <<"EOT"

cd /tmp
git clone https://git.sr.ht/~bosco/dungeonman
cp -r dungeonman/. /
chown -R ryan.ryan /home/ryan

EOT

You can’t easily clone into and existing directory and overwrite files, hence the recursive copy. It’s also worth observing that the Debian installer uses the BusyBox shell, so the interpreter for the script is /bin/sh instead of /bin/bash.

There are a myriad of choices for getting the Debian installer to use your preseed file including packing it into initrd, passing it as a DHCP option, passing parameters as kernel options, or having the installer prompt you. I chose the latter so that I could use the regular installer I already had on a USB stick and just add my preseed.cfg and preseed.sh files to the root directory. It took some exploring at the installer console to figure out where the USB stick gets mounted (hint, it’s /cdrom) but once I did I was able to select Advanced Options → Automated Install at the GRUB splash screen and enter file:///cdrom/preseed.cfg when prompted.

After selecting the interface I wanted to use, the installer chugs away for a few minutes before warning me it needs to reboot (you can disable that BTW). Pulling the USB stick and hitting enter, I boot to a new fresh install of my system. Not bad for a handful of key presses.

Git for Config Files

A Linux system is largely a collection of configuration files in the right places. Knowing what to put in the files and where to put them keeps Linux sysadmins employed. The best way I can think of to keep track of files as I change and adjust them is with git, so I decided to make the root the file system on my new server a git repository. I am definitely not the first or last person to have this idea.

Running git init as root, in the root directory will initialize things. To make tracking easier in / a .gitignore file can be used as an allow list. This way you only track the files you want:

*
!.gitignore
!preseed.cfg
!preseed.sh
!*/
!home/ryan/.vimrc
!home/ryan/notes.adoc
!home/ryan/.ssh/authorized_keys
!root/.ssh/authorized_keys
!usr/lib/systemd/network/10-internet.link
!etc/network/interfaces
!etc/issue
!etc/hostname

I realize this does clutter my root directory a bit, but it’s a price I’m willing to pay to make the location of the preseed files obvious to anyone who looks at it. Now when I build the system, I add my new SSH key to SourceHut and I can use git to track any changes that I make to my configs. Just looking at the .gitignore file, you can see exactly what I’ve set up so far. There’s a lot more work to be done, so expect a few more posts.