feat(image): remote LUKS unlock via a Tor onion service in the initramfs

Encrypted image setups can now bake a Tor onion service into the initramfs
so the dropbear unlock shell stays reachable behind NAT or a dynamic IP.
When the user opts in, configure_encryption installs tor + busybox, drops
the mkinitcpio hooks (ordered `netconf tor dropbear encryptssh`), generates
the v3 onion keys offline in the image chroot, and prints the stable
.onion address. Unlock with `torsocks ssh root@<onion-address>`.

The runtime hook syncs the clock via NTP first (RTC-less boards boot at
1970, which Tor's consensus checks reject) and starts the onion service
pointing at dropbear on 127.0.0.1:22.

Hardening baked in from an adversarial review of the shipped path:
- cmdline.txt boot path (RPi4-class firmware boot) now sets the same
  ip=::::<host>:eth0:dhcp net.ifnames=0 params as the boot.txt path, so
  the initramfs actually gets a network and the onion can publish.
- the initramfs bakes in libnss_dns.so.2 so the NTP hostname resolves.
- the hook extracts DHCP DNS with sed instead of sourcing the lease files,
  which would run attacker-controlled DHCP option strings as root pre-boot.
- NTP is attempted unconditionally (bounded), not gated on DHCP-provided DNS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-Birkenbach
2026-07-21 18:48:56 +02:00
parent b38303b72f
commit b88878efec
10 changed files with 334 additions and 3 deletions

View File

@@ -0,0 +1,37 @@
#!/bin/bash
# mkinitcpio install hook: Tor onion service for remote LUKS unlock.
# Installed to /etc/initcpio/install/tor by linux-image-manager.
build() {
add_binary /usr/bin/tor
# Full busybox for the ntpd applet: boards without an RTC (e.g. most
# Raspberry Pis) wake up in 1970, which Tor's consensus checks reject.
# A different target path keeps mkinitcpio's own busybox untouched.
add_binary /usr/bin/busybox /usr/local/bin/busybox
# glibc resolves the NTP server hostname via getaddrinfo(), which dlopen()s
# these NSS modules at runtime — add_binary only follows NEEDED libs, so
# without them DNS silently fails, ntpd never syncs, and the clock stays
# at 1970. glibc's built-in default (no nsswitch.conf) is "dns ... files".
add_binary /usr/lib/libnss_dns.so.2
add_binary /usr/lib/libnss_files.so.2
add_file /etc/tor/initramfs-torrc /etc/tor/torrc
add_file /etc/tor/initramfs-onion/hostname /etc/tor/onion/hostname
add_file /etc/tor/initramfs-onion/hs_ed25519_public_key /etc/tor/onion/hs_ed25519_public_key
add_file /etc/tor/initramfs-onion/hs_ed25519_secret_key /etc/tor/onion/hs_ed25519_secret_key
add_runscript
}
help() {
cat <<HELPEOF
Starts a Tor onion service in early userspace so the dropbear unlock
shell stays reachable under the .onion address baked into the image,
even behind NAT or a dynamic IP.
Place it between netconf and dropbear:
HOOKS=(... netconf tor dropbear encryptssh ...)
Optional kernel parameter:
tor_ntp=<server> NTP server used to set the clock before Tor starts
(default: pool.ntp.org)
HELPEOF
}