Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

0002 — BE re-unlock passphrase: dropbear in the BE, entered over SSH

Status: superseded by 0003 (2026-06-04)

Superseded. ADR 0003 adopts single-prompt key-handoff across kexec (SPEC §17.1 choice (b)). The dropbear-in-the-BE mechanism below is retained as the fallback path (keyboot.handoff=0, or whenever no handoff key is present — recovery, opt-out), so the implementation here stays live; it is just no longer the primary boot path.

Context

After keyboot kexecs into the boot environment, the BE’s own initramfs re-opens the LUKS-on-ZFS pool from scratch (SPEC §5 Stage 9 / §16.1 chose option (a): the BE has its own independent keyfile/passphrase flow — the keyfile/keys are deliberately NOT carried across the kexec boundary). That flow needs the keyfile-container passphrase, and SPEC §15.1 left how the BE obtains it open. The real-hardware bring-up (Hetzner, 2026-06-04) forced the question:

  • keyboot be-unlock calls passphrase::read() → prompts on the console / reads stdin. On a headless box whose serial console is dead (common — see docs/hetzner-deploy.md), that blocks forever: the BE initramfs has no SSH, so there’s no remote way to answer.
  • The only thing that made an unattended boot work was a baked /etc/keyboot/test/passphrase in the BE initramfs. But the BE initramfs lives unencrypted on the ESP/pool metapath, so a baked passphrase defeats the encryption — fine for CI (--test-passphrase-file), unacceptable for production.

The keyboot unlock environment already solves the equivalent problem well (SPEC §5 Stages 2–4): bring up the network, run dropbear, and let the operator enter the passphrase over SSH (or the console) via a shared FIFO. The BE re-unlock should reuse that model.

Decision

The BE initramfs gets the same network + dropbear + askpass path the keyboot unlock environment uses. No baked passphrase in production.

Mechanism (mirrors the unlock env, kept minimal for the BE):

  1. be-init brings up the NIC (udhcpc, reusing the coldplug already added for AHCI) and starts dropbear with keyboot’s host keys + the operator’s authorized_keys, forced-command = an askpass that writes the entered passphrase to a FIFO.
  2. be-init runs keyboot be-unlock … < <FIFO>be-unlock’s existing passphrase::read() already reads a single line from stdin when there’s no TTY, so the SSH-entered passphrase flows straight in. The console prompt stays as a fallback (now usable thanks to the writable-console fix).
  3. --test-passphrase-file continues to short-circuit this for CI only (read_passphrase() already checks /etc/keyboot/test/passphrase first).

The operator therefore enters the passphrase twice per boot — once for the unlock env, once for the BE. Accepted for v1 (see Consequences).

Alternatives considered

  • Baked passphrase (status quo / CI): simplest, but the BE initramfs is unencrypted, so it stores the passphrase in clear. Test-only. Rejected for production.
  • Console-only prompt: no new code, but blocks on headless / dead-serial boxes and offers no remote operator path. Rejected as the only mechanism; retained as a fallback.
  • Keyring handoff across kexec (SPEC §15.1 option b): keyboot stashes the keyfile/passphrase in a kernel-keyring slot the BE reads → a single prompt per boot. Attractive UX, but it carries key material across the kexec boundary, which §16.1’s v1 invariant explicitly avoids; needs a focused security review. Deferred — revisit as the single-prompt follow-up.
  • TPM-sealing the keyfile: fully unattended, but requires a TPM + a measured-boot story keyboot doesn’t have yet. Deferred.

Consequences

  • Two prompts per boot (unlock env + BE). The keyring-handoff alternative above is the documented path to one prompt later.
  • BE initramfs grows by dropbear + busybox net tooling + host/authorized keys (~a few hundred KB). Acceptable.
  • The BE initramfs now needs networking — reuses the unlock env’s udhcpc/ network grammar; static/VLAN/bond support tracks the unlock-env milestone.
  • install-os stops baking the passphrase for real installs; --authorized-keys (already added) supplies the BE’s operator key, and keyboot’s SSH host keys are reused. CI keeps --test-passphrase-file.
  • Supersedes the “baked test passphrase” stopgap recorded in docs/hetzner-deploy.md; that doc’s open item is closed by this ADR.

Implementation note

Lands in tools/keyboot-install-os/alpine.sh (be-init + the keyboot.files mkinitfs feature: dropbear, the askpass, host/authorized keys) and is covered by extending ci/qemu/install-os-boot-test.sh to feed the passphrase over SSH to the BE instead of relying on the baked file. Debian/Gentoo plugins need the equivalent be-init wiring.