Unattended updates with reboots on a LUKS encrypted system

On my encrypted machines I have a keyfile sitting at /luks.key with chmod 0000. Before kexec.target I append a cpio image with they keyfile to the original initrd in memory and load that instead of the default. If I remember correctly this only works with systemd-boot, I honestly forgot why, it’s been a while. Next you need to add the keyfile (/boot/luks.key in my case) path to your boot.initrd.luks.devices.<name>.keyFile config. Remember to enable fallbackToPassword for normal boots.

{
  pkgs,
  ...
}:
{
  systemd.services."kexec-load" = {
    before = [ "prepare-kexec.service" ];
    wantedBy = [ "kexec.target" ];
    path = with pkgs; [
      cpio
      zstd
      kexec-tools
    ];
    script = builtins.readFile ./load.bash;
    unitConfig = {
      ConditionPathExists = "/luks.key";
      DefaultDependencies = false;
    };
    serviceConfig.Type = "oneshot";
  };
}
#!/usr/bin/env bash

set -euxo pipefail

umask 0077

TMPDIR=$(mktemp --directory --tmpdir=/dev/shm initrd.XXXXXXXXX)

function cleanup() {
  shred -fu "${TMPDIR}/initrd" || true
  shred -fu "${TMPDIR}/boot/luks.key" || true
  rm -rf "$TMPDIR"
}

trap cleanup INT TERM EXIT

cd "$TMPDIR"

# put target files in place
mkdir -v ./boot
cp -v /luks.key ./boot/

# pack and append the initrd
PROFILE="$(readlink -f /nix/var/nix/profiles/system)"
cp -v "${PROFILE}/initrd" initrd
find . -print0 | cpio --null --create --format=newc --owner=+0:+0 | zstd >> initrd

# append initrd secrets
"$PROFILE/append-initrd-secrets" initrd

exec kexec --load "${PROFILE}/kernel" --initrd="${TMPDIR}/initrd" --append="$(cat "${PROFILE}/kernel-params") init=${PROFILE}/init"

Anyway, this fits my threat model for these machines given a certain update interval and the resulting maintenance headache. I tend to gitops my private infra these days. Push to git, CI builds, machines pull and switch/kexec/reboot as needed/possible.

4 Likes