Remote disk unlocking: Difference between revisions
Add more description about authorizedKeys and hostKeys |
|||
| (14 intermediate revisions by 5 users not shown) | |||
| Line 1: | Line 1: | ||
This page describes the method for <strong>remotely</strong> unlocking LUKS / ZFS encrypted root partition during boot process. SSH or even Tor may be used to access the system. | |||
== Setup == | == Setup == | ||
Generate host key for the SSH daemon | === Add kernel modules for the network card === | ||
The network card may not work in initrd without its kernel being manually loaded by {{nixos:option|boot.initrd.availableKernelModules}}. Find out the kernel module required by checking "Kernel modules" section in the output of <code>lspci -v | grep -iA8 'network\|ethernet'</code> (<code>lspci</code> is available in {{nixos:package|pciutils}}), and add it to {{nixos:option|boot.initrd.availableKernelModules}} (not to confuse with {{nixos:option|boot.availableKernelModules}}, which is for stage 2). | |||
{{file|/etc/nixos/configuration.nix|nix|<nowiki> | |||
boot.initrd.availableKernelModules = [ ... ]; | |||
</nowiki>}} | |||
=== Generate host key === | |||
Generate host key for the SSH daemon in <code>/etc/secrets/initrd/ssh_host_ed25519_key</code> which is required. | |||
<syntaxhighlight lang="console"> | <syntaxhighlight lang="console"> | ||
| Line 9: | Line 19: | ||
# ssh-keygen -t ed25519 -N "" -f /etc/secrets/initrd/ssh_host_ed25519_key | # ssh-keygen -t ed25519 -N "" -f /etc/secrets/initrd/ssh_host_ed25519_key | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Configure SSH === | |||
Configure {{nixos:option|boot.initrd.network.ssh}}. Add the generated host key to {{nixos:option|boot.initrd.network.ssh.hostKeys}} and your public key to {{nixos:option|boot.initrd.network.ssh.authorizedKeys}}. | |||
{{file|/etc/nixos/configuration.nix|nix|<nowiki>boot.initrd.network = { | |||
enable = true; | |||
ssh = { | |||
enable = true; | |||
hostKeys = [ | |||
"/etc/secrets/initrd/ssh_host_ed25519_key" | |||
]; | |||
authorizedKeys = [ | |||
"ssh-rsa AAAAyourpublic-key-here..."; | |||
]; | |||
}; | |||
}; | |||
</nowiki>}} | |||
Now proceed to one of [[#Setup with Systemd]] or [[#Setup without Systemd]]. | |||
== Setup with Systemd == | |||
=== Configure systemd-networkd === | |||
{{nixos:option|boot.initrd.systemd.network}} has a syntax similar to {{nixos:option|systemd.network}}. For details, see [[Systemd/networkd]]. | |||
First find the interface name(s) (<code>eth0</code> in this example): | |||
<syntaxhighlight lang="console"> | |||
# ip addr | |||
lo ... | |||
eth0 ... | |||
</syntaxhighlight> | |||
To configure DHCP: | |||
{{file|/etc/nixos/configuration.nix|nix|3=<nowiki> | |||
boot.initrd.systemd.network = { | |||
enable = true; | |||
networks."10-eth0" = { | |||
matchConfig.Name = "eth0"; | |||
networkConfig.DHCP = "ipv4"; | |||
# Wait until network interfaces have a routable address | |||
# https://wiki.archlinux.org/title/Systemd-networkd | |||
linkConfig.RequiredForOnline = "routable"; | |||
}; | |||
};</nowiki>|name=/etc/nixos/configuration.nix|lang=nix}} | |||
To configure static IP: | |||
{{file|/etc/nixos/configuration.nix|nix|<nowiki>boot.initrd.systemd.network = { | |||
enable = true; | |||
networks."10-eth0" = { | |||
matchConfig.Name = "eth0"; | |||
address = [ | |||
"192.168.1.123/24" | |||
"2001:db8:1234:5678::1/64" | |||
]; | |||
routes = [ | |||
{ Gateway = "192.168.1.1"; } | |||
{ Gateway = "fe80::1"; } | |||
]; | |||
# Wait until network interfaces have a routable address | |||
# https://wiki.archlinux.org/title/Systemd-networkd | |||
linkConfig.RequiredForOnline = "routable"; | |||
}; | |||
}; | |||
</nowiki>}} | |||
=== Debug shell === | |||
Enable debug shell. Although it is not required, enabling the debug shell allows you to enter the debug shell by press <code>Ctrl+Alt+F9</code> during Stage 1. | |||
{{file|/etc/nixos/configuration.nix|nix|<nowiki> | |||
boot.kernelParams = [ "rd.systemd.debug_shell=1" ]; | |||
</nowiki>}} | |||
The network and SSH status can be checked from within the debug shell: | |||
<syntaxhighlight lang="console"> | |||
# networkctl status: show systemd-networkd status | |||
# journalctl -u sshd | |||
# cat /etc/ssh/sshd_config | |||
</syntaxhighlight> | |||
=== Automatic password prompt === | |||
To automatically be prompted for a password when logging in via SSH, add <code>command="systemctl default"</code> to {{nixos:option|boot.initrd.network.ssh.authorizedKeys}}. | |||
{{file|/etc/nixos/configuration.nix|diff|<nowiki> | |||
boot.initrd.network = { | |||
enable = true; | |||
ssh = { | |||
enable = true; | |||
hostKeys = [ | |||
"/etc/secrets/initrd/ssh_host_ed25519_key" | |||
]; | |||
authorizedKeys = [ | |||
- "ssh-rsa AAAAyourpublic-key-here..."; | |||
+ ''command="systemctl default" ssh-rsa AAAAyourpublic-key-here...'' | |||
]; | |||
}; | |||
}; | |||
</nowiki>}} | |||
== Setup without Systemd == | |||
Enable SSH daemon in initrd | Enable SSH daemon in initrd | ||
| Line 14: | Line 131: | ||
{{file|/etc/nixos/configuration.nix|nix|<nowiki> | {{file|/etc/nixos/configuration.nix|nix|<nowiki> | ||
boot.initrd = { | boot.initrd = { | ||
systemd.enable = false; | |||
availableKernelModules = [ "r8169" ]; | availableKernelModules = [ "r8169" ]; | ||
network = { | network = { | ||
| Line 26: | Line 144: | ||
}; | }; | ||
postCommands = '' | postCommands = '' | ||
# | # unlock LUKS encrypted partitions | ||
echo 'cryptsetup-askpass | echo 'cryptsetup-askpass'</nowiki> >> <nowiki>/root/.profile | ||
# unlock ZFS encrypted partitions (NOTE: boot.initrd.supportedFilesystems.zfs must be true for zfs, zpool to be available here) | |||
# zpool import -a; | |||
# echo 'zfs load-key -a'</nowiki> >> <nowiki>/root/.profile | |||
# exit SSH | |||
echo 'exit'</nowiki> >> <nowiki>/root/.profile | |||
''; | ''; | ||
}; | }; | ||
}; | }; | ||
</nowiki>}} | </nowiki>}} | ||
{{Info|When using the systemd initrd (<code>boot.initrd.systemd.enable</code>, which is enabled by default starting with NixOS 26.05), <code>cryptsetup-askpass</code> is not available; use <code>systemctl default</code> instead. See the [https://nixos.org/manual/nixos/unstable/release-notes#sec-release-26.05 release notes] for more information.}} | |||
Adapt following parts according to your setup | Adapt following parts according to your setup | ||
| Line 44: | Line 169: | ||
The <code>postCommands</code> option is necessary to get a password prompt instead of a shell. | The <code>postCommands</code> option is necessary to get a password prompt instead of a shell. | ||
If you omit it, you will get dropped into <code>/bin/ash</code>, and you will have to manually run <code>cryptsetup-askpass</code> to enter the password. Alternatively, the <code>boot.initrd.systemd.users.root.shell</code> option can be set to <code>/bin/conspy</code> for passwords which expect stdin. This binary included by default, and provided by busybox. | If you omit it, you will get dropped into <code>/bin/ash</code>, and you will have to manually run <code>cryptsetup-askpass</code> to enter the password. Alternatively, the <code>boot.initrd.systemd.users.root.shell</code> option can be set to <code>/bin/conspy</code> for passwords which expect stdin. This binary included by default, and provided by busybox. | ||
== Usage == | == Usage == | ||
| Line 57: | Line 183: | ||
== Tips and tricks == | == Tips and tricks == | ||
=== | === Remote bcachefs unlocking === | ||
Starting with NixOS 26.05, the bcachefs module (boot.supportedFilesystems = [ "bcachefs" ]) automatically creates an unlock-bcachefs-<mountpoint>.service in the systemd initrd for boot-critical bcachefs filesystems. This service calls systemd-ask-password and pipes the response to bcachefs unlock, running before the generated sysroot.mount unit. | |||
For remote unlocking via SSH, set the initrd root shell to systemd-tty-ask-password-agent --watch, which picks up the pending password request and displays the prompt over the SSH connection. The agent must be added to the initrd's /bin via extraBin, as it is not included by default. | |||
{{file|/etc/nixos/configuration.nix|nix|<nowiki> | {{file|/etc/nixos/configuration.nix|nix|<nowiki> | ||
boot.initrd.systemd = let | boot.initrd.systemd = let | ||
unlockShell = pkgs.writeShellScriptBin "bcachefs-unlock-shell" '' | |||
keyctl link @u @s | keyctl link @u @s 2>/dev/null || true | ||
exec systemd-tty-ask-password-agent --watch | |||
''; | ''; | ||
in { | in { | ||
enable = true; | enable = true; | ||
initrdBin = with pkgs; [ keyutils ]; | initrdBin = with pkgs; [ keyutils ]; | ||
extraBin = { | |||
users.root.shell = " | "systemd-tty-ask-password-agent" = "${lib.getExe' pkgs.systemd "systemd-tty-ask-password-agent"}"; | ||
"bcachefs-unlock-shell" = "${lib.getExe unlockShell}"; | |||
}; | |||
users.root.shell = "/bin/bcachefs-unlock-shell"; | |||
}; | }; | ||
</nowiki>}} | </nowiki>}} | ||
=== Wireguard in initrd === | === Wireguard in initrd === | ||
| Line 114: | Line 239: | ||
=== Tor in initrd === | === Tor in initrd === | ||
==== Prepare the Onion ID ==== | ==== Prepare the Onion ID ==== | ||
| Line 126: | Line 249: | ||
To create these files: | To create these files: | ||
$ nix-shell -p mkp224o --command "mkp224o-donna | $ nix-shell -p mkp224o --command "mkp224o-donna a -n 1 -d ." | ||
set workdir: ./ | set workdir: ./ | ||
nixuum6flqthv6ar52j5e2ldulylfsfgezykeg37iy74kqowcp5gxfyd.onion | nixuum6flqthv6ar52j5e2ldulylfsfgezykeg37iy74kqowcp5gxfyd.onion | ||
| Line 133: | Line 256: | ||
hostname hs_ed25519_public_key hs_ed25519_secret_key | hostname hs_ed25519_public_key hs_ed25519_secret_key | ||
==== Setup Tor ==== | ==== Setup Tor (systemd stage1, since NixOS 26.05) ==== | ||
Since version 26.05, NixOS uses systemd stage1 in initrd. | |||
The following module starts a tor daemon in the initrd and uses it to expose the ssh port on an onion address.<syntaxhighlight lang="nixos"> | |||
{ config, pkgs, ... }: | |||
let | |||
onionDir = "/etc/tor/onion/bootup"; | |||
initrdTorRc = (pkgs.writeText "tor.rc" '' | |||
DataDirectory /etc/tor | |||
ShutdownWaitLength 0 | |||
HiddenServiceDir ${onionDir} | |||
HiddenServicePort ${builtins.toString config.boot.initrd.network.ssh.port} | |||
''); | |||
in | |||
{ | |||
boot.initrd = { | |||
secrets = { | |||
"${onionDir}" = /etc/secrets/initrd/onion; # Adapt to the location of your onion keys | |||
}; | |||
systemd = { | |||
initrdBin = [ pkgs.tor ]; | |||
storePaths = [ initrdTorRc ]; | |||
services."tor" = { | |||
description = "Tor daemon"; | |||
preStart = '' | |||
echo "tor: preparing onion keys" | |||
chmod -R 700 /etc/tor | |||
''; | |||
script = '' | |||
echo "tor: starting tor" | |||
tor -f ${initrdTorRc} --verify-config | |||
tor -f ${initrdTorRc} | |||
''; | |||
unitConfig.DefaultDependencies = false; | |||
wantedBy = [ "initrd.target" ]; | |||
after = [ | |||
"network.target" | |||
"initrd-nixos-copy-secrets.service" | |||
]; | |||
before = [ "shutdown.target" ]; | |||
conflicts = [ "shutdown.target" ]; | |||
}; | |||
}; | |||
}; | |||
} | |||
</syntaxhighlight> | |||
==== Setup Tor (pre NixOS 26.05) ==== | |||
Now that you have your 3 files, you have to script a bit, but it’s not too complicated. | Now that you have your 3 files, you have to script a bit, but it’s not too complicated. The snippet is adapted from [https://cgit.euer.krebsco.de/makefu/stockholm/src/commit/9b1008814e981dc01afe9ee7446322ad512c1d72/krebs/2configs/tor/initrd.nix krebs/2configs/tor/initrd.nix in stockholm]. | ||
<syntaxhighlight lang="nix"># copy your onion folder | <syntaxhighlight lang="nix"># copy your onion folder | ||