SSH
Secure Shell — Complete Reference Guide
Everything you need to know about SSH: from first connection to advanced tunnelling, key management, OpenSSH
server hardening, and automation. Suitable for developers, sysadmins, and DevOps engineers.
Topics Covered
Installation & first steps
Public / Private key cryptography
Key generation, types & best practices
SSH config file & aliases
SSH agent & agent forwarding
Port forwarding & tunnelling
SCP / SFTP file transfers
OpenSSH server (sshd) setup & hardening
Multiplexing & ProxyJump
Common options & flags reference
Troubleshooting & security checklist
1. What Is SSH?
SSH (Secure Shell) is a cryptographic network protocol for operating network services securely over an unsecured
network. It replaces older plaintext protocols such as Telnet, rlogin, and rsh, providing encrypted communication, strong
authentication, and data integrity.
1.1 How SSH Works
SSH uses a client-server model. The client initiates a TCP connection (default port 22) to the server. A handshake
negotiates a shared session key using algorithms such as Diffie-Hellman, then all traffic is encrypted with symmetric
encryption (e.g. AES-256). Authentication follows — either by password or, preferably, by public-key cryptography.
Layer Purpose Example Algorithms
Transport Encryption, integrity, compression AES-256-CTR, ChaCha20, SHA-256
Authentication Verify client identity RSA, Ed25519, ECDSA
Connection Multiplex channels (shell, tunnel…) TCP channels over one SSH session
1.2 SSH vs Other Protocols
Protocol Port Encrypted? Use
SSH 22 Yes Remote shell, tunnels, file transfer
Telnet 23 No Legacy remote shell (avoid)
FTP 21 No Legacy file transfer (avoid)
SFTP 22 Yes File transfer over SSH
RDP 3389 Yes Windows GUI remote desktop
2. Installing OpenSSH
OpenSSH is the most widely used SSH implementation. It ships with most Linux distributions and macOS. Windows
10/11 includes it as an optional feature.
2.1 Linux
# Debian / Ubuntu
sudo apt update && sudo apt install openssh-client openssh-server
# RHEL / CentOS / Fedora
sudo dnf install openssh-clients openssh-server
# Arch Linux
sudo pacman -S openssh
2.2 macOS
# OpenSSH client is pre-installed. To install server:
# Enable via System Settings > General > Sharing > Remote Login
# Or use Homebrew for a newer version:
brew install openssh
2.3 Windows
# PowerShell (run as Administrator):
Add-WindowsCapability -Online -Name [Link]~~~~[Link]
Add-WindowsCapability -Online -Name [Link]~~~~[Link]
# Start & enable the service:
Start-Service sshd
Set-Service -Name sshd -StartupType Automatic
2.4 Verify Installation
ssh -V
# Example output: OpenSSH_9.7p1, OpenSSL 3.3.0
■ Always keep OpenSSH updated. Security vulnerabilities are patched regularly.
3. Basic SSH Usage
3.1 Connect to a Remote Host
ssh username@hostname
ssh username@[Link]
ssh username@hostname -p 2222 # custom port
ssh -v username@hostname # verbose (debug)
ssh -vvv username@hostname # very verbose
3.2 Run a Remote Command Without Opening a Shell
ssh user@host 'uptime'
ssh user@host 'ls -la /var/log'
ssh user@host 'df -h && free -m'
3.3 Common ssh Flags
Flag Meaning
-p PORT Connect to non-standard port
-i FILE Use specific private key file
-l USER Specify login username
-v / -vvv Verbose / very verbose logging
-N No remote command (useful for tunnels)
-f Background after authentication
-T Disable pseudo-terminal allocation
-A Enable agent forwarding
-X Enable X11 forwarding (GUI apps)
-C Enable compression
-4 / -6 Force IPv4 / IPv6
-o OPT=V Pass a config option inline
-J host Jump / proxy through host
-L Local port forward
-R Remote port forward
-D Dynamic SOCKS proxy
4. Public-Key Cryptography Explained
SSH public-key authentication uses an asymmetric key pair: a private key (secret, stays on your machine) and a
public key (shared freely, placed on remote servers).
When you connect, the server issues a challenge encrypted with your public key. Only your private key can decrypt it,
proving your identity — your password never travels over the network.
Key Where? Secret? What it does
Private key ~/.ssh/id_ed25519 YES — never share Decrypts server challenges, signs data
Public key ~/.ssh/id_ed25519.pub No — share freely Added to server's authorized_keys
■ NEVER share your private key. If compromised, revoke it immediately on all servers.
5. Generating SSH Keys
5.1 Recommended Key Types (2024+)
Algorithm Command flag Key size / curve Recommendation
Ed25519 -t ed25519 256-bit (Curve25519) ★ Best choice — fast, small, modern
ECDSA -t ecdsa 256/384/521-bit Good — widely supported
RSA -t rsa -b 4096 required Legacy — use only if Ed25519 unsupported
DSA -t dsa 1024-bit only ✗ Deprecated — do not use
5.2 Generate a Key Pair
# Recommended — Ed25519
ssh-keygen -t ed25519 -C 'your@[Link]'
# RSA 4096 (legacy compat)
ssh-keygen -t rsa -b 4096 -C 'your@[Link]'
# With custom output file
ssh-keygen -t ed25519 -f ~/.ssh/myserver_key -C 'myserver'
You will be prompted for a passphrase — this encrypts your private key on disk. Strongly recommended; use ssh-agent
to avoid re-typing it every time.
5.3 Key Files Created
File Type Description
~/.ssh/id_ed25519 Private Keep secret. Permissions must be 600
~/.ssh/id_ed25519.pub Public Copy to servers. Safe to share.
# Fix permissions (required — SSH refuses keys with wrong perms)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
5.4 Copy Public Key to Server
# Easiest way:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
# Manual way:
cat ~/.ssh/id_ed25519.pub | ssh user@host 'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys'
# Fix permissions on server side:
ssh user@host 'chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys'
■ You can have multiple key pairs — one per server or purpose. Name them clearly.
6. SSH Agent
The SSH agent holds your decrypted private keys in memory so you only enter the passphrase once per session, not on
every connection.
6.1 Start & Add Keys
# Start agent (if not already running)
eval $(ssh-agent -s)
# Add default key (~/.ssh/id_ed25519)
ssh-add
# Add specific key
ssh-add ~/.ssh/myserver_key
# Add with expiry (key auto-removed after 4 hours)
ssh-add -t 14400 ~/.ssh/id_ed25519
# List loaded keys
ssh-add -l
# Remove all keys from agent
ssh-add -D
6.2 Agent Forwarding
Agent forwarding lets you SSH from a remote server to another server using your local keys — no need to copy private
keys to intermediate hosts.
ssh -A user@bastion # -A enables agent forwarding
# Now from bastion you can: ssh user@internal-server
■ Only enable agent forwarding to trusted hosts. A compromised server with forwarding can use your keys against other
servers.
6.3 macOS Keychain Integration
# Add to ~/.ssh/config to auto-load keys on macOS:
Host *
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
7. SSH Config File (~/.ssh/config)
The config file lets you define aliases and per-host defaults so you can type ssh myserver instead of long commands.
7.1 Full Example Config
# Global defaults
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
IdentitiesOnly yes
# Production web server
Host prod
HostName [Link]
User deploy
Port 22
IdentityFile ~/.ssh/prod_key
# Internal server via jump host
Host internal
HostName [Link]
User admin
ProxyJump bastion
# Bastion / jump host
Host bastion
HostName [Link]
User ec2-user
IdentityFile ~/.ssh/bastion_key
ForwardAgent yes
# GitHub
Host [Link]
User git
IdentityFile ~/.ssh/github_ed25519
7.2 Key Config Directives
Directive Effect
HostName Actual hostname or IP to connect to
User Default username
Port Non-standard port number
IdentityFile Path to private key for this host
IdentitiesOnly yes Only use specified key (don't try others)
ProxyJump host Jump through an intermediate SSH host
ForwardAgent yes Enable agent forwarding to this host
ServerAliveInterval N Send keepalive every N seconds
ServerAliveCountMax N Max missed keepalives before disconnect
StrictHostKeyChecking no / yes / ask — host key verification
Compression yes Enable data compression (slow links)
ControlMaster auto Enable connection multiplexing
ControlPath ~/.ssh/... Socket path for multiplexed connections
LogLevel VERBOSE Logging verbosity for debug
Permissions on ~/.ssh/config must be 600 or SSH will ignore the file.
8. Port Forwarding & Tunnelling
8.1 Local Port Forwarding (-L)
Forwards a local port to a destination via the SSH server. Useful to access a remote database or web UI locally.
# Forward local :8080 -> remote server's :80
ssh -L 8080:localhost:80 user@remote
# Access a DB on the remote network (not on remote itself):
ssh -L 5432:[Link] user@bastion
# Now: psql -h localhost -p 5432
# Persistent / background:
ssh -fNL 8080:localhost:80 user@remote
8.2 Remote Port Forwarding (-R)
Exposes a local port on the remote server. Useful to give others access to a local dev server.
# Expose local :3000 on remote's :9000
ssh -R 9000:localhost:3000 user@remote
# Anyone on the remote can now curl [Link]
# For external access, add to sshd_config on server:
GatewayPorts yes
8.3 Dynamic SOCKS Proxy (-D)
Turns SSH into a SOCKS5 proxy. Route any application's traffic through the remote server — acts like a simple VPN.
ssh -D 1080 -fN user@remote
# Then set SOCKS5 proxy in browser: localhost:1080
# Or use with curl:
curl --socks5 localhost:1080 [Link]
8.4 ProxyJump (-J)
Connect to a target through one or more bastion hosts in a single command.
ssh -J user@bastion user@target
# Multiple hops:
ssh -J user@hop1,user@hop2 user@target
# In config file:
Host target
ProxyJump bastion
Type Flag Direction Use Case
Local forward -L local:dest:port local → remote network Access remote DB locally
Remote forward -R remote:dest:port remote → local network Expose local service
Dynamic proxy -D port all traffic via remote Bypass firewall / VPN
ProxyJump -J jumphost chain through bastion Multi-hop access
9. File Transfer: SCP & SFTP
9.1 SCP (Secure Copy)
# Upload local file to remote
scp [Link] user@host:/remote/path/
# Download remote file
scp user@host:/remote/[Link] ./local/
# Recursive directory copy
scp -r ./mydir user@host:/home/user/
# Specify port
scp -P 2222 [Link] user@host:/tmp/
# Use a specific key
scp -i ~/.ssh/prod_key [Link] user@host:/tmp/
9.2 SFTP (Interactive)
sftp user@host
# Common SFTP commands:
# ls, lls — list remote / local directory
# cd, lcd — change remote / local directory
# get file — download file
# put file — upload file
# mget *.log — download multiple files
# mput *.csv — upload multiple files
# rm, rmdir — remove file / directory
# mkdir — create directory
# quit / bye — exit SFTP
9.3 rsync over SSH (Preferred for Large Transfers)
# Sync local dir to remote (incremental, compressed)
rsync -avz ./localdir/ user@host:/remote/dir/
# Dry run (preview changes):
rsync -avzn ./localdir/ user@host:/remote/dir/
# Custom SSH options with rsync:
rsync -avz -e 'ssh -p 2222 -i ~/.ssh/prod_key' ./src/ user@host:/dst/
■ rsync is far more efficient than scp for large directories — it only transfers changed files.
10. OpenSSH Server (sshd) — Setup & Hardening
10.1 Service Management
sudo systemctl start sshd # start
sudo systemctl stop sshd # stop
sudo systemctl restart sshd # restart
sudo systemctl enable sshd # start on boot
sudo systemctl status sshd # check status
# Reload config without dropping connections:
sudo systemctl reload sshd # or: sudo kill -HUP $(cat /run/[Link])
10.2 sshd_config — Secure Configuration
File location: /etc/ssh/sshd_config (root + reload required after changes)
# --- Port & Protocol ---
Port 2222 # change from default 22 (obscurity)
AddressFamily inet # IPv4 only (or inet6 / any)
ListenAddress [Link]
# --- Authentication ---
PermitRootLogin no # NEVER allow root login
MaxAuthTries 3 # limit brute-force attempts
MaxSessions 5
PubkeyAuthentication yes # enable key-based auth
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no # disable passwords (keys only!)
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM yes
# --- Restrictions ---
AllowUsers deploy admin youruser # whitelist users
# AllowGroups sshusers # or restrict by group
LoginGraceTime 30 # seconds to complete login
ClientAliveInterval 300
ClientAliveCountMax 2
# --- Forwarding (disable what you don't need) ---
X11Forwarding no
AllowAgentForwarding no # set yes only when needed
AllowTcpForwarding no # set yes only for tunnels
GatewayPorts no
# --- Logging ---
LogLevel VERBOSE # captures key fingerprints
SyslogFacility AUTH
# --- Cryptographic hardening (OpenSSH 8+) ---
KexAlgorithms curve25519-sha256,diffie-hellman-group16-sha512
Ciphers chacha20-poly1305@[Link],aes256-gcm@[Link]
MACs hmac-sha2-512-etm@[Link],hmac-sha2-256-etm@[Link]
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512
■ Disable PasswordAuthentication only AFTER you have verified key-based login works. Locking yourself out requires
console access.
10.3 Test Config Before Reload
sudo sshd -t # test — prints errors, silent on success
sudo sshd -T # dump full effective config
11. Key Management
11.1 List & Inspect Keys
# Show fingerprint of a key
ssh-keygen -lf ~/.ssh/id_ed25519.pub
# Show fingerprint in randomart:
ssh-keygen -lv -f ~/.ssh/id_ed25519.pub
# Show all keys the server has (from client):
ssh-keyscan hostname
11.2 Change Passphrase
ssh-keygen -p -f ~/.ssh/id_ed25519
11.3 Revoke / Remove a Key
# On the server, edit ~/.ssh/authorized_keys
# Delete the line containing the public key to revoke
nano ~/.ssh/authorized_keys
# Or use sed to remove a specific key (match by comment):
sed -i '/compromised-key-comment/d' ~/.ssh/authorized_keys
11.4 Known Hosts
When you first connect to a server, SSH stores its host key in ~/.ssh/known_hosts. On subsequent connections the key
is verified to detect MITM attacks.
# Remove a specific host entry (after server rebuild / IP change):
ssh-keygen -R hostname
ssh-keygen -R [Link]
# Show hash of a known host:
ssh-keygen -F hostname
■ Never blindly accept changed host keys. Verify the new fingerprint out-of-band (e.g. via the cloud console) before
removing the old entry.
12. Connection Multiplexing
Multiplexing reuses a single TCP connection for multiple SSH sessions, dramatically speeding up repeated connections
(no new handshake / auth).
# In ~/.ssh/config:
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600 # keep master alive 10 min after last session
# Create socket directory:
mkdir -p ~/.ssh/sockets
# Check active masters:
ls -la ~/.ssh/sockets/
# Close a master explicitly:
ssh -O exit user@host
■ Multiplexing is especially useful in CI/CD pipelines and Ansible — connections after the first are near-instant.
13. Git & GitHub over SSH
# 1. Generate a dedicated key:
ssh-keygen -t ed25519 -f ~/.ssh/github -C 'me@[Link]'
# 2. Add to GitHub: Settings > SSH Keys > New SSH Key
# (paste contents of ~/.ssh/[Link])
# 3. Add to ~/.ssh/config:
Host [Link]
User git
IdentityFile ~/.ssh/github
AddKeysToAgent yes
# 4. Test:
ssh -T git@[Link]
# Output: Hi username! You've successfully authenticated.
# 5. Clone via SSH:
git clone git@[Link]:user/[Link]
14. Security Checklist
# Check Why
1 Use Ed25519 keys Strongest modern algorithm
2 Set a passphrase on private key Protects if key file is stolen
3 Disable PasswordAuthentication Eliminates brute-force risk
4 Set PermitRootLogin no Root never needed over SSH
5 Change default port (22) Reduces automated scan noise
6 AllowUsers / AllowGroups Restrict who can log in
7 Enable LogLevel VERBOSE Audit trail for forensics
8 Install fail2ban / sshguard Auto-ban repeated failures
9 Use ProxyJump over agent fwd Safer multi-hop access
10 Rotate keys annually Limit blast radius of leak
11 Audit authorized_keys regularly Remove stale / unknown keys
12 Keep OpenSSH updated Patch known CVEs promptly
13 Use ssh-keygen -t to expire agent keys Limit exposure window
14 Restrict cipher suites in sshd_config Drop weak algorithms
15. Troubleshooting
Problem Likely Cause Fix
Permission denied (publickey) Wrong key / not in authorized_keys ssh-copy-id; check key permissions (600/700)
Connection refused sshd not running or firewall systemctl status sshd; check ufw/iptables
Host key verification failed Server key changed ssh-keygen -R hostname
Too many authentication failures Agent offers too many keys Use -o IdentitiesOnly=yes -i key
Timeout / hangs No keepalive / firewall drops idle Set ServerAliveInterval 60 in config
Bad permissions on config ~/.ssh/config is world-readable chmod 600 ~/.ssh/config
ssh-add: No such file Agent not running eval $(ssh-agent -s)
Broken pipe Network drop / idle timeout ServerAliveInterval + ServerAliveCountMax
Debug Steps
# 1. Verbose client output
ssh -vvv user@host 2>&1 | head -60
# 2. Check sshd logs on server
sudo journalctl -u sshd -n 50
sudo tail -f /var/log/[Link]
# 3. Verify authorized_keys on server
cat ~/.ssh/authorized_keys
ls -la ~/.ssh/
# 4. Test sshd config
sudo sshd -t
16. Quick Reference Card
Task Command
Connect ssh user@host
Custom port ssh -p 2222 user@host
Specific key ssh -i ~/.ssh/mykey user@host
Run command ssh user@host 'command'
Generate Ed25519 key ssh-keygen -t ed25519 -C 'comment'
Copy key to server ssh-copy-id user@host
Start agent eval $(ssh-agent -s)
Add key to agent ssh-add ~/.ssh/id_ed25519
List agent keys ssh-add -l
Upload file (scp) scp [Link] user@host:/path/
Download file (scp) scp user@host:/path/[Link] ./
Sync directory (rsync) rsync -avz ./src/ user@host:/dst/
SFTP session sftp user@host
Local tunnel :8080→:80 ssh -L 8080:localhost:80 user@host
Remote tunnel :9000→:3000 ssh -R 9000:localhost:3000 user@host
SOCKS proxy on :1080 ssh -D 1080 -fN user@host
Jump through bastion ssh -J bastion user@target
Remove known host ssh-keygen -R hostname
Test sshd config sudo sshd -t
Reload sshd sudo systemctl reload sshd
View sshd logs sudo journalctl -u sshd -n 50
SSH Complete Guide — generated with OpenSSH 9.x in mind. Check 'man ssh', 'man ssh_config', 'man sshd_config' for the full
reference.