diff options
Diffstat (limited to '')
| -rw-r--r-- | machines/nixos/x86_64-linux/bree.nix | 12 | ||||
| -rw-r--r-- | modules/default.nix | 1 | ||||
| -rw-r--r-- | modules/remote-unlock.nix | 111 | ||||
| -rw-r--r-- | profiles/remote-unlock.nix | 3 | ||||
| -rw-r--r-- | secrets/bree/disk-passphrase.age | 8 | ||||
| -rw-r--r-- | secrets/bree/disk-unlock-key.age | bin | 0 -> 721 bytes | |||
| -rw-r--r-- | secrets/secrets.nix | 10 |
7 files changed, 145 insertions, 0 deletions
diff --git a/machines/nixos/x86_64-linux/bree.nix b/machines/nixos/x86_64-linux/bree.nix index 2f564b5..f91bf4f 100644 --- a/machines/nixos/x86_64-linux/bree.nix +++ b/machines/nixos/x86_64-linux/bree.nix @@ -27,5 +27,17 @@ }; }; + age.secrets.disk-unlock-key.file = ../../../secrets/bree/disk-unlock-key.age; + age.secrets.disk-passphrase.file = ../../../secrets/bree/disk-passphrase.age; + + services.remoteDiskUnlock = { + enable = true; + hosts = [ + "192.168.1.114" + ]; + sshKeyPath = config.age.secrets.disk-unlock-key.path; + passphrasePath = config.age.secrets.disk-passphrase.path; + }; + system.stateVersion = "23.11"; # Did you read the comment? } diff --git a/modules/default.nix b/modules/default.nix index d6d7b65..f936646 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -3,6 +3,7 @@ imports = [ ./home-manager.nix ./host-config.nix + ./remote-unlock.nix ./ssh.nix ./user.nix ]; diff --git a/modules/remote-unlock.nix b/modules/remote-unlock.nix new file mode 100644 index 0000000..fea9345 --- /dev/null +++ b/modules/remote-unlock.nix @@ -0,0 +1,111 @@ +{ + 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; + }; + }; + }; +} diff --git a/profiles/remote-unlock.nix b/profiles/remote-unlock.nix index b0e3fe8..ea211ad 100644 --- a/profiles/remote-unlock.nix +++ b/profiles/remote-unlock.nix @@ -15,7 +15,10 @@ "/etc/initrd/ssh_host_ed25519_key" ]; authorizedKeys = [ + # my personal key "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINBkozy+X96u5ciX766bJ/AyQ3xm1tXZTIr5+4PVFZFi" + # key used to automatically unlock + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPr9Dv2MjZoRltmxi21PoS/42KnOhYxuq9r6ER62vjAx" ]; }; }; diff --git a/secrets/bree/disk-passphrase.age b/secrets/bree/disk-passphrase.age new file mode 100644 index 0000000..3811173 --- /dev/null +++ b/secrets/bree/disk-passphrase.age @@ -0,0 +1,8 @@ +age-encryption.org/v1 +-> ssh-ed25519 pFjJaA r/Q4nB/VcKaVXoJjDuIgnMVUr5K0rhrsVVq2lvQgQRQ +ZmwHs0sWxVKjS9njqPQR4rEV1aXxS80wWJQrAuf47vM +-> ssh-ed25519 OxmK1A /9e7fHg/Nh929cY7+0EagkxwME4jo0RxzBwdh8tuZnM +9UPI8Vnwebjick9WPlcT8lvNub687qchX4D4ntbanos +--- bwBCnL9gJhzuygCddmh0h0OXh/C6ysAgMfH9QBrQUMY + +I4ڍ:;X3T.n{A0^笆4F]P.uΕެ
\ No newline at end of file diff --git a/secrets/bree/disk-unlock-key.age b/secrets/bree/disk-unlock-key.age Binary files differnew file mode 100644 index 0000000..6d9a549 --- /dev/null +++ b/secrets/bree/disk-unlock-key.age diff --git a/secrets/secrets.nix b/secrets/secrets.nix index a8f01cf..155a88b 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -67,6 +67,16 @@ in hosts.bree ]; + "bree/disk-passphrase.age".publicKeys = [ + users.fcuny + hosts.bree + ]; + + "bree/disk-unlock-key.age".publicKeys = [ + users.fcuny + hosts.bree + ]; + "rivendell/wireguard.age".publicKeys = [ users.fcuny hosts.rivendell |
