SSH key rotation notesEdit
I store a detailed copy of these notes (with concrete paths and hostnames etc) on my personal laptop at ~/.ssh/NOTES.md
, but I’m including a somewhat more general version here.
Policy
- Use ED25519 keys (smaller than, faster than, and at least as secure as, RSA keys).
- Create one key per machine so that they can be independently revoked if compromised (or, for example, if I switch jobs and have to decommission a work machine).
- Use the same key for everything (ie. one key that can push to both GitHub and GitLab etc, rather than different keys for different target hosts).
- Rotate keys once per year, in January, not for security, but for practice (the rotation procedure is complicated enough that I want to prove that I can do it).
- Use filenames of the form
id_ed25519_$DATE{,.pub}
(eg. id_ed25519_20230128.pub
etc).
- Make symlinks at
~/.ssh/id_ed25519
pointing to the real keys so that ssh
can find/try it by default without needing to explicitly set up IdentityFile
in the config.
- Store backups in 1Password.
- Keep the private keys encrypted (duh).
Procedure
- On work laptop:
- Create new key etc.
ssh-add # Make sure old key is in agent; in case we need it to connect to Linux desktop.
cd ~/.ssh
ssh-keygen -t ed25519 -f id_ed25519_20230129
ln -sf id_ed25519_20230129 id_ed25519
ln -sf id_ed25519_20230129.pub id_ed25519.pub
rm id_ed25519_2022*
- Add key files to 1Password, tagged with "ssh", and noting the encryption passphrase.
- Mark old key files as archived in 1Password.
- On personal laptop:
- Create new key etc.
ssh-add # Make sure old key is in agent; will need it to connect to Linux desktop and push.
cd ~/.ssh
ssh-keygen -t ed25519 -f id_ed25519_20230129
ln -sf id_ed25519_20230129 id_ed25519
ln -sf id_ed25519_20230129.pub id_ed25519.pub
rm id_ed25519_2022*
- Add key files to 1Password, tagged with "ssh", and noting the encryption passphrase.
- Mark old key files as archived in 1Password.
- SSH to linux desktop and:
- Add new personal laptop and work laptop public keys to
~/.ssh/authorized_keys
.
- Remove old personal laptop and work laptop public keys from
~/.ssh/authorized_keys
.
- Create new key etc
ssh-add # Make sure old key is in agent; will need it to connect to Linux desktop.
cd ~/.ssh
ssh-keygen -t ed25519 -f id_ed25519_20230129
ln -sf id_ed25519_20230129 id_ed25519
ln -sf id_ed25519_20230129.pub id_ed25519.pub
rm id_ed25519_2022*
- Add key files to 1Password, tagged with "ssh", and noting the encryption passphrase.
- Mark old key files as archived in 1Password.
- Update the Ansible config that I use to manage my EC2 hosts.
- Copy public keys into
inventory.yml
under ssh_public_keys
.
- Deploy:
./run install --tags=authorized_keys
- Manually remove old keys from hosts (Ansible only adds keys, it doesn’t remove the old ones).
- Import new personal laptop key into AWS console.
- Go to AWS console → EC2 → Key pairs → Actions → Import key pair
- Note the key ID
- Paste the key ID into my
create-instance
script in my Ansible configs.
- Rotate my Git backups key.
- Add new public keys to, and remove old keys from:
- GitHub (https://github.com/settings/keys) — all "Authentication", not "Signing" keys.
- GitLab (https://gitlab.com/-/profile/keys): same keys as GitHub, again just for "Authentication", no expiry dates.
- BitBucket (https://bitbucket.org/account/settings/ssh-keys/): same keys as GitHub.
- Codeberg (https://codeberg.org/user/settings/keys): same keys as GitHub, although we don’t make use of the backup key here yet.
- Sourcehut (https://meta.sr.ht/keys): same keys as GitHub, although again we aren’t making use of backup key here yet.
id_ed25519_$DATE{,.pub}
(eg. id_ed25519_20230128.pub
etc).~/.ssh/id_ed25519
pointing to the real keys so that ssh
can find/try it by default without needing to explicitly set up IdentityFile
in the config.- On work laptop:
- Create new key etc.
ssh-add # Make sure old key is in agent; in case we need it to connect to Linux desktop. cd ~/.ssh ssh-keygen -t ed25519 -f id_ed25519_20230129 ln -sf id_ed25519_20230129 id_ed25519 ln -sf id_ed25519_20230129.pub id_ed25519.pub rm id_ed25519_2022*
- Add key files to 1Password, tagged with "ssh", and noting the encryption passphrase.
- Mark old key files as archived in 1Password.
- Create new key etc.
- On personal laptop:
- Create new key etc.
ssh-add # Make sure old key is in agent; will need it to connect to Linux desktop and push. cd ~/.ssh ssh-keygen -t ed25519 -f id_ed25519_20230129 ln -sf id_ed25519_20230129 id_ed25519 ln -sf id_ed25519_20230129.pub id_ed25519.pub rm id_ed25519_2022*
- Add key files to 1Password, tagged with "ssh", and noting the encryption passphrase.
- Mark old key files as archived in 1Password.
- SSH to linux desktop and:
- Add new personal laptop and work laptop public keys to
~/.ssh/authorized_keys
. - Remove old personal laptop and work laptop public keys from
~/.ssh/authorized_keys
. - Create new key etc
ssh-add # Make sure old key is in agent; will need it to connect to Linux desktop. cd ~/.ssh ssh-keygen -t ed25519 -f id_ed25519_20230129 ln -sf id_ed25519_20230129 id_ed25519 ln -sf id_ed25519_20230129.pub id_ed25519.pub rm id_ed25519_2022*
- Add key files to 1Password, tagged with "ssh", and noting the encryption passphrase.
- Mark old key files as archived in 1Password.
- Add new personal laptop and work laptop public keys to
- Update the Ansible config that I use to manage my EC2 hosts.
- Copy public keys into
inventory.yml
underssh_public_keys
. - Deploy:
./run install --tags=authorized_keys
- Manually remove old keys from hosts (Ansible only adds keys, it doesn’t remove the old ones).
- Copy public keys into
- Import new personal laptop key into AWS console.
- Go to AWS console → EC2 → Key pairs → Actions → Import key pair
- Note the key ID
- Paste the key ID into my
create-instance
script in my Ansible configs.
- Rotate my Git backups key.
- Create new key etc.
- Add new public keys to, and remove old keys from:
- GitHub (https://github.com/settings/keys) — all "Authentication", not "Signing" keys.
- GitLab (https://gitlab.com/-/profile/keys): same keys as GitHub, again just for "Authentication", no expiry dates.
- BitBucket (https://bitbucket.org/account/settings/ssh-keys/): same keys as GitHub.
- Codeberg (https://codeberg.org/user/settings/keys): same keys as GitHub, although we don’t make use of the backup key here yet.
- Sourcehut (https://meta.sr.ht/keys): same keys as GitHub, although again we aren’t making use of backup key here yet.