Remote disk unlocking: Difference between revisions

34j (talk | contribs)
Add more description about authorizedKeys and hostKeys
Arnecc (talk | contribs)
 
(14 intermediate revisions by 5 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.
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 which will run in initrd during boot (required)
=== 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 = ''
       # Automatically ask for the password on SSH login
       # unlock LUKS encrypted partitions
       echo 'cryptsetup-askpass || echo "Unlock was successful; exiting SSH session" && exit 1'</nowiki> >> <nowiki>/root/.profile
       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 ==


=== Bcachefs unlocking ===
=== 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.


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>.
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
   askPass = pkgs.writeShellScriptBin "bcachefs-askpass" ''
   unlockShell = pkgs.writeShellScriptBin "bcachefs-unlock-shell" ''
     keyctl link @u @s
     keyctl link @u @s 2>/dev/null || true
    mkdir /sysroot
     exec systemd-tty-ask-password-agent --watch
    until bcachefs mount /dev/vda2 /sysroot
     do
      sleep  1
    done
   '';
   '';
in {
in {
   enable = true;
   enable = true;
   initrdBin = with pkgs; [ keyutils ];
   initrdBin = with pkgs; [ keyutils ];
   storePaths = ["${askPass}/bin/bcachefs-askpass"];
   extraBin = {
   users.root.shell = "${askPass}/bin/bcachefs-askpass";
    "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>}}
Using systemd in initrd automatically continues the boot process after the target <code>/sysroot</code> is mounted.


=== Wireguard in initrd ===
=== Wireguard in initrd ===
Line 114: Line 239:


=== Tor in initrd ===
=== 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/makefu/stockholm/src/commit/9b1008814e981dc01afe9ee7446322ad512c1d72/krebs/2configs/tor/initrd.nix krebs/2configs/tor/initrd.nix in stockholm]


==== 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 snow -n 1 -d ."
  $ 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