Skip to content

Instantly share code, notes, and snippets.

@darron
Created August 24, 2026 16:19
Show Gist options
  • Select an option

  • Save darron/5c5713301c80164a5e41d1969718fef6 to your computer and use it in GitHub Desktop.

Select an option

Save darron/5c5713301c80164a5e41d1969718fef6 to your computer and use it in GitHub Desktop.
Remote LUKS unlock over SSH on Omarchy

Remote LUKS unlock over SSH on Omarchy

This procedure was tested on Omarchy with UEFI/Limine, a Btrfs root filesystem on LUKS, and a wired Realtek NIC. It uses Dropbear in the initramfs so SSH is available before the encrypted root is mounted.

Replace every value in <ANGLE_BRACKETS> before running a command. Do not publish a private key, disk passphrase, hostname, IP address, MAC address, or unredacted command history.

What this changes

The early boot sequence becomes:

  1. Firmware and Limine load the kernel and initramfs.
  2. The initramfs brings up wired networking.
  3. Dropbear listens on SSH port 22.
  4. The operator connects as root using a preinstalled public key.
  5. The Dropbear root shell asks for the LUKS passphrase.
  6. The SSH session closes after unlock and normal userspace boots.

The intended interface is an early-boot unlock shell, not a general-purpose root shell. That restriction is a property of the installed mkinitcpio-dropbear/encryptssh hook and its configuration, not a guarantee provided by Dropbear itself. Verify the generated image and test the behavior after package updates. The normal SSH service still needs to work separately after boot.

Assumptions and limits

This exact recipe assumes:

  • Omarchy using a busybox-style mkinitcpio configuration with the classic encrypt hook.
  • A Limine UKI built with limine-mkinitcpio.
  • Wired Ethernet available during boot.
  • The root LUKS device and ordinary local passphrase unlock already work.

Do not apply this hook rewrite unchanged to a system using a systemd initramfs and sd-encrypt; that needs a different configuration.

Security boundary

This exposes a privileged SSH service before the encrypted root filesystem is mounted. Normal userspace firewall rules are not active yet, so the host firewall does not protect the early Dropbear listener. Use this only on a trusted, access-controlled management network, or enforce an upstream ACL that allows TCP port 22 only from the intended management client. Do not expose the early service to the Internet, a guest network, or an unrestricted LAN.

LUKS protects the encrypted volume at rest; it does not authenticate the firmware, bootloader, EFI system partition, UKI, or initramfs. An attacker who can modify the boot artifacts could replace the unlock helper and capture the passphrase during the next boot. This procedure does not protect against that physical-access or evil-maid threat. A correctly configured verified boot chain can reduce that risk, but configuring one is outside this guide's scope.

1. Establish a physical recovery path and snapshot

Have a monitor/keyboard or another out-of-band console available for the first test. A failed initramfs can otherwise leave the machine inaccessible.

Open a root shell:

sudo -i

Verify that this is the writable top-level system, not an Omarchy snapshot overlay:

cat /proc/cmdline
findmnt -no SOURCE,FSTYPE,OPTIONS /
findmnt -no SOURCE,FSTYPE,OPTIONS /boot

The root output should show the normal Btrfs @ subvolume, for example:

/dev/mapper/root[/@] btrfs ... subvol=/@

If / is overlay, or the command line contains rootflags=subvol=/@/.snapshots/<number>/snapshot, reboot and select the normal top-level Omarchy entry before making changes. Do not build from the snapshot overlay.

Create an Omarchy snapshot before changing the initramfs:

omarchy-snapshot create

If this says that the root is not a Btrfs subvolume, stop and return to the normal top-level system. A snapshot is a rollback aid, not a complete backup of important data or the LUKS header. Omarchy snapshots restore the root filesystem, not /home; if /boot is a separate EFI partition, it is not rolled back by the Btrfs snapshot either. That is why the next step backs up the boot files separately.

Back up the current UKI and configuration:

install -d -m 700 /boot/remote-unlock-backup
install -o root -g root -m 600 \
  /boot/EFI/Linux/omarchy_linux.efi \
  /boot/remote-unlock-backup/omarchy_linux.efi
if [ -e /boot/limine.conf ]; then
  cp -a /boot/limine.conf /boot/remote-unlock-backup/limine.conf
