Archived
fix(proxmox): embed SSH host key via NIXOS_HOST_KEYS_DIR so sops can decrypt on first boot
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m30s
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m30s
--pre-format-files placed the key on the QEMU builder VM's rootfs, not the target disk. nixos-install chroots into the target and runs sshd-keygen, which found no key in the chroot and generated a fresh (unregistered) one. sops then could not decrypt on first boot because the key didn't match .sops.yaml, leaving both root and nixos with '!' in /etc/shadow even after mutableUsers = false was set (hashedPasswordFile pointed to paths sops never wrote). Fix modules/platforms/proxmox.nix to embed the clan SSH host key in environment.etc via NIXOS_HOST_KEYS_DIR at eval time -- the same pattern lxc.nix uses. nixos-install's own activation places the key on the target disk, sshd-keygen finds it already present and skips generation, and sops decrypts correctly on first boot. Includes the same preserveSshHostKey/restoreSshHostKey activation scripts as lxc.nix so subsequent nixos-rebuild switch calls (without NIXOS_HOST_KEYS_DIR) don't remove the key as "obsolete" from environment.etc. Update create-proxmox-resource.sh: switch VM builds from ./result-<target> --pre-format-files ... --build-memory 2048 to NIXOS_HOST_KEYS_DIR=$(pwd)/host-keys nix build --impure ... diskoImagesScript ./result-<target> --build-memory 2048 matching the LXC build path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011uRcikkTp3D5VbXj2DwNpQ
This commit is contained in:
@@ -1,9 +1,81 @@
|
||||
{ ... }:
|
||||
{ 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" ]; };
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user