This page permanently redirects to gemini://log.pfad.fr/2025/fde-nixos-colmena-passwordless-reboot/.

Encrypted NixOS home server with passwordless reboot

These are my notes on refurbishing a laptop with a broken screen hinge to a NixOS home server. A coworker recommended Colmena for managing NixOS on remote machines, so I decided to give it a try. I got confused by the Colmena manual, which expects NixOS to be already set up on the remote host but doesn't clearly show how to move the existing nix (remote) config inside Colmena.

=> Colmena - A simple, stateless NixOS deployment tool

Initial NixOS setup

First, NixOS must be set up on the target machine. I followed the official documentation, including full-disk encryption.

=> NixOS Manual - Installation

After the initial reboot, a couple of adjustments were needed in /etc/nixos/configuration.nix:

=> Passwordless sudo for Colmena - ibizaman's Blog

I then rebuilt the system nixos-rebuild switch and was able to log in from my main computer via ssh using the password. Once connected via ssh:

host-a = {
  deployment = {
    targetHost = "192.168.1.14";
    targetPort = 22;
    targetUser = "";
    buildOnTarget = true;
  };
  time.timeZone = "Europe/Berlin";
  imports = [
    ./host-a/configuration.nix
  ];
};

After that, colmena apply should be able to connect to the remote and deploy the config.

Bypassing SSH interactive auth

I use a TPM-backed ssh key which asks for a pin on every connection. To workaround the (documented) limitation of Colmena which requires non-interactive login, I started a ssh connection in "master mode" in another terminal. With this command running in the background, I am now able to run colmena apply.

ssh -M username@host-a

Passwordless reboot

Since I setup a full-disk encryption, I need to type the password on every boot. However I read recently on Lobster's that it was possible to skip this, when rebooting with kexec.

I first tried rebooting with systemctl kexec, but got the error

No kexec kernel loaded and autodetection failed.
Automatic loading works only on systems booted with EFI.

Since my laptop boots with a BIOS, this error was somewhat explainable. I (much later) saw, that it was a nixpkgs/systemd issue, since systemctl start kexec.target worked fine.

=> systemctl kexec stopped working - github issue on NixOS/nixpkgs

Anyway, I found a couple of helpful resources online to prepare kexec and after a lot of trial and error, I was finally able to reboot without a password!

=> pl's comment about LUKS and kexec to reboot encrypted systems, with no user interaction | flowztul/keyexec Collection of Scripts to Automatically Unlock LUKS Devices on kexec Reboot | https://ash64.eu/blog/2023/rebooting-via-kexec/ | Kexec - ArchWiki

The final systemd service looks like this (the prepare-kexec.service is managed by NixOS: it prepares an initrd image if not done already, which we are doing):

  systemd.services."kexec-load" = {
    unitConfig.DefaultDependencies = false;
    before = [ "prepare-kexec.service" ];
    path = with pkgs; [ cpio gzip kexec-tools ];
    script = (builtins.readFile ../scripts/kexec-load.sh);
    postStop = (builtins.readFile ../scripts/kexec-load-cleanup.sh);
    serviceConfig.Type = "oneshot";
    wantedBy = [ "kexec.target" ];
  };

And the kexec-load.sh reads:

#!/bin/sh

set -e
# adapted from https://github.com/flowztul/keyexec/blob/9af064a6aa4d92cc42d062398bc80754a9a6edd8/etc/default/kexec-cryptroot
# Copyright 2017, Lutz Wolf flow@0x0badc0.de
# Licensed under GPLv2 or later.

# Generate temporary initrd.img with LUKS master keys for kexec reboot

umask 0077

CRYPTROOT_TMPDIR="$(mktemp -d --tmpdir=/dev/shm initrd.XXXXXXXXXX)"

# clear the folder when exiting
cleanup() {
    shred -fu "${CRYPTROOT_TMPDIR}/initrd.img" || true
    shred -fu "${CRYPTROOT_TMPDIR}/boot/crypto_keyfile.bin" || true
    rm -rf "${CRYPTROOT_TMPDIR}"
}

trap cleanup INT TERM EXIT

p=$(readlink -f /nix/var/nix/profiles/system)
if ! [[ -d $p ]]; then
  echo "Could not find system profile for prepare-kexec"
  exit 1
fi

# prepare the boot folder
mkdir "${CRYPTROOT_TMPDIR}/boot"
cp /boot/crypto_keyfile.bin "${CRYPTROOT_TMPDIR}/boot/"

# append the boot folder to the initrd
cp "$p/initrd" "${CRYPTROOT_TMPDIR}/initrd.img"
cd "${CRYPTROOT_TMPDIR}"
find boot | cpio -H newc -o | gzip >> "${CRYPTROOT_TMPDIR}/initrd.img"

# load the new initrd (exec is needed)
exec kexec --load "$p/kernel" --initrd="${CRYPTROOT_TMPDIR}/initrd.img" --append="$(cat "$p/kernel-params") init=$p/init"

Running kexec (without exec) would succeed when running directly, but fail when run via systemctl. So the cleanup had to happen in another script (apparently trap EXIT does not work with exec), which I named kexec-load-cleanup.sh:

#!/bin/sh

PREFIX="/dev/shm/initrd."

for dir in ${PREFIX}*/; do
    shred -fu "${dir}/initrd.img" || true
    shred -fu "${dir}/boot/crypto_keyfile.bin" || true
    rm -rf "${dir}"
done

This cleanup is likely not needed since the files were created in RAM and are discarded on reboot. Better safe (with shred) than sorry since we are playing with LUKS keys.

Colmena reboot

Colmena does not expose a configuration to choose how to reboot, but directly executes reboot. So I created a reboot package in environment.systemPackages

(pkgs.writeShellScriptBin "reboot" "systemctl start kexec.target")

Et voilà, I can now run colmena apply --reboot and get a rebooted server, without user interaction!

📅 2025-01-19

=> Back to the index

=> Send me a comment or feedback

Proxy Information
Original URL
gemini://log.pfad.fr/2025/fde-nixos-colmena-passwordless-reboot
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
300.297921 milliseconds
Gemini-to-HTML Time
1.44085 milliseconds

This content has been proxied by September (ba2dc).