diff --git a/home/modules/ssh/README.md b/home/modules/ssh/README.md new file mode 100644 index 00000000..97359179 --- /dev/null +++ b/home/modules/ssh/README.md @@ -0,0 +1,61 @@ +# SSH keys + +* primary keys (host-specific, non-resident) +* backup key (shared, resident) + +## generate keys + +### YubiKey 01 - `host_01` + +```sh +ssh-keygen -t ed25519-sk \ + -O verify-required \ + -f ~/.ssh/id_ed25519_sk \ + -C "h@host_01" +``` + +### YubiKey 01 — `host_02` + +```sh +ssh-keygen -t ed25519-sk \ + -O verify-required \ + -f ~/.ssh/id_ed25519_sk \ + -C "h@host_02" +``` + +### YubiKey 02 - `host_*` + +```sh +ssh-keygen -t ed25519-sk \ + -O resident \ + -O verify-required \ + -f ~/.ssh/id_ed25519_sk_bak \ + -C "backup" +``` + +## register keys + +when you the primary key (`id_ed25519_sk.pub`), make sure to also register the +backup key (`id_ed25519_sk_bak.pub`) if needed. + +## recovery scenarios + +| scenario | recovery | +|---|---| +| primary key file lost | generate new primary key on that device, re-register (use backup key) | +| primary YubiKey lost | generate new primary keys on all devices using new YubiKey (use backup key) | +| backup key file lost | regenerate from backup YubiKey resident key (use `ssh-keygen -K`) | +| backup YubiKey lost | generate resident backup key, distribute across hosts, re-register (use primary key) | + +## notes / to do + +TODO: automate distributing `id_ed25519_sk_bak`, `id_ed25519_sk_bak.pub` to all devices +TODO: declare setup scripts (use e.g. `$HOSTNAME`) +TODO: register backup key with hosts (add to authorized hosts for each host) +TODO: register backup key with services (e.g. Gitea) +TODO: make sure to fall back to backup key when host-specific primary key is not present +TODO: see if / how `-O application=ssh:` could be used + +## references + +* diff --git a/hosts/andromache/ssh_user.pub b/hosts/andromache/ssh_user.pub index dd45ff38..5b9baa35 100644 --- a/hosts/andromache/ssh_user.pub +++ b/hosts/andromache/ssh_user.pub @@ -1 +1 @@ -sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAILoWbtyewa2LUWpWCYK46OCWs+2E+BeIsk+0We20qsf9AAAABHNzaDo= h@andromache +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIIj+isTFg+W3v68HL28a9p9nX7ldAxu+ht9uUfKLvqcwAAAABHNzaDo= h@andromache diff --git a/modules/ssh/authorized-keys.nix b/modules/ssh/authorized-keys.nix index d30480e8..aea22fdd 100644 --- a/modules/ssh/authorized-keys.nix +++ b/modules/ssh/authorized-keys.nix @@ -17,13 +17,15 @@ in }; # auto generate authorized_keys from `authorizedHosts` - config.users.users.${username}.openssh.authorizedKeys.keys = lib.flatten ( - map ( - hostname: - let - keyFile = ../../hosts/${hostname}/ssh_user.pub; - in - lib.optionals (builtins.pathExists keyFile) (lib.splitString "\n" (builtins.readFile keyFile)) - ) ((builtins.filter (h: h != config.host.name) adminHosts) ++ config.ssh.authorizedHosts) - ); + config.users.users.${username}.openssh.authorizedKeys.keys = + lib.flatten ( + map ( + hostname: + let + keyFile = ../../hosts/${hostname}/ssh_user.pub; + in + lib.optionals (builtins.pathExists keyFile) (lib.splitString "\n" (builtins.readFile keyFile)) + ) ((builtins.filter (h: h != config.host.name) adminHosts) ++ config.ssh.authorizedHosts) + ) + ++ lib.splitString "\n" (builtins.readFile ./ssh_bak.pub); } diff --git a/modules/ssh/ssh_bak.pub b/modules/ssh/ssh_bak.pub new file mode 100644 index 00000000..c758a45e --- /dev/null +++ b/modules/ssh/ssh_bak.pub @@ -0,0 +1 @@ +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAINg9dwrE10NQ7WMhL/hFJHRNFlDedvloCm9E4XEfnpStAAAABHNzaDo= backup