Esc
Type to search posts, tags, and more...
Skip to content

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

TaskCommand
Generate Ed25519 keyssh-keygen -t ed25519 -C "id"
Generate RSA key (legacy)ssh-keygen -t rsa -b 4096 -C "id"
Copy key to hostssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
Start agenteval "$(ssh-agent -s)"
Add key to agentssh-add ~/.ssh/id_ed25519
Add with timeoutssh-add -t 4h ~/.ssh/id_ed25519
List loaded keysssh-add -L
Remove all keysssh-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.

! Was this useful?