What Is SSH Key Authentication?
SSH (Secure Shell) keys use public-private key pairs instead of passwords for authentication. Using a bank vault analogy, a password is like a number combination (anyone can try), while an SSH key is like a physical key (only the holder can open it).
| Auth Method | Security Level | Convenience | Automation |
|---|---|---|---|
| Password | Low (guessable/stealable) | Enter every time | Difficult |
| SSH Key | High (requires private key) | Set up once | Easy |
| SSH Key + Passphrase | Very high | Managed via ssh-agent | Easy |
SSH Key Generation
# Generate Ed25519 key (currently recommended algorithm)
ssh-keygen -t ed25519 -C "your.email@example.com"
# Output:
# Generating public/private ed25519 key pair.
# Enter file in which to save the key (/home/user/.ssh/id_ed25519):
# Enter passphrase (empty for no passphrase): ← Setting a passphrase is recommended
# Enter same passphrase again:
# Your identification has been saved in /home/user/.ssh/id_ed25519
# Your public key has been saved in /home/user/.ssh/id_ed25519.pub
# Use RSA for legacy systems that don't support Ed25519
ssh-keygen -t rsa -b 4096 -C "your.email@example.com"
# Generate keys with specific names (for multi-account setups)
ssh-keygen -t ed25519 -C "work@company.com" -f ~/.ssh/id_ed25519_work
ssh-keygen -t ed25519 -C "personal@gmail.com" -f ~/.ssh/id_ed25519_personal
Checking Key Files
# Check generated key files
ls -la ~/.ssh/
# -rw------- id_ed25519 ← Private key (never share this!)
# -rw-r--r-- id_ed25519.pub ← Public key (register on servers)
# View public key contents (register this value on GitHub/servers)
cat ~/.ssh/id_ed25519.pub
# Output: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGx... your.email@example.com
# Check key fingerprint
ssh-keygen -l -f ~/.ssh/id_ed25519.pub
# Output: 256 SHA256:abc123def456... your.email@example.com (ED25519)
ssh-agent: Passphrase Management
When you set a passphrase, you’d have to enter it every time. With ssh-agent, you only need to enter it once per session.
# Start ssh-agent
eval "$(ssh-agent -s)"
# Output: Agent pid 12345
# Add key to agent (enter passphrase once)
ssh-add ~/.ssh/id_ed25519
# Output: Identity added: /home/user/.ssh/id_ed25519 (your.email@example.com)
# List registered keys
ssh-add -l
# Output: 256 SHA256:abc123... your.email@example.com (ED25519)
# On macOS, you can save the passphrase to keychain
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
# Remove all keys
ssh-add -D
Auto-Start Configuration (bashrc/zshrc)
# Add to ~/.bashrc or ~/.zshrc
# Start ssh-agent if not already running
if [ -z "$SSH_AUTH_SOCK" ]; then
eval "$(ssh-agent -s)" > /dev/null 2>&1
ssh-add ~/.ssh/id_ed25519 2>/dev/null
fi
Registering SSH Keys on GitHub
# 1. Copy public key
# Linux
cat ~/.ssh/id_ed25519.pub | xclip -selection clipboard
# macOS
pbcopy < ~/.ssh/id_ed25519.pub
# Windows (Git Bash)
cat ~/.ssh/id_ed25519.pub | clip
# 2. Register on GitHub
# GitHub → Settings → SSH and GPG keys → New SSH key
# Title: an identifiable name like "My Laptop"
# Key: paste the copied public key
# 3. Test connection
ssh -T git@github.com
# Output: Hi username! You've been authenticated, but GitHub does not provide shell access.
# 4. Change existing HTTPS repository to SSH
git remote set-url origin git@github.com:username/repo.git
git remote -v
# Output: origin git@github.com:username/repo.git (fetch)
# origin git@github.com:username/repo.git (push)
SSH Config File: Managing Connection Settings
The ~/.ssh/config file lets you manage per-host settings, replacing long commands with short aliases.
# ~/.ssh/config
# Default settings (applied to all hosts)
Host *
AddKeysToAgent yes # Automatically add keys to agent
IdentitiesOnly yes # Only try specified keys
ServerAliveInterval 60 # Send keepalive every 60 seconds (prevent disconnection)
ServerAliveCountMax 3 # Disconnect after 3 failed keepalives
# GitHub personal account
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_personal
# GitHub work account (using alias)
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_work
# GitLab
Host gitlab.com
HostName gitlab.com
User git
IdentityFile ~/.ssh/id_ed25519
PreferredAuthentications publickey
# Dev server (quick access via alias)
Host dev-server
HostName 192.168.1.100
User deploy
Port 2222 # Custom port instead of default 22
IdentityFile ~/.ssh/id_ed25519_server
# Production server (via jump host)
Host prod-server
HostName 10.0.0.50
User deploy
ProxyJump bastion-server # Connect through bastion
Host bastion-server
HostName bastion.example.com
User admin
IdentityFile ~/.ssh/id_ed25519_bastion
# Usage examples after applying config
# Quick access via alias (instead of: ssh -i ~/.ssh/id_ed25519_server -p 2222 deploy@192.168.1.100)
ssh dev-server
# Production server access (automatic bastion routing)
ssh prod-server
# SCP file transfer also uses aliases
scp ./deploy.tar.gz dev-server:/tmp/
# Port forwarding (local 3000 → server 3000)
ssh -L 3000:localhost:3000 dev-server
GitHub/GitLab Multi-Account Setup
For when you need to use personal and work GitHub accounts simultaneously.
# 1. Generate keys per account
ssh-keygen -t ed25519 -C "personal@gmail.com" -f ~/.ssh/id_ed25519_personal
ssh-keygen -t ed25519 -C "work@company.com" -f ~/.ssh/id_ed25519_work
# 2. Register each public key on the corresponding GitHub account
# personal public key → personal GitHub
# work public key → work GitHub
# 3. Configure ~/.ssh/config (see example above)
# Host github.com → personal key
# Host github-work → work key
# 4. Per-repository Git configuration
# Personal repository
cd ~/personal/my-project
git remote set-url origin git@github.com:personal-user/my-project.git
git config user.name "Personal Name"
git config user.email "personal@gmail.com"
# Work repository (using alias!)
cd ~/work/company-project
git remote set-url origin git@github-work:company-org/company-project.git
# ↑ Using github-work alias (not github.com)
git config user.name "Work Name"
git config user.email "work@company.com"
# 5. Test
ssh -T git@github.com # Verify personal account
ssh -T git@github-work # Verify work account
Automate with gitconfig Conditional Includes
# ~/.gitconfig (global settings)
[user]
name = Personal Name
email = personal@gmail.com
# Automatically apply work settings in the work directory
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-work
# ~/.gitconfig-work (work-specific settings)
[user]
name = Work Name
email = work@company.com
Security Recommendations
| Item | Recommended Setting | Risk |
|---|---|---|
| Algorithm | Ed25519 | RSA under 2048 bits is vulnerable |
| Passphrase | Required | Without it, a stolen key file is immediately usable |
| Private key permissions | chmod 600 (owner-only read) | SSH refuses keys readable by others |
| Config permissions | chmod 644 | Overly open permissions are a security risk |
| Key separation | Separate keys per purpose/server | If one key is compromised, everything is exposed |
| Key rotation | Recommended yearly | Old keys carry risk of undetected compromise |
# Set file permissions correctly
chmod 700 ~/.ssh # .ssh directory
chmod 600 ~/.ssh/id_ed25519 # Private key (owner read/write only)
chmod 644 ~/.ssh/id_ed25519.pub # Public key (read allowed)
chmod 644 ~/.ssh/config # Config file
chmod 644 ~/.ssh/known_hosts # known_hosts
Practical Tips
- Use Ed25519 as the default: It provides stronger security with shorter keys than RSA, and better performance too.
- Always set a passphrase: Even if your laptop is lost or key files are leaked, a passphrase prevents immediate exploitation.
- Separate keys by purpose: Create separate keys for GitHub, server access, and work/personal use. If one is exposed, the damage is limited.
- Make full use of
~/.ssh/config: Replacing complex ssh commands with short aliases dramatically improves productivity. - Manage
known_hosts: Always verify the fingerprint of a server you’re connecting to for the first time. When a server changes, remove the old entry withssh-keygen -R hostname. - Use Deploy Keys for GitHub: In CI/CD, use Deploy Keys that access only specific repositories instead of full account keys to reduce the security scope.