fi
cp -a /etc/default/limine /etc/default/limine.before-remote-unlock

if [ -e /etc/mkinitcpio.conf.d/zz-remote-unlock.conf ]; then
  cp -a /etc/mkinitcpio.conf.d/zz-remote-unlock.conf \
    /etc/mkinitcpio.conf.d/zz-remote-unlock.conf.before
fi

2. Install the initramfs packages

On Omarchy, use its package wrapper:

omarchy pkg add \
  dropbear \
  mkinitcpio-dropbear \
  mkinitcpio-netconf \
  mkinitcpio-utils \
  mkinitcpio-nfs-utils

Verify the packages:

pacman -Q \
  dropbear \
  mkinitcpio-dropbear \
  mkinitcpio-netconf \
  mkinitcpio-utils \
  mkinitcpio-nfs-utils

3. Install a dedicated public unlock key

Do this first on the client machine. Use a dedicated key so it can be revoked without changing normal SSH access:

ssh-keygen -t rsa -b 4096 -f ~/.ssh/initramfs_unlock \
  -C 'initramfs-unlock'

Transfer only ~/.ssh/initramfs_unlock.pub to the server. Never transfer or publish ~/.ssh/initramfs_unlock.

On the server, paste the public key as exactly one physical line:

install -d -m 700 /etc/dropbear
vi /etc/dropbear/root_key
chmod 600 /etc/dropbear/root_key

Verify the key without printing it into the gist or log:

ssh-keygen -lf /etc/dropbear/root_key -E sha256
grep -c . /etc/dropbear/root_key
stat -c '%a %s bytes %n' /etc/dropbear/root_key

The mode should be 600, and the nonblank-line count should be 1. Compare the fingerprint with the client-side public key:

ssh-keygen -lf ~/.ssh/initramfs_unlock.pub -E sha256

4. Identify the NIC driver and early interface name

The name used by normal userspace is not necessarily the name available in the initramfs. Predictable names such as enp3s0 are often assigned only after udev runs; the initramfs may initially call the device eth0.

Inspect the running system:

lspci -k | grep -A3 -i ethernet
ip -br link
dmesg --ctime | grep -Ei 'renamed from|ethernet|r8169|link'

Record two values:

  • <NIC_MODULE>: the kernel driver, such as r8169.
  • <INITRAMFS_IFACE>: the name before the udev rename, such as eth0.

For example, the relevant evidence may look like:

r8169 ... eth0: RTL8168g/8111g
r8169 ... enp3s0: renamed from eth0

In that case, use r8169 as <NIC_MODULE> and eth0 as <INITRAMFS_IFACE>. Do not use enp3s0 in the early ip= parameter merely because that is what ip link shows after normal boot.

5. Add the initramfs hooks

This guide assumes the existing configuration has the classic encrypt hook. Check before editing:

grep -RInE 'HOOKS=.*(encrypt|sd-encrypt)' \
  /etc/mkinitcpio.conf /etc/mkinitcpio.conf.d 2>/dev/null

Create a late drop-in. Replace r8169 below with your actual <NIC_MODULE>:

vi /etc/mkinitcpio.conf.d/zz-remote-unlock.conf

Use this content:

MODULES+=(r8169)

_remote_hooks=()
for _remote_hook in "${HOOKS[@]}"; do
  case "$_remote_hook" in
    encrypt) _remote_hooks+=(netconf dropbear encryptssh) ;;
    netconf|dropbear|encryptssh) ;;
    *) _remote_hooks+=("$_remote_hook") ;;
  esac
done
HOOKS=("${_remote_hooks[@]}")
unset _remote_hooks _remote_hook

The zz- prefix makes this override run after the normal Omarchy mkinitcpio drop-ins. The build should show these hooks in this order:

netconf -> dropbear -> encryptssh

Check the drop-in syntax:

bash -n /etc/mkinitcpio.conf.d/zz-remote-unlock.conf

6. Add explicit static early networking

Find the LUKS partition and a stable device identifier. This recipe uses the partition's PARTUUID. PARTUUID identifies the partition, while UUID for a crypto_LUKS partition identifies the LUKS container; do not substitute one for the other without changing the cryptdevice= prefix:

lsblk -o NAME,TYPE,FSTYPE,UUID,PARTUUID,MOUNTPOINTS
blkid -s PARTUUID -o value /dev/<LUKS_PARTITION>

