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_20240103
ln -sf id_ed25519_20240103 id_ed25519
ln -sf id_ed25519_20240103.pub id_ed25519.pub
rm id_ed25519_2023*
- Add key files to 1Password, tagged with "ssh", and noting the encryption passphrase[1].
- 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; note that the work key should be authorized for SSO with the GitHub organization.
- 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.
To make this easy, it’s better to just duplicate the existing item and then edit it (you can replace the associated key file); that way, you don’t have to set up the tags again, or worry about adding the key to the wrong vault etc. ↩︎
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_20240103 ln -sf id_ed25519_20240103 id_ed25519 ln -sf id_ed25519_20240103.pub id_ed25519.pub rm id_ed25519_2023*
- Add key files to 1Password, tagged with "ssh", and noting the encryption passphrase[1].
- 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; note that the work key should be authorized for SSO with the GitHub organization.
- 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.
To make this easy, it’s better to just duplicate the existing item and then edit it (you can replace the associated key file); that way, you don’t have to set up the tags again, or worry about adding the key to the wrong vault etc. ↩︎