Remote disk unlocking: Difference between revisions
imported>Riotbib Change from deprecated hostECDSAkey to hostKeys and from dropbear to ssh-keygen |
|||
(30 intermediate revisions by 12 users not shown) | |||
Line 1: | Line 1: | ||
If you want to unlock your computer remotely via SSH or even through Tor, and you are facing the problem, that you can’t reach your computer before your computer is unlocked. Tor will help you to reach your computer, even during the boot process. | |||
== Setup == | |||
Generate host key for the SSH daemon which will run in initrd during boot | |||
= | <syntaxhighlight lang="bash"> | ||
# mkdir -p /etc/secrets/initrd | |||
# ssh-keygen -t ed25519 -N "" -f /etc/secrets/initrd/ssh_host_ed25519_key | |||
</syntaxhighlight> | |||
Enable SSH daemon in initrd | |||
{{file|/etc/nixos/configuration.nix|nix|<nowiki> | |||
boot.initrd = { | |||
availableKernelModules = [ "r8169" ]; | |||
network = { | |||
enable = true; | |||
udhcpc.enable = true; | |||
flushBeforeStage2 = true; | |||
ssh = { | |||
enable = true; | |||
port = 22; | |||
authorizedKeys = [ "ssh-rsa AAAAyourpublic-key-here..." ]; | |||
hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ]; | |||
}; | |||
postCommands = '' | |||
# Automatically ask for the password on SSH login | |||
echo 'cryptsetup-askpass || echo "Unlock was successful; exiting SSH session" && exit 1'</nowiki> >> <nowiki>/root/.profile | |||
''; | |||
}; | |||
}; | |||
</nowiki>}} | |||
Adapt following parts according to your setup | |||
* '''authorizedKeys''': Add the SSH public keys for the users which should be able to authenticate to the SSH daemon to the <code>authorizedKeys</code> option. | |||
* '''availableKernelModules''': Most likely your network card is not working without its kernel module being part of the initrd, so you have to find out which module is used for your network. Use <code>lspci -v | grep -iA8 'network\|ethernet'</code> for that. | |||
* '''kernelParams''': Instead of using DHCP you could also configure a static IP, for example with kernel parameter <code>boot.kernelParams = [ "ip=10.25.0.2::10.25.0.1:255.255.255.0:myhost::none" ];</code>, where <code>10.25.0.2</code> is the client IP, <code>10.25.0.1</code> is the gateway IP. See [https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt the kernel documentation] for more information on the <code>ip=</code> parameter. When using DHCP, make sure your computer is always attached to the network and is able to get an IP adress, or the boot process will hang. | |||
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. | |||
== Usage == | |||
After reboot, connect to the initrd SSH daemon using | |||
< | <syntaxhighlight lang="bash"> | ||
# ssh root@10.25.0.2 | |||
</syntaxhighlight> | |||
Where <code>10.25.0.2</code> is the IP which is acquired via DHCP or configured via the kernel parameter. | |||
== Tips and tricks == | |||
=== | === Bcachefs unlocking === | ||
Unlocking encrypted Bcachefs root filesystems is [https://github.com/NixOS/nixpkgs/issues/291529 not yet supported]. As a workaround, following script, in combination with the setup above, can be used as SSH shell, to unlock the disk <code>/dev/vda2</code>. | |||
< | {{file|/etc/nixos/configuration.nix|nix|<nowiki> | ||
boot.initrd. | boot.initrd.systemd = let | ||
askPass = pkgs.writeShellScriptBin "bcachefs-askpass" '' | |||
keyctl link @u @s | |||
mkdir /sysroot | |||
until bcachefs mount /dev/vda2 /sysroot | |||
do | |||
sleep 1 | |||
done | |||
''; | |||
in { | |||
enable = true; | enable = true; | ||
initrdBin = with pkgs; [ keyutils ]; | |||
storePaths = ["${askPass}/bin/bcachefs-askpass"]; | |||
users.root.shell = "${askPass}/bin/bcachefs-askpass"; | |||
}; | }; | ||
</ | </nowiki>}} | ||
Using systemd in initrd automatically continues the boot process after the target <code>/sysroot</code> is mounted. | |||
=== Wireguard in initrd === | |||
Considering you've already enabled the ssh daemon, configured networking (for example with DHCP or static IP) and configured an unlocking command, following additional snippet will enable [[WireGuard]] connectivity to a remote peer while in initrd.<syntaxhighlight lang="nix"> | |||
boot.initrd.availableKernelModules = [ "r8169" "wireguard" ]; | |||
boot.initrd.systemd = { | |||
enable = true; | |||
network = { | |||
netdevs."30-wg-initrd" = { | |||
netdevConfig = { | |||
Kind = "wireguard"; | |||
Name = "wg-initrd"; | |||
}; | |||
wireguardConfig = { PrivateKeyFile = "/etc/secrets/30-wg-initrd.key"; }; | |||
wireguardPeers = [{ | |||
AllowedIPs = [ "10.250.0.1/32" ]; | |||
PublicKey = "wUE//Lwi8DZVIvAjIAtMoy+ku+hJ0w28H7ofySwAJRk="; | |||
Endpoint = "198.51.100.1:51821"; | |||
PersistentKeepalive = 25; | |||
}]; | |||
}; | |||
networks."30-wg-initrd" = { | |||
name = "wg-initrd"; | |||
addresses = [{ Address = "10.250.0.2/24"; }]; | |||
}; | |||
}; | |||
}; | |||
boot.initrd.secrets."/etc/secrets/30-wg-initrd.key" = "/etc/wireguard/private-key"; | |||
</syntaxhighlight>First generate a private und public key pair as mentioned in the WireGuard article. Reference the private key in <code>boot.initrd.secrets</code>, in this exmaple <code>/etc/wireguard/private-key</code>. Put the <code>PublicKey</code> of the remote peer into the <code>wireguardPeers</code> array. | |||
Configure the IP addresses used by your initrd peer (<code>10.250.0.2</code>) and the remote peer (<code>10.250.0.1</code>). Also specify the IP and port of the remote peer in <code>Endpoint</code>, in our example <code>198.51.100.1:51821</code>. The remote peer also needs to know address configuration and the public key of the initrd peer. | |||
Last but not least add the <code>wireguard</code> kernel module to <code>boot.initrd.availableKernelModules</code> beside the module required by your network device. | |||
=== Tor in initrd === | |||
= | An example with an ssh server listening at a tor hidden service address can be found at [https://cgit.euer.krebsco.de/stockholm/tree/krebs/2configs/tor/initrd.nix?id=9919cb25912dfcc50881239f95494dd2f8e7b858 krebs/2configs/tor/initrd.nix in stockholm] | ||
=== Prepare the Onion ID === | ==== Prepare the Onion ID ==== | ||
You need 3 files to create an onion id (a.k.a. tor hidden service). | You need 3 files to create an onion id (a.k.a. tor hidden service). | ||
Line 49: | Line 123: | ||
* <code>hs_ed25519_secret_key</code> | * <code>hs_ed25519_secret_key</code> | ||
To create | To create these files, you have to run tor once, with a dummy configuration. | ||
<pre>DataDirectory /tmp/my-dummy.tor/ | <pre>DataDirectory /tmp/my-dummy.tor/ | ||
Line 70: | Line 144: | ||
Hit <code>Ctrl-C</code> and the files you need, should be in <code>/home/tony/tor/onion</code>. | Hit <code>Ctrl-C</code> and the files you need, should be in <code>/home/tony/tor/onion</code>. | ||
=== Setup Tor === | ==== Setup Tor ==== | ||
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. | ||
< | <syntaxhighlight lang="nix"># copy your onion folder | ||
boot.initrd.secrets = { | boot.initrd.secrets = { | ||
"/etc/tor/onion/bootup"; = /home/tony/tor/onion; # maybe find a better spot to store this. | |||
}; | }; | ||
Line 86: | Line 160: | ||
# start tor during boot process | # start tor during boot process | ||
boot.initrd.network.postCommands = let | boot.initrd.network.postCommands = let | ||
torRc = (pkgs.writeText | torRc = (pkgs.writeText "tor.rc" '' | ||
DataDirectory /etc/tor | DataDirectory /etc/tor | ||
SOCKSPort 127.0.0.1:9050 IsolateDestAddr | SOCKSPort 127.0.0.1:9050 IsolateDestAddr | ||
Line 94: | Line 168: | ||
''); | ''); | ||
in '' | in '' | ||
echo | echo "tor: preparing onion folder" | ||
# have to do this otherwise tor does not want to start | # have to do this otherwise tor does not want to start | ||
chmod -R 700 /etc/tor | chmod -R 700 /etc/tor | ||
echo | echo "make sure localhost is up" | ||
ip a a 127.0.0.1/8 dev lo | ip a a 127.0.0.1/8 dev lo | ||
ip link set lo up | ip link set lo up | ||
echo | echo "tor: starting tor" | ||
tor -f ${torRc} --verify-config | tor -f ${torRc} --verify-config | ||
tor -f ${torRc} & | tor -f ${torRc} & | ||
'';</ | '';</syntaxhighlight> | ||
That was it. Tor should be running during your boot process. | That was it. Tor should be running during your boot process. | ||
=== Setup haveged === | ==== Setup haveged ==== | ||
If your system doesn't gather enough entropy the startup time of tor is rather long (2:42 vs 0:06 on a RPi 4b). Counter it by starting <code>haveged</code>. | If your system doesn't gather enough entropy the startup time of tor is rather long (2:42 vs 0:06 on a RPi 4b). Counter it by starting <code>haveged</code>. | ||
Line 117: | Line 191: | ||
</pre> | </pre> | ||
Then use this snippet before <code>echo | Then use this snippet before <code>echo "tor: starting tor"</code> in your <code>boot.initrd.network.postCommands</code>. | ||
<pre> | <pre> | ||
echo "haveged: starting haveged" | echo "haveged: starting haveged" | ||
Line 123: | Line 197: | ||
</pre> | </pre> | ||
=== Setup ntpdate === | ==== Setup ntpdate ==== | ||
If your system doesn't utilize a RTC you've to ensure time is correctly set before startup of tor. | If your system doesn't utilize a RTC you've to ensure time is correctly set before startup of tor. | ||
Line 132: | Line 206: | ||
</pre> | </pre> | ||
Then use this snippet before <code>echo | Then use this snippet before <code>echo "tor: starting tor"</code> in your <code>boot.initrd.network.postCommands</code>. | ||
<pre> | <pre> | ||
echo "ntp: starting ntpdate" | echo "ntp: starting ntpdate" | ||
Line 140: | Line 214: | ||
</pre> | </pre> | ||
== | ==== Usage ==== | ||
When your computer boots, and asks for the LUKS password. Now you can unlock your encrypted Hard drive using: | When your computer boots, and asks for the LUKS password. Now you can unlock your encrypted Hard drive using: | ||
<pre>torify ssh root@ | <pre>torify ssh root@<onion.id>.onion -p 22 'my-secret-password'</pre> | ||
=== Enable Wifi in initrd === | |||
Following example configuration by [https://discourse.nixos.org/t/wireless-connection-within-initrd/38317/13 @loutr] enables wifi connections inside initrd. Replace interface name <code>wlp0s20f0u4</code> with the name of your wifi adapter. Depending on your wifi device, you might need to add different kernel modules.<syntaxhighlight lang="nix"> | |||
boot.initrd = { | |||
# crypto coprocessor and wifi modules | |||
availableKernelModules = [ "ccm" "ctr" "iwlmvm" "iwlwifi" ]; | |||
systemd = { | |||
enable = true; | |||
packages = [ pkgs.wpa_supplicant ]; | |||
initrdBin = [ pkgs.wpa_supplicant ]; | |||
targets.initrd.wants = [ "wpa_supplicant@wlp0s20f0u4.service" ]; | |||
# prevent WPA supplicant from requiring `sysinit.target`. | |||
services."wpa_supplicant@".unitConfig.DefaultDependencies = false; | |||
== | users.root.shell = "/bin/systemd-tty-ask-password-agent"; | ||
network = { | |||
enable = true; | |||
networks."10-wlan" = { | |||
matchConfig.Name = "wlp0s20f0u4"; | |||
networkConfig.DHCP = "yes"; | |||
}; | |||
ssh = { | |||
enable = true; | |||
port = 22; | |||
hostKeys = [ "/etc/ssh/ssh_host_ed25519_key" ]; | |||
authorizedKeys = default.user.openssh.authorizedKeys.keys; | |||
}; | |||
}; | |||
secrets."/etc/wpa_supplicant/wpa_supplicant-wlp0s20f0u4.conf" = /root/secrets/wpa_supplicant.conf; | |||
}; | |||
</syntaxhighlight>The file <code>wpa_supplicat-wlp0s20f0u4.conf</code> is the wireless profile used by [[wpa_supplicant]] which will get copied into the initramfs. | |||
[[Category:Server]] | |||
[[Category:Cookbook]] |
Latest revision as of 10:58, 18 December 2024
If you want to unlock your computer remotely via SSH or even through Tor, and you are facing the problem, that you can’t reach your computer before your computer is unlocked. Tor will help you to reach your computer, even during the boot process.
Setup
Generate host key for the SSH daemon which will run in initrd during boot
# mkdir -p /etc/secrets/initrd
# ssh-keygen -t ed25519 -N "" -f /etc/secrets/initrd/ssh_host_ed25519_key
Enable SSH daemon in initrd
/etc/nixos/configuration.nix
boot.initrd = {
availableKernelModules = [ "r8169" ];
network = {
enable = true;
udhcpc.enable = true;
flushBeforeStage2 = true;
ssh = {
enable = true;
port = 22;
authorizedKeys = [ "ssh-rsa AAAAyourpublic-key-here..." ];
hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ];
};
postCommands = ''
# Automatically ask for the password on SSH login
echo 'cryptsetup-askpass || echo "Unlock was successful; exiting SSH session" && exit 1' >> /root/.profile
'';
};
};
Adapt following parts according to your setup
- authorizedKeys: Add the SSH public keys for the users which should be able to authenticate to the SSH daemon to the
authorizedKeys
option. - availableKernelModules: Most likely your network card is not working without its kernel module being part of the initrd, so you have to find out which module is used for your network. Use
lspci -v | grep -iA8 'network\|ethernet'
for that. - kernelParams: Instead of using DHCP you could also configure a static IP, for example with kernel parameter
boot.kernelParams = [ "ip=10.25.0.2::10.25.0.1:255.255.255.0:myhost::none" ];
, where10.25.0.2
is the client IP,10.25.0.1
is the gateway IP. See the kernel documentation for more information on theip=
parameter. When using DHCP, make sure your computer is always attached to the network and is able to get an IP adress, or the boot process will hang.
The postCommands
option is necessary to get a password prompt instead of a shell.
If you omit it, you will get dropped into /bin/ash
, and you will have to manually run cryptsetup-askpass
to enter the password. Alternatively, the boot.initrd.systemd.users.root.shell
option can be set to /bin/conspy
for passwords which expect stdin. This binary included by default, and provided by busybox.
Usage
After reboot, connect to the initrd SSH daemon using
# ssh root@10.25.0.2
Where 10.25.0.2
is the IP which is acquired via DHCP or configured via the kernel parameter.
Tips and tricks
Bcachefs unlocking
Unlocking encrypted Bcachefs root filesystems is not yet supported. As a workaround, following script, in combination with the setup above, can be used as SSH shell, to unlock the disk /dev/vda2
.
/etc/nixos/configuration.nix
boot.initrd.systemd = let
askPass = pkgs.writeShellScriptBin "bcachefs-askpass" ''
keyctl link @u @s
mkdir /sysroot
until bcachefs mount /dev/vda2 /sysroot
do
sleep 1
done
'';
in {
enable = true;
initrdBin = with pkgs; [ keyutils ];
storePaths = ["${askPass}/bin/bcachefs-askpass"];
users.root.shell = "${askPass}/bin/bcachefs-askpass";
};
Using systemd in initrd automatically continues the boot process after the target /sysroot
is mounted.
Wireguard in initrd
Considering you've already enabled the ssh daemon, configured networking (for example with DHCP or static IP) and configured an unlocking command, following additional snippet will enable WireGuard connectivity to a remote peer while in initrd.
boot.initrd.availableKernelModules = [ "r8169" "wireguard" ];
boot.initrd.systemd = {
enable = true;
network = {
netdevs."30-wg-initrd" = {
netdevConfig = {
Kind = "wireguard";
Name = "wg-initrd";
};
wireguardConfig = { PrivateKeyFile = "/etc/secrets/30-wg-initrd.key"; };
wireguardPeers = [{
AllowedIPs = [ "10.250.0.1/32" ];
PublicKey = "wUE//Lwi8DZVIvAjIAtMoy+ku+hJ0w28H7ofySwAJRk=";
Endpoint = "198.51.100.1:51821";
PersistentKeepalive = 25;
}];
};
networks."30-wg-initrd" = {
name = "wg-initrd";
addresses = [{ Address = "10.250.0.2/24"; }];
};
};
};
boot.initrd.secrets."/etc/secrets/30-wg-initrd.key" = "/etc/wireguard/private-key";
First generate a private und public key pair as mentioned in the WireGuard article. Reference the private key in boot.initrd.secrets
, in this exmaple /etc/wireguard/private-key
. Put the PublicKey
of the remote peer into the wireguardPeers
array.
Configure the IP addresses used by your initrd peer (10.250.0.2
) and the remote peer (10.250.0.1
). Also specify the IP and port of the remote peer in Endpoint
, in our example 198.51.100.1:51821
. The remote peer also needs to know address configuration and the public key of the initrd peer.
Last but not least add the wireguard
kernel module to boot.initrd.availableKernelModules
beside the module required by your network device.
Tor in initrd
An example with an ssh server listening at a tor hidden service address can be found at krebs/2configs/tor/initrd.nix in stockholm
Prepare the Onion ID
You need 3 files to create an onion id (a.k.a. tor hidden service).
hostname
hs_ed25519_public_key
hs_ed25519_secret_key
To create these files, you have to run tor once, with a dummy configuration.
DataDirectory /tmp/my-dummy.tor/ SOCKSPort 127.0.0.1:10050 IsolateDestAddr SOCKSPort 127.0.0.1:10063 HiddenServiceDir /home/tony/tor/onion HiddenServicePort 1234 127.0.0.1:1234
Let’s asume you created this file in /home/tony/tor/tor.rc
.
Verify that everything is tor.rc
awesome, by running tor -f /home/tony/tor/tor.rc --verify-config
. If you don’t see any errors, just run tor -f /home/tony/tor/tor.rc
.
You will get some output like this.
May 21 18:38:39.000 [notice] Bootstrapped 80% (ap_conn): Connecting to a relay to build circuits May 21 18:38:39.000 [notice] Bootstrapped 85% (ap_conn_done): Connected to a relay to build circuits May 21 18:38:39.000 [notice] Bootstrapped 89% (ap_handshake): Finishing handshake with a relay to build circuits May 21 18:38:39.000 [notice] Bootstrapped 90% (ap_handshake_done): Handshake finished with a relay to build circuits May 21 18:38:39.000 [notice] Bootstrapped 95% (circuit_create): Establishing a Tor circuit May 21 18:38:40.000 [notice] Bootstrapped 100% (done): Done
Hit Ctrl-C
and the files you need, should be in /home/tony/tor/onion
.
Setup Tor
Now that you have your 3 files, you have to script a bit, but it’s not too complicated.
# copy your onion folder
boot.initrd.secrets = {
"/etc/tor/onion/bootup"; = /home/tony/tor/onion; # maybe find a better spot to store this.
};
# copy tor to you initrd
boot.initrd.extraUtilsCommands = ''
copy_bin_and_libs ${pkgs.tor}/bin/tor
'';
# start tor during boot process
boot.initrd.network.postCommands = let
torRc = (pkgs.writeText "tor.rc" ''
DataDirectory /etc/tor
SOCKSPort 127.0.0.1:9050 IsolateDestAddr
SOCKSPort 127.0.0.1:9063
HiddenServiceDir /etc/tor/onion/bootup
HiddenServicePort 22 127.0.0.1:22
'');
in ''
echo "tor: preparing onion folder"
# have to do this otherwise tor does not want to start
chmod -R 700 /etc/tor
echo "make sure localhost is up"
ip a a 127.0.0.1/8 dev lo
ip link set lo up
echo "tor: starting tor"
tor -f ${torRc} --verify-config
tor -f ${torRc} &
'';
That was it. Tor should be running during your boot process.
Setup haveged
If your system doesn't gather enough entropy the startup time of tor is rather long (2:42 vs 0:06 on a RPi 4b). Counter it by starting haveged
.
Append in your boot.initrd.extraUtilsCommands
.
copy_bin_and_libs ${pkgs.haveged}/bin/haveged
Then use this snippet before echo "tor: starting tor"
in your boot.initrd.network.postCommands
.
echo "haveged: starting haveged" haveged -F &
Setup ntpdate
If your system doesn't utilize a RTC you've to ensure time is correctly set before startup of tor.
Append in your boot.initrd.extraUtilsCommands
.
copy_bin_and_libs ${pkgs.ntp}/bin/ntpdate
Then use this snippet before echo "tor: starting tor"
in your boot.initrd.network.postCommands
.
echo "ntp: starting ntpdate" echo "ntp 123/tcp" >> /etc/services echo "ntp 123/udp" >> /etc/services ntpdate w.x.y.z # pick one IP from https://www.ntppool.org/
Usage
When your computer boots, and asks for the LUKS password. Now you can unlock your encrypted Hard drive using:
torify ssh root@<onion.id>.onion -p 22 'my-secret-password'
Enable Wifi in initrd
Following example configuration by @loutr enables wifi connections inside initrd. Replace interface name wlp0s20f0u4
with the name of your wifi adapter. Depending on your wifi device, you might need to add different kernel modules.
boot.initrd = {
# crypto coprocessor and wifi modules
availableKernelModules = [ "ccm" "ctr" "iwlmvm" "iwlwifi" ];
systemd = {
enable = true;
packages = [ pkgs.wpa_supplicant ];
initrdBin = [ pkgs.wpa_supplicant ];
targets.initrd.wants = [ "wpa_supplicant@wlp0s20f0u4.service" ];
# prevent WPA supplicant from requiring `sysinit.target`.
services."wpa_supplicant@".unitConfig.DefaultDependencies = false;
users.root.shell = "/bin/systemd-tty-ask-password-agent";
network = {
enable = true;
networks."10-wlan" = {
matchConfig.Name = "wlp0s20f0u4";
networkConfig.DHCP = "yes";
};
ssh = {
enable = true;
port = 22;
hostKeys = [ "/etc/ssh/ssh_host_ed25519_key" ];
authorizedKeys = default.user.openssh.authorizedKeys.keys;
};
};
secrets."/etc/wpa_supplicant/wpa_supplicant-wlp0s20f0u4.conf" = /root/secrets/wpa_supplicant.conf;
};
The file wpa_supplicat-wlp0s20f0u4.conf
is the wireless profile used by wpa_supplicant which will get copied into the initramfs.