# SSH key best practices

LLMS index: [llms.txt](/en/llms.txt)

---

SSH keys are the de facto standard for authenticating to infrastructure, but poor management turns every deployment into a potential vulnerability. This note collects proven practices: from key generation to revocation and rotation without service downtime.

## Introduction

SSH keys function as long‑lived credentials, and their lifecycle directly impacts supply‑chain security. Unlike passwords, keys are often created once and forgotten, leading to an accumulation of “zombie” keys with privileges that exceed current needs. Proper generation, binding to an agent, and regular rotation minimize the attack surface and enable auditing of changes. The following sections describe concrete commands and configurations used in operations.

## Key Generation

Modern OpenSSH defaults recommend the Ed25519 algorithm because of its short key length (256 bits) and high resistance to attacks. RSA still appears in legacy systems but requires increasing the length to at least 4096 bits for acceptable security.

The primary generation command is:

```bash
ssh-keygen -t ed25519 -C "user@infrastructure" -f ~/.ssh/id_ed25519
```

Flag explanations:
- `-t` – cryptographic algorithm type (`ed25519`, `rsa`, `ecdsa`, `sk-ed25519@openssh.com` for Security Key).
- `-C` – comment, usually an email or service name; it is stored in the key but not used for authentication.
- `-f` – path to the key file; if the directory does not exist, it will be created with correct permissions.

For fully automated scenarios (CI/CD, scripts) you can pass an empty passphrase via `-N ""`, but understand the risk: a passwordless key can be used by any process running as the user. It is preferable to use `ssh-agent` with an unlock session or a key store (Keychain, `ssh-agent -s`).

**Table: Comparison of common key types**

| Algorithm | Key length | Primary use | Notes |
|-----------|------------|-------------|-------|
| Ed25519   | 256 bits   | Modern infrastructures, deployment scripts | Recommended standard starting with OpenSSH 6.8 |
| RSA       | 2048–4096 bits | Legacy systems, some vendor platforms | 4096 bits minimally acceptable; larger is overkill |
| ECDSA     | 256–521 bits | Specific requirements, older devices | 521‑bit (P‑521) most secure but less compatible |

When generating keys for different services, it is convenient to split them by files (e.g., `id_ed25519_work`, `id_ed25519_cloud`) to simplify revocation and rotation without affecting other contexts.

## Using ssh-agent

The SSH agent holds the decrypted key in memory of the current session, avoiding password entry on each connection. Starting the agent and adding a key:

```bash
# Start agent (output environment variables)
eval $(ssh-agent -s)

# Add key to agent
ssh-add ~/.ssh/id_ed25519
```

In newer OpenSSH versions (8.2+) the agent can be started in config mode:

```bash
ssh-agent -c
```

The `~/.ssh/config` file allows automatically loading keys for specific hosts:

```
Host *.github.com
    AddKeysToAgent yes
    UseKeychain yes
    IdentityFile ~/.ssh/id_ed25519_github

Host *.gitlab.com
    AddKeysToAgent yes
    IdentityFile ~/.ssh/id_ed25519_gitlab
```

Flags in `IdentityFile` and `AddKeysToAgent` guarantee that the key is loaded once on the first connection to a host and remains available for subsequent sessions within the agent’s lifetime. On macOS the flag `UseKeychain yes` integrates the key with Keychain, locking it when the screen is locked and unlocking when unlocked.

> [!TIP]
> Enable `IdentitiesOnly yes` in the config if the SSH client begins trying all keys during a connection attempt, which can cause delays or authentication errors on servers with a limited list of allowed keys.

## Revocation and Rotation

Keys should not live forever. Rotation policy depends on the infrastructure risk profile, but minimal hygiene includes regular audits and a revocation procedure when personnel change or responsibility shifts.

**Revocation procedure:**

1. Remove the public key from the remote server:
   ```bash
   ssh-keygen -f ~/.ssh/known_hosts -R "hostname_or_ip"
   ```
   or manually edit `~/.ssh/authorized_keys` deleting the line corresponding to the revoked key.

2. Locally delete the key and generate a new one:
   ```bash
   shred -u ~/.ssh/id_ed25519  # secure deletion (if util-linux installed)
   rm -rf ~/.ssh/id_ed25519*
   ssh-keygen -t ed25519 -C "user@infrastructure" -f ~/.ssh/id_ed25519
   ```

3. Distribute the new public key via IaC or a configuration manager (Ansible, Terraform) to avoid manual edits.

**Automated rotation:**

For large fleets you can use scripts that check the key creation date (comment `-C` or metadata inside the key) and automatically revoke keys older than N days. It is important to retain key history for at least 30–90 days before full deletion to avoid breaking services that have not yet updated their configuration.

> [!WARNING]
> If a key leak is detected (e.g., in public repositories or logs), act immediately: revoke the key on all hosts, generate a new one, and update the configuration. Do not wait for scheduled rotation—exposure of a key means full loss of control over the associated resources.
We need to produce the English version of the article, matching the structure and facts of the Russian draft. The Russian draft sections: Introduction, Генерация ключей (Key generation), Использование ssh-agent (Using ssh-agent), Отзыв и ротация (Revocation and rotation). We need to produce English article, same structure, same commands, tables, admonitions. No front matter, no code fences around whole article. Just markdown body.

We need to translate the Russian content into English, preserving the same details, commands, tables, admonitions. Ensure we keep the same practical tone, short paragraphs, specific commands, tables, code fences with language spec. Use GitHub admonitions as in Russian: > [!TIP], > [!WARNING]. Also maybe > [!NOTE] if needed but Russian didn't have note. We'll keep tip and warning.

We need to produce
