Skip to content

SSH

SSH (Secure Shell) protocol version 2 provides encrypted remote login and command execution. The Protocol operates in three layers:

flowchart TD
    A["Transport Layer<br />(TCP/IP, encryption, server auth)"] --> B["Authentication Layer<br />(password, key, keyboard-interactive)"]
    B --> C["Connection Layer<br />(channels, forwarding, X11, SFTP)"]
  • TCP connection (default port 22)
  • Server presents host key for verification
  • Key exchange (curve25519-sha256, ecdh-sha2-nistp256, diffie-hellman-group14-sha256)
  • Symmetric encryption (chacha20-poly1305, aes256-gcm)
  • MAC / AEAD for integrity
  • Session keys derived from shared secret
  • Password authentication
  • Public key authentication (default preferred method)
  • Keyboard-interactive (PAM, 2FA, OTP)
  • GSSAPI (Kerberos)
  • Host-based authentication
  • Multiplexed channels over a single TCP connection
  • Session channels (shell, exec, subsystem)
  • Port forwarding channels
  • X11 forwarding
  • Agent forwarding
  • SFTP subsystem

The SSH client configuration file supports per-host settings, pattern matching, and conditional Blocks.

~/.ssh/config
# Global defaults
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
StrictHostKeyChecking accept-new
UserKnownHostsFile ~/.ssh/known_hosts
# Jump host (bastion)
Host bastion
HostName bastion.example.com
User deploy
Port 2222
IdentityFile ~/.ssh/id_bastion
# Internal servers via jump host
Host 10.0.0.*
ProxyJump bastion
User admin
IdentityFile ~/.ssh/id_internal
# Specific server
Host web-prod
HostName 10.0.0.10
User www
ProxyJump bastion
ForwardAgent yes
# GitHub
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_github
IdentitiesOnly yes
~/.ssh/config
Host *
User admin
# Override for specific hosts
Match host 10.0.0.* exec "ping -c 1 -W 1 %h"
ProxyJump bastion
# Match on original host (useful with ProxyJump)
Match host bastion.example.com
ForwardAgent yes
# Match on local user
Match host * user root
PermitTTY no
ForwardAgent no
# Match on destination port
Match host * port 2222
User jumpuser
HostName # actual hostname (not the alias)
User # login username
Port # SSH port (default 22)
IdentityFile # path to private key file
IdentitiesOnly # only use explicitly specified keys (default no)
ProxyJump # jump host (simpler than ProxyCommand)
ProxyCommand # custom command for connection (more flexible)
ForwardAgent # forward SSH agent (yes/no/ask)
ForwardX11 # forward X11 (yes/no/ask)
LocalForward # local port forwarding (-L)
RemoteForward # remote port forwarding (-R)
DynamicForward # SOCKS proxy (-D)
ServerAliveInterval # send keepalive every N seconds
ServerAliveCountMax # max missed keepalives before disconnect
TCPKeepAlive # enable TCP keepalive (default yes)
Compression # enable compression (yes/no)
ControlMaster # connection multiplexing (yes/no/ask/auto)
ControlPath # socket path for multiplexed connections
ControlPersist # how long to keep master connection open
StrictHostKeyChecking # (yes/no/accept-new/ask)
UserKnownHostsFile # path to known_hosts file
LogLevel # (QUIET/FATAL/ERROR/INFO/VERBOSE/DEBUG)
NumberOfPasswordPrompts # max password prompts (default 3)
# Enable connection sharing in ~/.ssh/config
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# Create socket directory
mkdir -p ~/.ssh/sockets
# First connection opens a master socket
ssh server.example.com
# Subsequent connections reuse the existing socket (instant!)
ssh server.example.com # reuses existing connection
scp file server.example.com:/tmp/ # also reuses
Terminal window
# Generate Ed25519 key (recommended — small, fast, secure)
ssh-keygen -t ed25519 -C "user@workstation"
ssh-keygen -t ed25519 -a 100 -C "user@workstation" # 100 KDF rounds
# Generate RSA key (4096 bits, for legacy compatibility)
ssh-keygen -t rsa -b 4096 -C "user@workstation"
# Generate ECDSA key
ssh-keygen -t ecdsa -b 521 -C "user@workstation"
# Specify output file
ssh-keygen -t ed25519 -f ~/.ssh/id_github -C "github-key"
# Generate key with no passphrase (for automation — use with caution)
ssh-keygen -t ed25519 -f ~/.ssh/id_deploy -N ""
# Change passphrase on existing key
ssh-keygen -p -f ~/.ssh/id_ed25519
# Generate public key from private key
ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub
# Generate fingerprint
ssh-keygen -l -f ~/.ssh/id_ed25519.pub
# Generate visual fingerprint (randomart)
ssh-keygen -lv -f ~/.ssh/id_ed25519.pub
OpenSSH (default):
id_ed25519 — private key (OpenSSH format)
id_ed25519.pub — public key (single line)
PEM (legacy):
id_rsa — "BEGIN RSA PRIVATE KEY" (PEM format)
id_rsa.pub — public key
PKCS8:
Convert with: ssh-keygen -p -f id_rsa -m PEM # to PEM
Convert with: ssh-keygen -p -f id_rsa -m RFC4716 # to RFC4716
Ed25519 keys:
- Best security per bit
- Fastest key operations (sign/verify)
- Smallest key size (64 bytes)
- Recommended for all new keys
RSA keys:
- Minimum 2048 bits (2048 is weak, 3072 is acceptable, 4096 is standard)
- Slower than Ed25519
- Widely compatible with legacy systems
# ~/.ssh/authorized_keys — one public key per line
# Format: [options] key-type base64-key [comment]
# Restrict key to specific command
command="/usr/bin/backup.sh",no-port-forwarding,no-X11-forwarding,no-pty ssh-ed25519 AAAA... backup@server
# Restrict by source IP
from="10.0.0.0/24" ssh-ed25519 AAAA... admin@office
# Disable specific forwarding
no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA... restricted
# Combined restrictions
command="/usr/local/bin/monitor",from="10.0.0.50",no-pty,no-port-forwarding ssh-ed25519 AAAA... monitor
# Restrict to specific environment variables
environment="PATH=/usr/bin:/bin" ssh-ed25519 AAAA... env-user
Terminal window
# Deploy public key to remote server
ssh-copy-id user@server.example.com
# Manual deployment
cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
# View authorized_keys with restrictions
cat ~/.ssh/authorized_keys
Terminal window
# Generate new key
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_new -C "user@workstation"
# Deploy new key
ssh-copy-id -i ~/.ssh/id_ed25519_new.pub user@server
# Test new key
ssh -i ~/.ssh/id_ed25519_new user@server
# Remove old key from authorized_keys on server
ssh user@server "sed -i "/OLD_KEY_COMMENT/d" ~/.ssh/authorized_keys"
# Update local config
sed -i 's/id_ed25519/id_ed25519_new/' ~/.ssh/config
# Remove old key
rm ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub
/etc/ssh/sshd_config
# Network
Port 22
AddressFamily inet # inet (IPv4 only), inet6, any
ListenAddress 0.0.0.0
ListenAddress ::
# Host keys
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
# Key exchange algorithms (drop weak ones)
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha256
# Ciphers
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
# MACs
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
# Authentication
PermitRootLogin prohibit-password # yes/no/prohibit-password/forced-commands-only
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
UsePAM no
# Authorized keys location
AuthorizedKeysFile .ssh/authorized_keys
AuthorizedPrincipalsFile none
# Access control
AllowUsers deploy admin@10.0.0.0/24
# AllowGroups ssh-users
# DenyUsers baduser
# DenyGroups nogroup
# Session
MaxAuthTries 3
MaxSessions 10
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
AllowTcpForwarding yes
PermitTunnel no
PermitTTY yes
# Security
StrictModes yes # check file permissions on key files
PermitRootLogin prohibit-password
AllowAgentForwarding no
AllowTcpForwarding no # disable if not needed
# Logging
SyslogFacility AUTH
LogLevel VERBOSE
# Banner
Banner /etc/ssh/banner
# Subsystems
Subsystem sftp /usr/lib/openssh/sftp-server
# or for chrooted SFTP:
# Subsystem sftp internal-sftp
Terminal window
# Generate host keys
ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key
ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key
# Show host key fingerprints
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
# Verify server fingerprint from client
ssh-keyscan server.example.com | ssh-keygen -lf -
Terminal window
# Validate configuration before restarting
sshd -t
sshd -T # show effective configuration
# Restart
systemctl restart sshd
# Check status
systemctl status sshd
systemctl is-active sshd
Terminal window
# Start the agent
eval $(ssh-agent)
ssh-agent bash # start a shell with agent
# Add keys to the agent
ssh-add # add default keys
ssh-add ~/.ssh/id_ed25519 # add specific key
ssh-add -l # list keys in agent
ssh-add -L # list public keys
ssh-add -d ~/.ssh/id_ed25519 # remove specific key
ssh-add -D # remove all keys
# Add key with limited lifetime
ssh-add -t 3600 ~/.ssh/id_ed25519 # 1 hour
ssh-add -t 8h ~/.ssh/id_ed25519 # 8 hours
# Lock agent
ssh-add -x # lock with password prompt
Terminal window
# Enable forwarding per-host in ~/.ssh/config
Host server
ForwardAgent yes
# Or via command line
ssh -A user@server
# Or via ProxyJump (forward agent through jump host)
Host internal
ProxyJump bastion
ForwardAgent yes