The static ip= form is:

ip=<INITRAMFS_HOST_IP>::<GATEWAY_IP>:<NETMASK>::<INITRAMFS_IFACE>:none

For example, if the address assigned to the machine being unlocked is 192.0.2.50, the gateway is 192.0.2.1, the netmask is 255.255.255.0, and the early interface is eth0, the parameter is:

ip=192.0.2.50::192.0.2.1:255.255.255.0::eth0:none

The 192.0.2.0/24 values above are documentation-only examples. Replace them with values from the target network and ensure the address is reserved or otherwise cannot conflict.

Edit the existing Limine defaults rather than creating a second conflicting ip= parameter:

vi /etc/default/limine

Inspect the existing KERNEL_CMDLINE[default] assignment first. Preserve its existing cryptdevice, root, rootflags, resume, graphics, and other required arguments. Replace any old ip=... token with the new static value; do not add a second conflicting ip= token or duplicate the root arguments.

A complete minimal Omarchy-style command-line assignment, for a system that does not already have one, looks like this:

ESP_PATH="/boot"
KERNEL_CMDLINE[default]+="cryptdevice=PARTUUID=<LUKS_PARTUUID>:root root=/dev/mapper/root rootflags=subvol=@ rw ip=<INITRAMFS_HOST_IP>::<GATEWAY_IP>:<NETMASK>::<INITRAMFS_IFACE>:none rootfstype=btrfs"

Replace all angle-bracket placeholders before saving. Treat that assignment as a shape, not as a line to paste over an existing Omarchy configuration. Most installations already have additional generated or locally required arguments; retain them.

The explicit static form removes DHCP and interface autodetection from the pre-boot path. If DHCP is required instead, specify the early interface explicitly rather than using bare ip=dhcp:

ip=:::::<INITRAMFS_IFACE>:dhcp netconf_timeout=30

7. Build and inspect the UKI

Check the Limine defaults and rebuild:

bash -n /etc/default/limine
limine-mkinitcpio

Treat a build error as a stop condition. The build must finish with messages equivalent to Unified kernel image generation successful and Updated: /boot/limine.conf.

Verify that the generated boot entry contains the intended early interface:

grep -n 'ip=' /boot/limine.conf

Verify that the UKI contains the driver, hooks, key, and unlock shell:

lsinitcpio -l /boot/EFI/Linux/omarchy_linux.efi | \
  grep -E 'r8169|hooks/(netconf|dropbear|encryptssh)|root_key|authorized_keys|cryptsetup_shell|usr/bin/dropbear'

The output should include equivalents of:

.../r8169.ko.zst
hooks/netconf
hooks/dropbear
hooks/encryptssh
etc/dropbear/root_key
root/.ssh/authorized_keys
usr/bin/cryptsetup_shell
usr/bin/dropbear

Do not reboot until these checks pass and a physical recovery path is ready.

8. Test the early SSH service

On the client, open a separate terminal before rebooting. Use the dedicated private key and an isolated known-hosts file because the initramfs Dropbear host key may differ from the normal OpenSSH host key:

ssh -i ~/.ssh/initramfs_unlock \
  -o IdentitiesOnly=yes \
  -o UserKnownHostsFile=/tmp/initramfs-unlock-known_hosts \
  -o StrictHostKeyChecking=accept-new \
  root@<INITRAMFS_HOST_IP>

The early Dropbear service listens on port 22 by default. The login user is root; the key is not installed for the normal user account.

After reboot, the connection should reach a prompt similar to:

Enter passphrase for /dev/<LUKS_PARTITION>:

Enter the LUKS passphrase. The input is not echoed. After a successful unlock, the initramfs SSH session normally closes and the machine continues booting. Reconnect using the normal account and hostname after userspace is ready.

On the tested Omarchy package set, the session may close with:

cryptsetup: no process found

This is noisy cleanup after a successful unlock, not an unlock failure. Treat it as harmless only when the normal SSH service returns and checks confirm that the expected /dev/mapper/root mapper and root filesystem are present. If normal boot does not complete, investigate the failure instead of assuming this message is benign.

If the passphrase is rejected, the same early SSH session may present the prompt again. If SSH times out, do not keep rebooting blindly; use the troubleshooting section below.

9. Troubleshooting and recovery

