Efficient SSH Key Management with Bash
A practical walkthrough of SSH key generation, distribution, agent management, and config file patterns — updated for modern best practices with Ed25519.
Contents
Why Key-Based SSH Still Matters
Password authentication over SSH is a liability. Brute-force attacks, credential reuse, keylogger exposure — the list goes on. Key-based authentication eliminates all of it. You generate a keypair, distribute the public half, and the private key never leaves your machine.
This post covers the full workflow: generating keys, distributing them, managing the SSH agent, and setting up config files that make connecting to dozens of hosts painless.
Generate an Ed25519 Key
Ed25519 is the recommended algorithm. It produces shorter keys than RSA, offers better performance, and has a simpler implementation with fewer parameters to get wrong.
ssh-keygen -t ed25519 -C "you@example.com"
You will be prompted for a filename (default ~/.ssh/id_ed25519) and a passphrase. Always set a passphrase — the SSH agent will handle the inconvenience for you.
If you need to connect to legacy systems that do not support Ed25519, fall back to RSA with a 4096-bit key:
ssh-keygen -t rsa -b 4096 -C "you@example.com"
What Gets Created
~/.ssh/id_ed25519 # Private key — never share this
~/.ssh/id_ed25519.pub # Public key — goes on remote hosts
Distribute the Public Key
ssh-copy-id copies your public key into the remote host’s ~/.ssh/authorized_keys:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
It handles creating the .ssh directory and setting correct permissions on the remote side. If ssh-copy-id is not available (some minimal images strip it), you can do it manually:
cat ~/.ssh/id_ed25519.pub | ssh user@host "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
After this, password-free login works:
ssh user@host
Use the SSH Agent
Typing your passphrase every time defeats the purpose. The SSH agent holds decrypted keys in memory for the duration of your session.
Start the agent and add your key:
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
You enter the passphrase once. Subsequent SSH connections use the cached key.
macOS Keychain Integration
On macOS, you can store the passphrase in the system keychain so it persists across reboots:
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
Then add this to your ~/.ssh/config to load it automatically:
Host *
AddKeysToAgent yes
UseKeychain yes
Agent Lifetime
By default, keys stay loaded until the agent process ends. To limit exposure, set a timeout:
ssh-add -t 4h ~/.ssh/id_ed25519
The key is removed from memory after four hours.
SSH Config File
The ~/.ssh/config file is the single biggest quality-of-life improvement for anyone who manages multiple hosts. Instead of remembering usernames, ports, and key paths, you define them once.
Basic Host Entry
Host prod-web
HostName 10.20.30.40
User deploy
Port 2222
IdentityFile ~/.ssh/id_ed25519
Now ssh prod-web connects with the right user, port, and key.
Wildcard Patterns
Group hosts by naming convention:
Host prod-*
User deploy
IdentityFile ~/.ssh/id_ed25519_prod
Host dev-*
User admin
IdentityFile ~/.ssh/id_ed25519_dev
Jump Hosts (ProxyJump)
Reaching hosts behind a bastion is a single directive:
Host bastion
HostName bastion.example.com
User ops
IdentityFile ~/.ssh/id_ed25519
Host internal-db
HostName 10.0.1.50
User dba
ProxyJump bastion
ssh internal-db automatically tunnels through the bastion. No manual port forwarding.
Multiplexing
Reuse a single TCP connection for multiple SSH sessions to the same host:
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
Create the sockets directory:
mkdir -p ~/.ssh/sockets
The first connection opens the socket. Subsequent connections to the same host are instant — no handshake, no re-authentication.
Lock Down Permissions
SSH refuses to use keys with overly permissive file modes. Set them explicitly:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/config
chmod 600 ~/.ssh/authorized_keys
If you see WARNING: UNPROTECTED PRIVATE KEY FILE!, permissions are wrong on the private key.
Quick Reference
| Task | Command |
|---|---|
| Generate Ed25519 key | ssh-keygen -t ed25519 -C "id" |
| Generate RSA key (legacy) | ssh-keygen -t rsa -b 4096 -C "id" |
| Copy key to host | ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host |
| Start agent | eval "$(ssh-agent -s)" |
| Add key to agent | ssh-add ~/.ssh/id_ed25519 |
| Add with timeout | ssh-add -t 4h ~/.ssh/id_ed25519 |
| List loaded keys | ssh-add -L |
| Remove all keys | ssh-add -D |
That covers the fundamentals. A well-structured ~/.ssh/config, Ed25519 keys, and the SSH agent will handle the vast majority of day-to-day SSH workflows without friction.