Build, inspect, and learn SSH without uploading anything:
- SSH Config Generator — visual ~/.ssh/config builder
- SSH Key Generator — generate Ed25519 / RSA keys in-browser
- SSH Command Builder — assemble any ssh command visually
1. What is SSH and How Does It Work?
SSH — Secure Shell — is a cryptographic network protocol defined in RFC 4251 through 4254. It provides a secure, encrypted channel between two machines over an untrusted network. Every major cloud provider (AWS, GCP, Azure, DigitalOcean), every Linux VPS, and most network hardware supports SSH as the primary remote-access mechanism.
Before SSH became standard in the late 1990s, administrators used Telnet and rlogin — protocols that transmitted usernames, passwords, and all session data in plain text. A passive network observer could trivially steal credentials. SSH solved this by layering all communication inside strong encryption.
The SSH Handshake — Step by Step
When you type ssh user@server, the following sequence happens in milliseconds:
- TCP connection — your client opens a TCP connection to port 22 (or a custom port) on the server.
- Protocol negotiation — both sides exchange SSH protocol version strings.
- Algorithm negotiation (KEXINIT) — client and server agree on key exchange algorithms, host key types, ciphers, MACs, and compression.
- Key exchange (KEX) — using Diffie-Hellman or ECDH, the two parties derive a shared secret without ever transmitting it. This becomes the session key.
- Server authentication — the server proves its identity by signing a value with its host private key. Your client verifies this against
~/.ssh/known_hosts. - User authentication — the server challenges your client. You authenticate via public key, password, or certificate.
- Encrypted session — all further traffic is symmetrically encrypted using the negotiated session key.
This design means even if someone captures every byte of your SSH session on the wire, they cannot decrypt it without the session key — which was never transmitted.
SSH Protocol Versions
SSH-1 (1995) had several cryptographic weaknesses and is considered completely insecure. All modern systems use SSH-2 (RFC 4251, 2006). If a server advertises SSH-1, treat it as a security incident. OpenSSH dropped SSH-1 support in version 7.6 (2017).
2. SSH Key Types: Ed25519 vs RSA vs ECDSA
SSH public-key authentication requires a matched key pair: a private key you keep secret, and a public key you deploy to servers. There are four common algorithms:
| Algorithm | Key Size | Speed | Security | Compatibility | Recommendation |
|---|---|---|---|---|---|
| Ed25519 | 256-bit (fixed) | Very fast | Excellent | OpenSSH 6.5+ (2014) | ✅ Use this |
| ECDSA (P-256) | 256–521-bit | Fast | Good* | OpenSSH 5.7+ (2011) | ⚠️ Acceptable |
| RSA | 2048–4096-bit | Slower | Good at 4096 | Universal | ⚠️ Legacy only |
| DSA | 1024-bit (fixed) | Slow | Broken | Removed in OpenSSH 7.0 | ❌ Never use |
* ECDSA security depends on strong randomness during signing. Ed25519 is deterministic and immune to weak-RNG attacks.
Use Ed25519 by default. It produces a 68-character public key (vs 728 characters for RSA-4096), signs and verifies faster, and has stronger security guarantees. The only reason to use RSA-4096 in 2026 is compatibility with systems running OpenSSH older than 6.5, which is extremely rare.
You can use our browser-only SSH Key Generator to generate Ed25519 or RSA key pairs entirely locally — no server, no upload, no logging.
3. Generating SSH Keys
The ssh-keygen utility comes pre-installed with OpenSSH on Linux, macOS, and Windows 10+. Here are the most useful key generation commands:
# Generate an Ed25519 key (recommended, 2026)
ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/id_ed25519
# Generate RSA-4096 for legacy servers that don't support Ed25519
ssh-keygen -t rsa -b 4096 -C "[email protected]" -f ~/.ssh/id_rsa_legacy
# View the public key (safe to share)
cat ~/.ssh/id_ed25519.pub
# Copy the public key to a remote server
ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected] Key Generation Options Explained
-t: Algorithm type (ed25519,rsa,ecdsa).-b: Bit length. Only meaningful for RSA (use 4096). Ed25519 ignores this.-C: Comment embedded in the public key. Use your email or a descriptive label likedeploy-key-prod-2026.-f: Output filename. Always specify this when generating multiple keys to avoid overwriting your default key.-N: Passphrase (use-N ""for no passphrase — only for automated deploy keys with restricted permissions).
Passphrases and the SSH Agent
Every private key should have a passphrase. This encrypts the key at rest — if someone steals your ~/.ssh/ directory, they still cannot use the keys without the passphrase. The passphrase only unlocks the key; it is never sent to the server.
To avoid typing the passphrase repeatedly, add the key to ssh-agent: ssh-add ~/.ssh/id_ed25519. The agent holds the decrypted key in memory for the session. On macOS, adding UseKeychain yes to your SSH config persists the passphrase across reboots via Keychain.
File Permissions — The Silent Breaker
OpenSSH will silently refuse to use a private key with open permissions. Set these immediately after generating:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519 # private key
chmod 644 ~/.ssh/id_ed25519.pub # public key (can be world-readable)
chmod 600 ~/.ssh/config
chmod 600 ~/.ssh/authorized_keys 4. Managing authorized_keys
The ~/.ssh/authorized_keys file on a server lists all public keys permitted to authenticate. One public key per line. You can have hundreds of entries in one file.
# On the remote server, authorized_keys format:
# ~/.ssh/authorized_keys
# Standard entry
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... [email protected]
# Restrict to specific commands only
command="backup-script.sh",no-pty,no-port-forwarding ssh-ed25519 AAAA... backup-key
# Restrict by source IP
from="203.0.113.0/24" ssh-ed25519 AAAA... office-deploy-key Key Options in authorized_keys
Each line in authorized_keys can have prefix options that restrict how and when that key is valid:
command="script.sh"— the connection can only run that specific command, not a shell.no-pty— prevents pseudoterminal allocation (useful for automated scripts).no-port-forwarding— disables tunneling for that key.from="IP"— restricts which source IPs can authenticate with that key.expiry-time="20261231"— key expires on that date (OpenSSH 8.2+).
Deploying Keys Safely
The safest way to add a key to a server is ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server. This appends the key to authorized_keys with correct permissions and avoids the common mistake of overwriting the file. If you do not have ssh-copy-id, the manual equivalent is:
cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys" 5. The ~/.ssh/config File: Complete Reference
The SSH client config file is the single biggest productivity improvement for anyone managing more than two servers. Instead of memorizing ssh [email protected] -p 2222 -i ~/.ssh/id_ed25519_prod, you just type ssh prod.
Use our SSH Config Generator to build your config visually with zero syntax errors, then paste the output into ~/.ssh/config.
# ~/.ssh/config
# Permissions must be 600: chmod 600 ~/.ssh/config
# ── Global defaults for all hosts ───────────────────────────
Host *
AddKeysToAgent yes
UseKeychain yes # macOS only
ServerAliveInterval 60
ServerAliveCountMax 3
IdentitiesOnly yes
# ── Production web server ────────────────────────────────────
Host prod
HostName 198.51.100.24
User deploy
Port 2222
IdentityFile ~/.ssh/id_ed25519_prod
# ── Staging server ───────────────────────────────────────────
Host staging
HostName 198.51.100.50
User deploy
IdentityFile ~/.ssh/id_ed25519_staging
# ── Bastion / jump host ──────────────────────────────────────
Host bastion
HostName 203.0.113.10
User bastion-user
IdentityFile ~/.ssh/id_ed25519_bastion
# ── Internal DB (reached via bastion) ───────────────────────
Host internal-db
HostName 10.0.1.50
User db-admin
IdentityFile ~/.ssh/id_ed25519_db
ProxyJump bastion
# ── GitHub (shorthand) ───────────────────────────────────────
Host github.com
IdentityFile ~/.ssh/id_ed25519_github
User git Config File Precedence Rules
SSH evaluates the config file top-to-bottom. The first matching value for a parameter wins. This means global defaults in Host * should go at the bottom, because specific host blocks at the top take precedence. If you put Host * first with a specific IdentityFile, that overrides every specific host block below it.
Most Useful Config Directives
| Directive | Effect | Example |
|---|---|---|
HostName | Real hostname or IP | 203.0.113.10 |
User | SSH username | deploy |
Port | SSH port | 2222 |
IdentityFile | Path to private key | ~/.ssh/id_ed25519_prod |
IdentitiesOnly | Only use specified keys | yes |
ProxyJump | Jump through a bastion | bastion |
ForwardAgent | Forward SSH agent | yes (use carefully) |
LocalForward | Local port forward | 5432 db.internal:5432 |
ServerAliveInterval | Keepalive seconds | 60 |
StrictHostKeyChecking | Host key checking | accept-new (safer than no) |
ControlMaster | Multiplexing | auto |
ControlPath | Multiplex socket path | ~/.ssh/cm-%r@%h:%p |
Wildcards and Patterns in Host
The Host directive supports wildcards: Host *.staging.example.com matches any subdomain. Host 10.0.1.* matches any IP in that range. You can also negate patterns: Host * !bastion matches everything except the bastion alias.
6. Jump Hosts and ProxyJump
A jump host (bastion host) is an internet-facing server that acts as a secure gateway to servers on a private network. Private servers (like databases, Kubernetes nodes, or internal APIs) never have public IPs — you must go through the bastion.
The Modern Way: ProxyJump
ProxyJump (added in OpenSSH 7.3, 2016) is the clean, modern approach. It creates a nested SSH connection: your client connects to the jump host, then the jump host's SSH client (running inside the same SSH session) connects to the target. The target server sees a connection from the jump host's IP.
# One-liner with -J flag
ssh -J [email protected] [email protected]
# With SSH config (much cleaner)
# Add to ~/.ssh/config:
# Host internal-db
# HostName 10.0.1.50
# User db-admin
# ProxyJump bastion
#
# Then just:
ssh internal-db Chaining Multiple Jump Hosts
If your architecture has multiple network hops (e.g., internet → DMZ bastion → production network bastion → target), you can chain them:
# Two jump hops
ssh -J user@dmz-bastion,user@prod-bastion user@final-server
# In SSH config:
# Host final-server
# HostName 10.10.2.100
# ProxyJump dmz-bastion,prod-bastion ProxyJump vs ProxyCommand
The older approach used ProxyCommand with ssh -W. ProxyJump is strictly better: it is simpler, supports agent forwarding correctly, and OpenSSH optimizes the connection path internally. Only use ProxyCommand if you need a non-SSH proxy (e.g., nc or socat for custom routing).
7. SSH Port Forwarding and Tunnels
SSH port forwarding (SSH tunneling) lets you route arbitrary TCP traffic through an SSH connection. It is how developers securely access databases, admin UIs, and internal services without exposing them to the internet.
# Local port forwarding: reach remote DB at localhost:5432
ssh -L 5432:postgres.internal:5432 -N -f bastion
# Remote port forwarding: expose local dev server on remote port
ssh -R 8080:localhost:3000 -N -f your-server
# Dynamic SOCKS5 proxy (all traffic through the tunnel)
ssh -D 1080 -N -f your-server
# Persistent tunnel using SSH config (add to ~/.ssh/config):
# Host db-tunnel
# HostName bastion.example.com
# User bastion-user
# LocalForward 5432 postgres.internal:5432
# ServerAliveInterval 30 Local Port Forwarding (-L)
Local forwarding binds a port on your local machine and forwards connections through the SSH server to a target address. The target is resolved by the SSH server, not your machine. This is how you reach a database on a private network:
# Your local :5432 → SSH server → postgres.internal:5432
ssh -L 5432:postgres.internal:5432 -N -f user@jump-server
# Now connect your DB client to localhost:5432
psql -h localhost -p 5432 -U app_user mydb -N means "don't run a command after connecting" (just forward). -f sends the process to the background.
Remote Port Forwarding (-R)
Remote forwarding binds a port on the remote server and forwards connections back to your local machine. This lets you temporarily expose a local dev server to the internet through a public server you control:
# Remote server's :8080 → back to your localhost:3000
ssh -R 8080:localhost:3000 -N user@your-public-server
# Now visiting http://your-public-server:8080 hits your local app Dynamic SOCKS5 Proxy (-D)
Dynamic forwarding creates a SOCKS5 proxy on your local machine that routes all traffic through the SSH server. Configure your browser or system proxy to localhost:1080 and all your web traffic exits from the server's network — useful for accessing geo-restricted resources or testing from different regions:
ssh -D 1080 -N -f user@your-server
# Configure browser: SOCKS5 proxy → localhost:1080 8. SSH Agent and Agent Forwarding
ssh-agent is a background process that holds your decrypted private keys in memory. When you run ssh-add ~/.ssh/id_ed25519, the agent stores the key. Every SSH connection then asks the agent to perform authentication operations — you enter your passphrase once per session.
# Start the agent (usually auto-started by your desktop session)
eval "$(ssh-agent -s)"
# Add a key with passphrase caching
ssh-add ~/.ssh/id_ed25519
# Add with a time limit (key auto-expires after 8 hours)
ssh-add -t 28800 ~/.ssh/id_ed25519
# List loaded keys
ssh-add -l
# Remove all keys from agent
ssh-add -D Agent Forwarding: Power and Risk
ForwardAgent yes in your SSH config causes your local agent to be made available on the remote server. While connected to that server, it can make further SSH connections (e.g., git pull from a private repo) using your local key — your private key never leaves your machine.
The risk: anyone with root access on the remote server can use your forwarded agent socket to authenticate as you to other systems during your session. The socket disappears when you disconnect, but the window is real. Best practice: only forward the agent to servers you fully trust, and consider using ssh-add -c (confirms each use) for sensitive keys.
9. SSH Multiplexing (ControlMaster)
Every SSH connection requires a full TLS-like handshake. If you run many short-lived SSH commands (e.g., git pull, Ansible tasks, rsync), this overhead adds up. SSH multiplexing reuses a single connection for multiple sessions.
# Add to ~/.ssh/config:
Host your-server
HostName 203.0.113.10
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 10m
# First connection opens the master; subsequent connections reuse it instantly
# ControlPersist 10m keeps the master alive 10 minutes after the last session closes
With multiplexing, ssh your-server date goes from ~300ms (handshake) to ~15ms (socket lookup). For Ansible running 50 tasks, this saves minutes per playbook run.
10. Server-Side Hardening (sshd_config)
The server-side SSH daemon is configured at /etc/ssh/sshd_config. Poor defaults on a fresh cloud instance are responsible for a significant share of server compromises. Here is a production-ready hardening configuration:
# /etc/ssh/sshd_config — server hardening checklist
Port 2222 # non-default port
PermitRootLogin no # never allow direct root login
PasswordAuthentication no # key-only auth
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
AllowUsers deploy admin # explicit allowlist
LoginGraceTime 30
MaxAuthTries 3
MaxSessions 5
X11Forwarding no
AllowAgentForwarding no # disable unless needed
ClientAliveInterval 300
ClientAliveCountMax 2
# After editing, verify config and reload:
# sshd -t && systemctl reload sshd Host Keys and known_hosts
When you first connect to a server, SSH asks you to verify its host key fingerprint. If you accept, the key is stored in ~/.ssh/known_hosts. On subsequent connections, SSH compares the server's key to this stored value. A mismatch triggers a loud warning — this protects you against man-in-the-middle attacks.
To get the host key fingerprint before connecting (e.g., from your cloud provider's console), use:
# On the server (run this before exposing SSH):
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
# Remove a stale known_hosts entry (after server rebuild):
ssh-keygen -R hostname-or-ip Certificate-Based Authentication (Advanced)
For organizations with many servers and users, managing authorized_keys files at scale is painful. SSH certificates (via a Certificate Authority) solve this: users get short-lived signed certificates instead of deploying public keys to every server. This is how large companies (GitHub, Facebook) handle SSH at scale. OpenSSH supports this natively via TrustedUserCAKeys in sshd_config.
11. Troubleshooting SSH Connections
SSH's verbose mode is your best diagnostic tool. Add -v, -vv, or -vvv for increasing verbosity:
ssh -vvv user@server 2>&1 | head -80 Common Issues and Fixes
| Error / Symptom | Likely Cause | Fix |
|---|---|---|
| Permission denied (publickey) | Wrong key, wrong user, bad permissions | Check -vvv output; verify authorized_keys perms (600); confirm correct -i key |
| Connection timed out | Firewall blocking port 22 | Check security groups / iptables; try nc -zvw5 host 22 |
| Host key verification failed | Server rebuilt or MITM | Run ssh-keygen -R host if server rebuilt; investigate if unexpected |
| WARNING: UNPROTECTED PRIVATE KEY | Key has wrong permissions | chmod 600 ~/.ssh/id_ed25519 |
| Too many authentication failures | Agent offering too many keys | Add IdentitiesOnly yes to SSH config |
| Broken pipe / connection drops | No keepalive configured | Add ServerAliveInterval 60 to SSH config |
| ProxyJump authentication fails | No agent forwarding to jump host | Add ForwardAgent yes for the jump host block |
Reading SSH Logs on the Server
# Most Linux distributions:
journalctl -u sshd -f
# Or directly:
tail -f /var/log/auth.log # Debian/Ubuntu
tail -f /var/log/secure # RHEL/CentOS/Fedora 12. Essential SSH Command Reference
Use our SSH Command Builder to assemble complex SSH commands visually, with options for tunneling, agent forwarding, custom ports, and more.
| Command | Purpose |
|---|---|
ssh user@host | Basic connection |
ssh -p 2222 user@host | Custom port |
ssh -i ~/.ssh/key user@host | Specific identity file |
ssh -J jump user@target | Jump through bastion |
ssh -L 8080:internal:80 user@host | Local port forward |
ssh -R 8080:localhost:3000 user@host | Remote port forward |
ssh -D 1080 -N user@host | SOCKS5 proxy |
ssh -A user@host | Agent forwarding |
ssh -N -f user@host | Background, no shell |
ssh -o StrictHostKeyChecking=accept-new user@host | Auto-accept new host keys |
ssh-copy-id -i key.pub user@host | Deploy public key |
ssh-keygen -R hostname | Remove from known_hosts |
scp file.txt user@host:/path/ | Copy file to server |
rsync -avz -e ssh ./dir user@host:/path/ | Sync directory over SSH |
Frequently Asked Questions
- What is SSH and how does it work?
- SSH (Secure Shell) is a cryptographic network protocol that creates an encrypted tunnel between two machines. It uses asymmetric key exchange (Diffie-Hellman / ECDH) to derive a shared session key, then encrypts all traffic symmetrically. Authentication can use passwords, public-key pairs, or certificates. All modern remote server access relies on SSH.
- Which SSH key type should I use in 2026?
- Ed25519 is the recommended key type in 2026. It produces shorter keys, signs faster than RSA, and is based on elliptic curve cryptography with no known practical attacks. Use
ssh-keygen -t ed25519. Only fall back to RSA-4096 for legacy systems running OpenSSH older than 6.5. - Where is the SSH config file located?
- The per-user SSH config file is at
~/.ssh/configon Linux and macOS, and atC:\Users\YourName\.ssh\configon Windows. Permissions must be 600 (chmod 600 ~/.ssh/config). The system-wide config is at/etc/ssh/ssh_config. - What is an SSH jump host (ProxyJump)?
- A jump host is an intermediate server you pass through to reach servers on a private network. Configure it with
ProxyJump bastionin~/.ssh/configunder the target host block. On the command line, usessh -J user@bastion user@target. ProxyJump replaced the older ProxyCommand pattern in OpenSSH 7.3+. - How do I set up SSH key-based authentication?
- Run
ssh-keygen -t ed25519to generate a key pair. Deploy the public key withssh-copy-id user@server. Verify you can log in with the key, then harden the server by settingPasswordAuthentication noin/etc/ssh/sshd_configand runningsystemctl reload sshd. - What is SSH agent forwarding and when should I use it?
- Agent forwarding (
ForwardAgent yes) makes your local SSH keys available on a remote server for further SSH connections (e.g., git operations). Use it only on servers you fully trust — a root user on the remote machine can use your forwarded agent socket during your session. For most use cases, ProxyJump is safer than agent forwarding. - How do I set up SSH port forwarding?
- Local forwarding:
ssh -L localport:remote-host:remoteport -N user@jump— your local port connects through the jump host to the remote service. Remote forwarding:ssh -R remoteport:localhost:localport user@server— exposes your local service on the server's port. Dynamic (-D 1080) creates a SOCKS5 proxy. - How do I harden SSH on my server?
- Key steps: disable root login (
PermitRootLogin no), disable password auth (PasswordAuthentication no), change the default port, restrict allowed users (AllowUsers deploy), enable fail2ban, setMaxAuthTries 3andLoginGraceTime 30. Always runsshd -tto validate config before reloading.