DEV Community

dpm_bush
dpm_bush

Posted on Originally published at sshflow.com

A Practical SSH Key Lifecycle for Developers and Small Teams

SSH key sprawl is rarely caused by generating too many keys. The real problem is losing track of who owns each key, which servers trust it, and how to remove access when it is no longer needed.

A useful management process makes every key identifiable, protects its private half, and gives you a dependable way to replace or revoke it. You can do that with standard OpenSSH tools long before you need a centralized management system.

Start with a map of access

An SSH key pair has two parts: the private key stays with the person or service that authenticates, while the public key is installed on accounts that should accept that identity. On a typical OpenSSH server, those authorized public keys are listed in ~/.ssh/authorized_keys.

Begin by listing your local SSH directory:

ls -la ~/.ssh
Enter fullscreen mode Exit fullscreen mode

Files such as id_ed25519 are normally private keys; the matching .pub file is the public key. Do not copy a private key to a server to enable login. The server needs the public key, not the secret credential.

A long public-key string is awkward to compare, so use its fingerprint as an inventory identifier:

ssh-keygen -lf ~/.ssh/id_ed25519.pub
Enter fullscreen mode Exit fullscreen mode

Record the fingerprint, owner, purpose, and the accounts or systems where it is authorized. On the server, inspect the relevant account's authorized_keys file:

cat ~/.ssh/authorized_keys
Enter fullscreen mode Exit fullscreen mode

Before removing an unfamiliar entry, find out whether it belongs to a deployment job, backup process, or another administrator. The OpenSSH authorized_keys file is where ordinary per-user key authorization is commonly managed.

Keep identities separate and recognizable

Avoid sharing one human private key across a team. If several people use the same credential, logs cannot distinguish them, and offboarding one person may require replacing the key everywhere. Give each person a separate identity; use separate identities for automation as well.

Separate keys can also make sense across security boundaries, such as production and staging. Choose names that explain ownership or purpose rather than creating a trail of id_rsa_old_final files.

OpenSSH can select a key by host through ~/.ssh/config:

Host production
    HostName 203.0.113.10
    User deploy
    IdentityFile ~/.ssh/prod_ed25519
    IdentitiesOnly yes

Host staging
    HostName 198.51.100.20
    User deploy
    IdentityFile ~/.ssh/staging_ed25519
    IdentitiesOnly yes
Enter fullscreen mode Exit fullscreen mode

Now ssh production and ssh staging use the configured identities. IdentitiesOnly yes helps prevent the client from offering unrelated keys available through an agent. For more host-specific settings, see these SSH config examples.

Protect private keys where they live

On Unix-like systems, a private key is commonly restricted to its owner:

chmod 600 ~/.ssh/id_ed25519
chmod 700 ~/.ssh
Enter fullscreen mode Exit fullscreen mode

OpenSSH may reject a private key if its permissions are too broad. These restrictions also reduce the chance of another local user reading it.

For interactive use, a passphrase encrypts the private-key file at rest. You can load the key into an SSH agent so you do not need to enter the passphrase for every connection:

ssh-add ~/.ssh/id_ed25519
Enter fullscreen mode Exit fullscreen mode

A passphrase does not change the public key already installed on your servers. It protects the private key file if someone obtains a copy.

Deploy public keys, then verify access

For a regular Linux account, ssh-copy-id can install a public key:

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
Enter fullscreen mode Exit fullscreen mode

If you add a key manually, append the public key to the intended account's authorized_keys file. Keep the private key on its owner's machine.

After deployment, open a fresh connection and confirm that the new identity works before changing or removing existing access. This small check prevents a rotation from becoming an accidental lockout.

Rotate and revoke deliberately

Rotation replaces a key; revocation makes an old key stop granting access. A safe routine rotation looks like this:

  1. Generate a replacement key and keep the old one temporarily.
  2. Install the replacement public key on every intended account.
  3. Test a fresh connection using the new key.
  4. Remove the old public key from each authorization source.
  5. Confirm the old key no longer works and update your inventory.

If a private key may be compromised, do not wait for a convenient rotation window. Remove its authorization as quickly as practical, then check every place that trusted the same public key.

Ordinary OpenSSH key pairs do not have a built-in expiration date. A public key continues to grant access while it remains trusted by the server. OpenSSH certificates are different: they can have validity periods. For a straightforward small deployment, removing the obsolete public key from its authorization source is usually the simplest way to revoke access.

Know when manual tracking has stopped working

One person with a few servers can often manage keys with clear filenames, SSH config, and a documented inventory. A small team should make sure each person has an individual key and that onboarding and offboarding include server-side updates.

As the number of users, servers, or automation identities grows, manually editing each host becomes harder to audit. Automate public-key deployment and removal with your configuration or identity tooling, and keep a record of whether each change succeeded. A centralized system becomes useful when you can no longer reliably answer: who can access which systems, and how do we remove that access?

The goal is not to rotate keys on an arbitrary schedule. It is to know who owns them, where they are trusted, and how to revoke them when circumstances change. Start with that visibility; automate when the process is no longer dependable by hand.

I originally published a more detailed version of this guide on the SSHFlow blog.

I'm also building SSHFlow — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.

Top comments (0)