SSH times out, but the physical LUKS screen is visible

The UKI and encryption hook are running; early networking or Dropbear startup failed. Boot locally by entering the passphrase, then inspect:

dmesg --ctime | grep -Ei 'renamed from|ethernet|<NIC_MODULE>|link'

The most common cause is using the normal userspace name (enp3s0) instead of the original initramfs name (eth0). Also confirm that the driver was forced into the image with MODULES+=(<NIC_MODULE>).

The key is rejected

Compare fingerprints and permissions:

ssh-keygen -lf /etc/dropbear/root_key -E sha256
chmod 600 /etc/dropbear/root_key

Rebuild the UKI after any key change. Do not paste the private key into the server or into a gist.

The machine boots into a snapshot overlay

Select the normal top-level Omarchy Limine entry. Snapshot entries use a temporary overlay and are not the writable top-level @ system. Verify with:

findmnt -no SOURCE,FSTYPE,OPTIONS /

Only create snapshots or edit persistent initramfs configuration when the output shows the normal writable @ subvolume.

Restore the normal boot configuration

Run this only from the writable top-level system, using the backup filenames created earlier. This restores both the root-side configuration and the separately backed-up boot artifacts:

mv /etc/mkinitcpio.conf.d/zz-remote-unlock.conf \
  /etc/mkinitcpio.conf.d/zz-remote-unlock.conf.disabled
cp -a /etc/default/limine.before-remote-unlock /etc/default/limine
cp -a /boot/remote-unlock-backup/omarchy_linux.efi \
  /boot/EFI/Linux/omarchy_linux.efi
if [ -e /boot/remote-unlock-backup/limine.conf ]; then
  cp -a /boot/remote-unlock-backup/limine.conf /boot/limine.conf
fi
limine-mkinitcpio

The installed packages may remain present. Disabling the drop-in and restoring the Limine command line removes the remote-unlock hooks from any future generated image; the backup copy restores the last known-good image immediately. Verify the rebuilt image before rebooting.

Optional hardening

Pin the early Dropbear host key

The isolated UserKnownHostsFile keeps the initramfs host key separate from the normal userspace SSH key, but StrictHostKeyChecking=accept-new is trust-on-first-use. It does not protect the first connection from a man-in-the-middle attack.

For a security-sensitive deployment, obtain the expected early host-key fingerprint through a trusted local console, a trusted build/recovery process, or another authenticated channel. Store that key in the isolated known-hosts file before remote unlock and use strict checking:

ssh -i ~/.ssh/initramfs_unlock \
  -o IdentitiesOnly=yes \
  -o UserKnownHostsFile=/tmp/initramfs-unlock-known_hosts \
  -o StrictHostKeyChecking=yes \
  root@<INITRAMFS_HOST_IP>

Treat an unexpected early host-key change as a stop condition until it has been explained. Rebuilding the initramfs may legitimately change the key, depending on how the installed package provisions it; verify and repin it through the trusted channel after such a change. Do not use ssh-keyscan over an unauthenticated network as proof that a fingerprint is genuine.

Test a real recovery path

A snapshot and a copied UKI are useful rollback material, but neither is by itself a guaranteed boot path. Before operating the machine headlessly, verify at least one recovery method:

  • a physical or out-of-band console with a known local unlock procedure;
  • a known-good, selectable Limine fallback entry; or
  • bootable recovery media that can unlock the LUKS volume, mount the Btrfs root and EFI partition, and restore the saved configuration.

A backup file on the same disk is not sufficient if the EFI partition, disk, or boot configuration is damaged. A snapshot also does not roll back a separate EFI partition or guarantee that the machine can boot far enough to restore it.

Security notes

  • Use a dedicated client key and protect its private half normally.
  • The public key is copied into the initramfs and may be present on the EFI partition; it is not a secret.
  • Never write the LUKS passphrase into the key file, command line, shell history, gist, or logs.
  • Restrict early SSH reachability to a trusted, access-controlled management network. The initramfs service has fewer controls than a full OpenSSH installation, and normal userspace firewall rules do not protect it.
  • Use an isolated UserKnownHostsFile for the early service so its host key does not conflict with the normal host key. For stronger protection, use the optional strict host-key pinning procedure above.
  • A Btrfs snapshot is not a complete backup and does not protect against disk failure or accidental deletion of unrelated data.

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment