{ lib, flakeTarget, ... }: let # Bakes this exact flake target's pre-generated SSH host key straight # into /etc/ssh/ -- mirrors lxc.nix's builtins.getEnv pattern (impure # and empty under normal `nix build`/`nix eval`, so this is a no-op # unless explicitly opted into with NIXOS_HOST_KEYS_DIR=... --impure). # # Unlike --pre-format-files (which places files on the QEMU builder VM's # rootfs, not the target disk), embedding via environment.etc here means # nixos-install's own activation step installs the key onto the target # disk. sshd-keygen then finds it already present and skips generation, # so the disk image boots with the clan-registered key and sops can # decrypt on first boot. # # Without this, nixos-install's sshd-keygen activation generates a fresh # key (unregistered in .sops.yaml), sops decryption fails permanently, # and password hashes are never applied -- confirmed live: passwords # stayed '!' even with mutableUsers = false because hashedPasswordFile # pointed to a path that sops never wrote. hostKeysDirStr = builtins.getEnv "NIXOS_HOST_KEYS_DIR"; hasHostKeysDir = hostKeysDirStr != "" && builtins.pathExists hostKeysDirStr; hostKeysDir = /. + hostKeysDirStr; privKeyFile = hostKeysDir + "/${flakeTarget}_ssh_host_ed25519_key"; pubKeyFile = hostKeysDir + "/${flakeTarget}_ssh_host_ed25519_key.pub"; hasKeyForThisTarget = hasHostKeysDir && builtins.pathExists privKeyFile && builtins.pathExists pubKeyFile; in { imports = [ ../hardware-configuration/vm/proxmox.nix ../boot/efi.nix ../disko/proxmox.nix ]; environment.etc = lib.mkIf hasKeyForThisTarget { "ssh/ssh_host_ed25519_key" = { source = privKeyFile; mode = "0600"; }; "ssh/ssh_host_ed25519_key.pub" = { source = pubKeyFile; mode = "0644"; }; }; # NixOS's etc activation removes any /etc file that was in the previous # generation's environment.etc but is absent from the current one. Since # the SSH key is only in environment.etc during the --impure build (when # NIXOS_HOST_KEYS_DIR is set), normal rebuilds would remove it as # "obsolete". These scripts mirror lxc.nix's approach: save the live key # before etc runs, restore it after. Without the explicit deps, the # topological sort places preserveSshHostKey after etc (confirmed live on # lxc-tor-relay: position 7 vs etc's position 5), so the key is gone # before it can be saved. system.activationScripts = { preserveSshHostKey = '' if [ -f /etc/ssh/ssh_host_ed25519_key ]; then cp /etc/ssh/ssh_host_ed25519_key /run/sshd-host-key-preserve.tmp cp /etc/ssh/ssh_host_ed25519_key.pub /run/sshd-host-key-preserve.pub.tmp fi ''; restoreSshHostKey = { deps = [ "etc" ]; text = '' if [ ! -f /etc/ssh/ssh_host_ed25519_key ] && [ -f /run/sshd-host-key-preserve.tmp ]; then install -m 0600 /run/sshd-host-key-preserve.tmp /etc/ssh/ssh_host_ed25519_key install -m 0644 /run/sshd-host-key-preserve.pub.tmp /etc/ssh/ssh_host_ed25519_key.pub fi rm -f /run/sshd-host-key-preserve.tmp /run/sshd-host-key-preserve.pub.tmp ''; }; etc = { deps = [ "preserveSshHostKey" ]; }; setupSecrets = { deps = [ "restoreSshHostKey" ]; }; }; }