Master SSH key management and key-based authentication. This comprehensive guide covers everything from generating secure SSH keys to advanced configuration, troubleshooting, and security best practices for production environments.
Why Use SSH Key Authentication?
SSH key authentication provides stronger security than password-based authentication and enables automated, passwordless access to remote systems.
- Enhanced Security: Cryptographically secure, resistant to brute-force attacks
- Passwordless Access: No need to type passwords for each connection
- Automation Friendly: Essential for scripts, CI/CD, and automated deployments
- Auditability: Each key can be traced to specific users or systems
- Revocability: Easy to revoke access by removing public keys
- Multi-factor Support: Can combine with passwords for 2FA
1. SSH Key Types & Algorithms
Key Algorithm Comparison
Ed25519RSA 4096ECDSA 521RSA 3072RSA 2048DSA 10242. Generating SSH Keys
Key File Structure
# Private Key File (~/.ssh/id_ed25519)
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACD7g6M9J4Hx8X7JjKkK7WQ3pLm1oYzR8tNvq2wK5jQ7AAAAqJ3e5XOd3uVz
...
-----END OPENSSH PRIVATE KEY-----
# Public Key File (~/.ssh/id_ed25519.pub)
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPuDoz0ngfHxfsmMqQrtZDekubWhjNHy02+rbArmNDs your_email@example.com
# Components of public key:
# 1. Key type: ssh-ed25519
# 2. Public key: AAAAC3NzaC1lZDI1NTE5AAAAIPuDoz0ngfHxfsmMqQ...
# 3. Comment: your_email@example.com
3. Managing SSH Keys on Servers
Copying Public Keys to Servers
authorized_keys File Format
# ~/.ssh/authorized_keys - Server-side public key storage
# Each line contains one public key with optional options
# Basic key entry
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPuDoz0ngfHxfsmMqQrtZDekubWhjNHy02+rbArmNDs alice@laptop
# Key with command restriction
command="/usr/bin/rbash",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding ssh-ed25519 AAA... deploy@ci
# Key with source IP restriction
from="192.168.1.0/24,10.0.0.0/8" ssh-rsa AAA... admin@workstation
# Key with expiration date
expiry-time="2025-12-31" ssh-ed25519 AAA... temp@contractor
# Key for specific command only
command="scp",restrict ssh-ed25519 AAA... backup@server
# Multiple restrictions combined
command="/usr/bin/rbash",from="10.0.0.5",no-port-forwarding,no-pty ssh-rsa AAA... restricted@host
# Environment variable setting
environment="PATH=/usr/bin" ssh-ed25519 AAA... script@automation
# Certificate authority signed key
cert-authority,principals="admin,root" ssh-rsa AAA... ca@organization
# Tunnel-only key
permitopen="localhost:8080",permitlisten="localhost:9090" ssh-ed25519 AAA... tunnel@proxy
Server-side Configuration
# Enable public key authentication
PubkeyAuthentication yes
# Location of authorized_keys files
AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
# Allow users to place keys in ~/.ssh/authorized_keys
StrictModes yes
# Disable password authentication (after key setup)
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no
# Allow agent forwarding (use with caution)
AllowAgentForwarding yes
# Key algorithms to accept (modern defaults)
PubkeyAcceptedAlgorithms +ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com
# Disable weak key types
HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-512
# Limit authentication attempts
MaxAuthTries 3
MaxSessions 10
# Disable empty passwords
PermitEmptyPasswords no
# Root login restrictions
PermitRootLogin prohibit-password # Root can login with key only
# Restrict users/groups
AllowUsers alice bob charlie
AllowGroups ssh-users
# Restrict key options
AuthorizedKeysCommand /usr/bin/sss_ssh_authorizedkeys
AuthorizedKeysCommandUser nobody
# ChrootDirectory for SFTP-only users
Match User sftpuser
ChrootDirectory /var/sftp
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
4. SSH Agent & Key Management
SSH Agent Usage
Persistent SSH Agent Configuration
# ~/.bashrc or ~/.zshrc - Persistent SSH agent setup
# SSH Agent configuration
SSH_ENV="$HOME/.ssh/agent-environment"
function start_agent {
echo "Initializing new SSH agent..."
/usr/bin/ssh-agent | sed 's/^echo/#echo/' > "${SSH_ENV}"
echo "succeeded"
chmod 600 "${SSH_ENV}"
. "${SSH_ENV}" > /dev/null
/usr/bin/ssh-add;
}
# Source SSH settings, if applicable
if [ -f "${SSH_ENV}" ]; then
. "${SSH_ENV}" > /dev/null
ps -ef | grep ${SSH_AGENT_PID} | grep ssh-agent$ > /dev/null || {
start_agent;
}
else
start_agent;
fi
# Automatically add keys on startup (optional)
keys_to_add=(
"$HOME/.ssh/id_ed25519"
"$HOME/.ssh/work_key"
"$HOME/.ssh/github"
)
for key in "${keys_to_add[@]}"; do
if [[ -f "$key" ]] && ! ssh-add -l | grep -q "$(ssh-keygen -lf "$key" | awk '{print $2}')"; then
echo "Adding SSH key: $(basename "$key")"
ssh-add "$key"
fi
done
# Git SSH configuration
export GIT_SSH_COMMAND="ssh -o IdentitiesOnly=yes -i ~/.ssh/github"
5. Advanced SSH Key Management
Key Rotation & Migration
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_new -C "$(whoami)@$(hostname)-$(date +%Y%m%d)"
ssh-copy-id -i ~/.ssh/id_ed25519_new.pub user@server
ssh -i ~/.ssh/id_ed25519_new user@server "echo 'New key works'"
# Get old key fingerprint
OLD_FINGERPRINT=$(ssh-keygen -lf ~/.ssh/id_ed25519 | awk '{print $2}')
# Remove from server
ssh user@server "sed -i '/$OLD_FINGERPRINT/d' ~/.ssh/authorized_keys"
mv ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.old
mv ~/.ssh/id_ed25519.pub ~/.ssh/id_ed25519.pub.old
mv ~/.ssh/id_ed25519_new ~/.ssh/id_ed25519
mv ~/.ssh/id_ed25519_new.pub ~/.ssh/id_ed25519.pub
Multiple SSH Keys & Config File
# ~/.ssh/config - SSH client configuration
# Global settings
Host *
AddKeysToAgent yes
UseKeychain yes # macOS keychain integration
IdentitiesOnly yes # Only use specified identities
ServerAliveInterval 60 # Keep connections alive
ServerAliveCountMax 3
TCPKeepAlive yes
Compression yes
LogLevel ERROR
# Personal GitHub
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/github
IdentitiesOnly yes
# Work GitHub
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/work_github
IdentitiesOnly yes
# Production server
Host production
HostName 203.0.113.10
User deploy
Port 2222
IdentityFile ~/.ssh/deploy_key
IdentitiesOnly yes
ForwardAgent no # Security: no agent forwarding to prod
ServerAliveInterval 30
ServerAliveCountMax 5
# Development server
Host dev
HostName 192.168.1.100
User developer
IdentityFile ~/.ssh/id_ed25519
ForwardAgent yes # Safe: agent forwarding in dev
LocalForward 8080 localhost:80 # Port forwarding
# Jump host/bastion configuration
Host internal-*
ProxyJump bastion
ForwardAgent no
Host bastion
HostName bastion.example.com
User admin
IdentityFile ~/.ssh/bastion_key
Host internal-web
HostName 10.0.1.10
User webadmin
Host internal-db
HostName 10.0.1.20
User dbadmin
# AWS EC2 instances
Host *.compute.amazonaws.com
User ec2-user
IdentityFile ~/.ssh/aws_key.pem
StrictHostKeyChecking accept-new # Auto-accept new host keys
# Legacy server with old algorithms
Host legacy
HostName legacy.example.com
User admin
IdentityFile ~/.ssh/legacy_rsa
HostKeyAlgorithms +ssh-rsa # Enable legacy algorithms
KexAlgorithms +diffie-hellman-group14-sha1
# SFTP-only server
Host sftp-server
HostName sftp.example.com
User upload
IdentityFile ~/.ssh/sftp_key
Subsystem sftp internal-sftp
Certificate-based Authentication
# Create Certificate Authority (CA)
ssh-keygen -t ed25519 -f ~/.ssh/ca_key -C "CA Key"
# Sign user key with CA
ssh-keygen -s ~/.ssh/ca_key -I "alice@company" -n alice,bob -V +52w ~/.ssh/id_ed25519.pub
# Result: ~/.ssh/id_ed25519-cert.pub
# -----BEGIN CERTIFICATE-----
# ...
# Server configuration (/etc/ssh/sshd_config)
TrustedUserCAKeys /etc/ssh/ca.pub
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
# Client configuration (~/.ssh/config)
Host *.example.com
CertificateFile ~/.ssh/id_ed25519-cert.pub
IdentityFile ~/.ssh/id_ed25519
6. Security Best Practices
1. Use strong passphrases: Always protect private keys with passphrases
2. Regular key rotation: Rotate keys every 6-12 months or after security incidents
3. Disable password authentication: Once keys are working, disable PasswordAuthentication
4. Use key restrictions: Limit keys with from=, command=, no-port-forwarding options
5. Secure private keys: chmod 600 for private keys, chmod 644 for public keys
6. Audit authorized_keys: Regularly review and remove unused keys
7. Use modern algorithms: Prefer Ed25519 over RSA, avoid DSA completely
8. Limit agent forwarding: Only enable when absolutely necessary
9. Monitor SSH access: Review /var/log/auth.log or /var/log/secure
10. Implement fail2ban: Protect against brute-force attacks
Security Hardening Script
#!/bin/bash
# ssh-security-hardening.sh - Harden SSH configuration
set -e
echo "=== SSH Security Hardening ==="
# Backup original config
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup.$(date +%Y%m%d)
# Apply security settings
cat >> /etc/ssh/sshd_config << 'EOF'
# Security hardening
Protocol 2
PermitRootLogin prohibit-password
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no
# Key exchange algorithms
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256
# Ciphers
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
# MACs
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
# Authentication limits
MaxAuthTries 3
MaxSessions 10
LoginGraceTime 60
ClientAliveInterval 300
ClientAliveCountMax 2
# Restrict users
AllowUsers alice bob charlie
# AllowGroups ssh-users
# Disable unused features
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
PermitTunnel no
PrintMotd no
TCPKeepAlive no
Compression no
# Chroot for SFTP
Subsystem sftp internal-sftp
EOF
# Set proper permissions
chmod 600 /etc/ssh/sshd_config
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_* 2>/dev/null || true
chmod 644 ~/.ssh/*.pub 2>/dev/null || true
# Restart SSH service
systemctl restart sshd
echo "SSH security hardening complete!"
echo "Test connection before closing current session."
7. Troubleshooting SSH Key Issues
ssh -v, ls -la ~/.sshssh-addecho $SSH_AUTH_SOCK, ssh-add -lssh -i key.pemssh -o IdentitiesOnly=yesssh -Q cipher, ssh -Q kextelnet host 22, nc -zv host 22ssh-keygen -R hostnamessh -o PreferredAuthentications=publickeyDebugging SSH Connections
Common Permission Issues
# Fix SSH directory permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_* # Private keys
chmod 644 ~/.ssh/*.pub # Public keys
chmod 644 ~/.ssh/known_hosts
chmod 600 ~/.ssh/config # If exists
# Fix home directory permissions (important!)
chmod 755 ~
chown -R $(whoami):$(whoami) ~/.ssh
# Check SELinux context (RHEL/CentOS)
ls -Z ~/.ssh
restorecon -Rv ~/.ssh
# Verify with ssh in debug mode
ssh -vvv user@localhost 2>&1 | grep -i "permission"
8. Automation & Scripting with SSH Keys
Automated Deployment Script
#!/bin/bash
# deploy.sh - Automated deployment using SSH keys
set -euo pipefail
# Configuration
DEPLOY_KEY="$HOME/.ssh/deploy_key"
DEPLOY_USER="deploy"
DEPLOY_HOST="production.example.com"
APP_DIR="/var/www/myapp"
BACKUP_DIR="/var/backups/myapp"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Logging function
log() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"
}
error() {
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] ERROR:${NC} $1" >&2
}
# Check if deploy key exists
if [[ ! -f "$DEPLOY_KEY" ]]; then
error "Deploy key not found: $DEPLOY_KEY"
exit 1
fi
# Set restrictive permissions on deploy key
chmod 600 "$DEPLOY_KEY"
# SSH command with deploy key
SSH_CMD="ssh -i '$DEPLOY_KEY' -o StrictHostKeyChecking=yes -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR $DEPLOY_USER@$DEPLOY_HOST"
# Function to run remote command
run_remote() {
log "Running: $1"
eval "$SSH_CMD '$1'"
local status=$?
if [[ $status -ne 0 ]]; then
error "Command failed with exit code: $status"
return $status
fi
}
# Main deployment process
log "Starting deployment to $DEPLOY_HOST"
# 1. Create backup
log "Creating backup..."
run_remote "tar -czf $BACKUP_DIR/backup-$(date +%Y%m%d-%H%M%S).tar.gz -C $APP_DIR ."
# 2. Pull latest code
log "Pulling latest code..."
run_remote "cd $APP_DIR && git pull origin main"
# 3. Install dependencies
log "Installing dependencies..."
run_remote "cd $APP_DIR && npm ci --only=production"
# 4. Run database migrations
log "Running database migrations..."
run_remote "cd $APP_DIR && npm run migrate"
# 5. Restart application
log "Restarting application..."
run_remote "sudo systemctl restart myapp"
# 6. Verify deployment
log "Verifying deployment..."
run_remote "curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/health" | grep -q "200" && \
log "Deployment successful!" || \
error "Health check failed"
log "Deployment completed successfully!"
Ansible SSH Key Configuration
# ansible/ssh-keys.yml - Ansible playbook for SSH key management
- name: Configure SSH keys on servers
hosts: all
become: yes
vars:
ssh_users:
- name: alice
keys:
- "ssh-ed25519 AAA... alice@laptop"
- "ssh-ed25519 AAA... alice@work"
- name: bob
keys:
- "ssh-ed25519 AAA... bob@desktop"
- name: deploy
keys:
- "ssh-ed25519 AAA... deploy@ci"
tasks:
- name: Ensure .ssh directory exists
file:
path: "/home/{{ item.name }}/.ssh"
state: directory
mode: '0700'
owner: "{{ item.name }}"
group: "{{ item.name }}"
loop: "{{ ssh_users }}"
when: item.name != 'root'
- name: Deploy authorized_keys
copy:
dest: "/home/{{ item.name }}/.ssh/authorized_keys"
content: "{{ item.keys | join('\n') }}"
mode: '0600'
owner: "{{ item.name }}"
group: "{{ item.name }}"
loop: "{{ ssh_users }}"
when: item.name != 'root'
- name: Configure SSH daemon
lineinfile:
path: /etc/ssh/sshd_config
regexp: "^{{ item.key }}"
line: "{{ item.key }} {{ item.value }}"
state: present
validate: '/usr/sbin/sshd -t -f %s'
loop:
- { key: 'PasswordAuthentication', value: 'no' }
- { key: 'PubkeyAuthentication', value: 'yes' }
- { key: 'PermitRootLogin', value: 'prohibit-password' }
- { key: 'MaxAuthTries', value: '3' }
notify: restart sshd
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
Master SSH Key Management
SSH key-based authentication is fundamental to secure system administration, automation, and modern infrastructure management. By mastering key generation, management, and security practices, you can create robust, scalable, and secure access controls for your systems.
Remember: Security is a process, not a product. Regularly audit your SSH keys, rotate them periodically, and follow the principle of least privilege. Use modern algorithms like Ed25519, implement proper key restrictions, and always protect private keys with strong passphrases.
Next Steps: Audit your current SSH keys with ssh-add -l and find ~/.ssh -name "*.pub" -exec ssh-keygen -lf {} \;. Rotate any weak or old keys. Implement a centralized SSH key management system for teams. Set up SSH certificates for large-scale environments. As you gain experience, you'll appreciate the power and flexibility that proper SSH key management provides.