{ config, lib, pkgs, ... }: with lib; let cfg = config.services.remoteDiskUnlock; unlockScript = pkgs.writeShellScript "remote-disk-unlock" '' #!/usr/bin/env bash set -euo pipefail SSH_KEY="$CREDENTIALS_DIRECTORY/ssh-key" PASSPHRASE_FILE="$CREDENTIALS_DIRECTORY/passphrase" for server in ${concatStringsSep " " cfg.hosts}; do echo "Probing host $server on port 22" if ${pkgs.netcat}/bin/nc -z -w 5 "$server" 22 2>/dev/null; then echo "Host $server is already unlocked, skipping" continue fi echo "No response on port 22, probing host $server on port 911" if ${pkgs.netcat}/bin/nc -z -w 5 "$server" 911 2>/dev/null; then echo "Host $server is waiting for unlock - unlocking" ${pkgs.openssh}/bin/ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null \ -i "$SSH_KEY" -p 911 "root@$server" < "$PASSPHRASE_FILE" || true else echo "Host $server is down, retry later" fi done ''; in { options.services.remoteDiskUnlock = { enable = mkEnableOption "remote disk unlock service"; hosts = mkOption { type = types.listOf types.str; default = [ ]; description = "List of hostnames/IPs to monitor and unlock"; example = [ "server1.local" "192.168.1.100" ]; }; sshKeyPath = mkOption { type = types.path; description = "Path to SSH private key"; example = "/run/agenix/disk-unlock-key"; }; passphrasePath = mkOption { type = types.path; description = "Path to disk passphrase file"; example = "/run/agenix/disk-passphrase"; }; interval = mkOption { type = types.str; default = "10min"; description = "How often to check hosts (systemd timer format)"; }; }; config = mkIf cfg.enable { systemd.services.remote-disk-unlock = { description = "Unlock remote encrypted disks"; serviceConfig = { Type = "oneshot"; ExecStart = "${unlockScript}"; DynamicUser = true; LoadCredential = [ "ssh-key:${cfg.sshKeyPath}" "passphrase:${cfg.passphrasePath}" ]; PrivateTmp = true; ProtectSystem = "strict"; ProtectHome = true; NoNewPrivileges = true; ProtectKernelTunables = true; ProtectKernelModules = true; ProtectControlGroups = true; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; RestrictNamespaces = true; LockPersonality = true; MemoryDenyWriteExecute = true; RestrictRealtime = true; RestrictSUIDSGID = true; }; }; systemd.timers.remote-disk-unlock = { description = "Check and unlock remote disks periodically"; wantedBy = [ "timers.target" ]; timerConfig = { OnBootSec = "1min"; OnUnitActiveSec = cfg.interval; Persistent = false; }; }; }; }