Remote disk unlocking: Difference between revisions

34j (talk | contribs)
Setup: Support ZFS in postCommands
Arnecc (talk | contribs)
 
(12 intermediate revisions by 5 users not shown)
Line 3: Line 3:
== 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 27: Line 145:
     postCommands = ''
     postCommands = ''
       # unlock LUKS encrypted partitions
       # unlock LUKS encrypted partitions
       echo 'cryptsetup-askpass' >> /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)
       # unlock ZFS encrypted partitions (NOTE: boot.initrd.supportedFilesystems.zfs must be true for zfs, zpool to be available here)
       # zpool import -a;
       # zpool import -a;
       # echo 'zfs load-key -a' >> /root/.profile
       # echo 'zfs load-key -a'</nowiki> >> <nowiki>/root/.profile
       # exit SSH
       # exit SSH
       echo 'exit' >> /root/.profile
       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 49: 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 62: 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 119: 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 131: 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 138: 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