← Back to all cheatsheets
Linux
sshsecurityremote-accessnetworkinglinux

SSH Cheat Sheet

Basic SSH Commands

Connect to Remote Host

# Basic connection
ssh username@hostname
ssh [email protected]

# Connect on specific port
ssh -p 2222 username@hostname

# Connect with verbose output
ssh -v username@hostname
ssh -vv username@hostname    # More verbose
ssh -vvv username@hostname   # Debug level

# Connect with specific identity file
ssh -i ~/.ssh/id_rsa username@hostname

# Execute command on remote host
ssh username@hostname "ls -la"

# Execute multiple commands
ssh username@hostname "cd /var/log && tail -f syslog"

# Run command in background
ssh -f username@hostname "command"

SSH Key Setup

Generate SSH Keys

# Generate RSA key (default 3072 bits)
ssh-keygen

# Generate RSA key with specific size
ssh-keygen -t rsa -b 4096

# Generate ED25519 key (recommended, more secure)
ssh-keygen -t ed25519

# Generate with comment
ssh-keygen -t ed25519 -C "[email protected]"

# Generate with custom filename
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_work

# Generate key with passphrase (interactive)
ssh-keygen -t ed25519

# Generate key with passphrase (non-interactive)
ssh-keygen -t ed25519 -N "your_passphrase" -f ~/.ssh/id_ed25519

# Generate key without passphrase (NOT RECOMMENDED)
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519_nopass

Why Use Passphrase on SSH Keys

# RECOMMENDED: Always protect your SSH key with a passphrase
#
# Benefits:
# 1. If key file is stolen, attacker still needs passphrase
# 2. Two-factor security: something you have (key) + something you know (passphrase)
# 3. Prevents unauthorized use if laptop is compromised
#
# Best Practice:
# - Use strong passphrase (12+ characters)
# - Use ssh-agent to avoid typing passphrase repeatedly
# - Never create keys without passphrase for production systems

# Generate properly secured key
ssh-keygen -t ed25519 -C "[email protected]"
# When prompted, enter a strong passphrase (not empty!)

Copy SSH Key to Remote Host

# Copy public key to remote server
ssh-copy-id username@hostname
ssh-copy-id -i ~/.ssh/id_ed25519.pub username@hostname

# Copy to specific port
ssh-copy-id -p 2222 username@hostname

# Manual copy (if ssh-copy-id not available)
cat ~/.ssh/id_ed25519.pub | ssh username@hostname "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# Or using scp
scp ~/.ssh/id_ed25519.pub username@hostname:~/.ssh/authorized_keys

# Set proper permissions on remote
ssh username@hostname "chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"

Using ssh-agent (Avoid Repeated Passphrase Entry)

# Start ssh-agent
eval "$(ssh-agent -s)"

# Add key to agent (prompts for passphrase once)
ssh-add ~/.ssh/id_ed25519

# Add key with timeout (hours)
ssh-add -t 3600 ~/.ssh/id_ed25519

# List added keys
ssh-add -l

# Remove all keys from agent
ssh-add -D

# Remove specific key
ssh-add -d ~/.ssh/id_ed25519

# Auto-start ssh-agent in ~/.bashrc or ~/.zshrc
if [ -z "$SSH_AUTH_SOCK" ]; then
   eval "$(ssh-agent -s)"
   ssh-add ~/.ssh/id_ed25519 2>/dev/null
fi

Setting Up SSH on EC2 (AWS)

Initial EC2 Setup

# 1. Launch EC2 instance with key pair
# AWS Console -> EC2 -> Launch Instance -> Select/Create Key Pair

# 2. Download private key (e.g., mykey.pem)

# 3. Set proper permissions
chmod 400 ~/Downloads/mykey.pem

# 4. Connect to EC2 instance
ssh -i ~/Downloads/mykey.pem [email protected]

# For Ubuntu AMI
ssh -i ~/Downloads/mykey.pem [email protected]

# For Amazon Linux
ssh -i ~/Downloads/mykey.pem [email protected]

Configure SSH Key-Only Authentication on EC2

# 1. Connect to EC2
ssh -i mykey.pem ec2-user@ec2-ip

# 2. Create new user (if needed)
sudo useradd -m -s /bin/bash newuser
sudo mkdir -p /home/newuser/.ssh
sudo chmod 700 /home/newuser/.ssh

# 3. Add your public key
# On local machine, copy your public key
cat ~/.ssh/id_ed25519.pub

# On EC2, add to authorized_keys
sudo nano /home/newuser/.ssh/authorized_keys
# Paste public key, save and exit

# 4. Set proper permissions
sudo chmod 600 /home/newuser/.ssh/authorized_keys
sudo chown -R newuser:newuser /home/newuser/.ssh

# 5. Configure SSH daemon for key-only authentication
sudo nano /etc/ssh/sshd_config

# Add/modify these settings:
# PubkeyAuthentication yes
# PasswordAuthentication no
# ChallengeResponseAuthentication no
# UsePAM no

# 6. Restart SSH service
sudo systemctl restart sshd

# 7. Test connection (in new terminal, keep old one open!)
ssh newuser@ec2-ip

Setting Up SSH on KVM Virtual Machine

KVM VM SSH Setup

# 1. Ensure SSH server is installed on VM
# On VM:
sudo apt update
sudo apt install openssh-server  # Ubuntu/Debian
# OR
sudo yum install openssh-server  # RHEL/CentOS

# 2. Enable and start SSH service
sudo systemctl enable ssh
sudo systemctl start ssh
sudo systemctl status ssh

# 3. Check VM IP address
ip addr show
# Or
hostname -I

# 4. On host machine, copy SSH key to VM
ssh-copy-id username@vm-ip-address

# 5. Test connection
ssh username@vm-ip-address

# 6. Configure SSH for key-only authentication
# On VM:
sudo nano /etc/ssh/sshd_config

# Set these values:
# PubkeyAuthentication yes
# PasswordAuthentication no
# PermitRootLogin no

# 7. Restart SSH
sudo systemctl restart ssh

KVM Network Configuration for SSH Access

# Check KVM network
virsh net-list
virsh net-dhcp-leases default

# Get VM IP
virsh domifaddr vm-name

# Configure port forwarding (if using NAT)
# Forward host port 2222 to VM port 22
sudo iptables -t nat -A PREROUTING -p tcp --dport 2222 -j DNAT --to-destination VM_IP:22

# Connect via forwarded port
ssh -p 2222 username@localhost

Jump Host (Bastion Host) Setup

What is a Jump Host?

Internet -> Jump Host (Public IP) -> Private Instances (No Public IP)

Purpose:
- Single point of entry to private network
- Enhanced security (only jump host exposed to internet)
- Centralized access control and logging

Basic Jump Host Connection

# Method 1: Two-step connection
ssh user@jumphost
# Then from jump host:
ssh user@private-instance

# Method 2: ProxyJump (SSH 7.3+)
ssh -J user@jumphost user@private-instance

# Method 3: Multiple jumps
ssh -J user@jump1,user@jump2 user@final-host

# Method 4: ProxyCommand (older SSH versions)
ssh -o ProxyCommand="ssh -W %h:%p user@jumphost" user@private-instance

Setting Up Jump Host

# On Jump Host:
# 1. Install SSH server
sudo apt install openssh-server

# 2. Create user accounts
sudo useradd -m -s /bin/bash adminuser
sudo mkdir -p /home/adminuser/.ssh
sudo chmod 700 /home/adminuser/.ssh

# 3. Add public keys
sudo nano /home/adminuser/.ssh/authorized_keys
# Paste public key

sudo chmod 600 /home/adminuser/.ssh/authorized_keys
sudo chown -R adminuser:adminuser /home/adminuser/.ssh

# 4. Harden SSH configuration
sudo nano /etc/ssh/sshd_config

# Recommended settings:
# PermitRootLogin no
# PasswordAuthentication no
# PubkeyAuthentication yes
# AllowUsers adminuser
# MaxAuthTries 3
# ClientAliveInterval 300
# ClientAliveCountMax 2

# 5. Restart SSH
sudo systemctl restart sshd

# 6. Configure firewall
sudo ufw allow 22/tcp
sudo ufw enable

Accessing Private Instance Through Jump Host

# Setup:
# 1. Add your public key to jump host
ssh-copy-id user@jumphost-ip

# 2. From jump host, add key to private instance
# Either:
# a) Copy your private key to jump host (NOT RECOMMENDED)
# b) Use SSH agent forwarding (RECOMMENDED)

# Method 1: SSH Agent Forwarding (RECOMMENDED)
# On local machine, add key to agent
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

# Connect with agent forwarding
ssh -A user@jumphost

# Now from jump host, you can connect to private instance
ssh user@private-instance-ip

# Method 2: ProxyJump (Most Convenient)
ssh -J user@jumphost user@private-instance-ip

# Method 3: Using SSH config (see below for details)

SSH Config File (~/.ssh/config)

Basic SSH Config

# Create/edit config file
nano ~/.ssh/config

# Set proper permissions
chmod 600 ~/.ssh/config

SSH Config Examples

# Example 1: Simple host alias
Host myserver
    HostName 192.168.1.100
    User admin
    Port 22
    IdentityFile ~/.ssh/id_ed25519

# Usage: ssh myserver (instead of ssh [email protected])

# Example 2: EC2 Instance
Host ec2-prod
    HostName ec2-xx-xx-xx-xx.compute.amazonaws.com
    User ec2-user
    IdentityFile ~/.ssh/aws-key.pem
    ServerAliveInterval 60

# Usage: ssh ec2-prod

# Example 3: Jump Host Configuration
Host jumphost
    HostName jump.example.com
    User admin
    IdentityFile ~/.ssh/id_ed25519_jump
    ServerAliveInterval 60

Host private-server
    HostName 10.0.1.50
    User ubuntu
    IdentityFile ~/.ssh/id_ed25519
    ProxyJump jumphost

# Usage: ssh private-server (automatically goes through jumphost)

# Example 4: Multiple Jump Hosts
Host jump1
    HostName jump1.example.com
    User admin
    IdentityFile ~/.ssh/id_jump1

Host jump2
    HostName 10.0.0.10
    User admin
    IdentityFile ~/.ssh/id_jump2
    ProxyJump jump1

Host final-server
    HostName 10.0.1.100
    User ubuntu
    IdentityFile ~/.ssh/id_ed25519
    ProxyJump jump1,jump2

# Usage: ssh final-server

# Example 5: Wildcard for multiple hosts
Host *.example.com
    User admin
    IdentityFile ~/.ssh/id_ed25519
    ServerAliveInterval 60
    ForwardAgent yes

# Example 6: Development servers pattern
Host dev-*
    User developer
    IdentityFile ~/.ssh/id_ed25519_dev
    ForwardAgent no

Host prod-*
    User admin
    IdentityFile ~/.ssh/id_ed25519_prod
    ForwardAgent no

# Example 7: Complete production setup
Host bastion
    HostName bastion.company.com
    User myuser
    IdentityFile ~/.ssh/id_ed25519_company
    ServerAliveInterval 60
    ServerAliveCountMax 3

Host db-server
    HostName 10.0.1.10
    User dbadmin
    IdentityFile ~/.ssh/id_ed25519_company
    ProxyJump bastion
    LocalForward 5432 localhost:5432

Host web-server
    HostName 10.0.1.20
    User webadmin
    IdentityFile ~/.ssh/id_ed25519_company
    ProxyJump bastion
    LocalForward 3306 localhost:3306

# Example 8: Default settings for all hosts
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    Compression yes
    ControlMaster auto
    ControlPath ~/.ssh/control-%r@%h:%p
    ControlPersist 10m

SSH Config Options Reference

# Common Options:
# HostName          - Real hostname or IP
# User              - Username for connection
# Port              - SSH port (default 22)
# IdentityFile      - Path to private key
# ProxyJump         - Jump through intermediate host(s)
# LocalForward      - Local port forwarding
# RemoteForward     - Remote port forwarding
# DynamicForward    - SOCKS proxy
# ForwardAgent      - Forward SSH agent (yes/no)
# ServerAliveInterval - Send keepalive every N seconds
# ServerAliveCountMax - Max failed keepalives before disconnect
# Compression       - Enable compression (yes/no)
# StrictHostKeyChecking - Check host keys (yes/no/ask)
# UserKnownHostsFile - Location of known_hosts file
# LogLevel          - Logging verbosity (QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG)

PreferredAuthentications - SSH Key vs Password

Understanding PreferredAuthentications

# PreferredAuthentications specifies the order of authentication methods
# SSH tries each method in order until one succeeds

# Common authentication methods:
# - publickey: SSH key authentication
# - password: Password authentication
# - keyboard-interactive: Challenge-response (like password but more flexible)
# - gssapi-with-mic: Kerberos authentication

When to Use SSH Key vs Password

SSH Key Authentication (RECOMMENDED):
✓ Production servers and critical systems
✓ Automated scripts and CI/CD pipelines
✓ Jump hosts and bastion servers
✓ Long-term server access
✓ When security is paramount
✓ Git operations (GitHub, GitLab, etc.)

Password Authentication (LIMITED USE):
✓ Initial server setup (before keys deployed)
✓ Emergency access scenarios
✓ Short-term or temporary access
✓ Testing/development environments (with caution)
✗ NOT for production systems
✗ NOT for automated processes

SSH Config Examples with PreferredAuthentications

Example 1: Force SSH Key Only (Production Servers)

# Production servers - SSH key ONLY, never try password
Host prod-* *.production.com
    User admin
    IdentityFile ~/.ssh/id_ed25519_prod
    PreferredAuthentications publickey
    # This ensures SSH won't even try password auth
    PubkeyAuthentication yes
    PasswordAuthentication no

# Usage: ssh prod-web-01
# Will ONLY attempt publickey authentication

Example 2: Try SSH Key First, Fall Back to Password

# Development server - prefer key, allow password fallback
Host dev-* *.dev.internal
    User developer
    IdentityFile ~/.ssh/id_ed25519_dev
    PreferredAuthentications publickey,password
    # Tries publickey first, then password if key fails

# Usage: ssh dev-server
# 1. Tries SSH key first
# 2. If key fails, prompts for password

Example 3: Password Only (Legacy Systems)

# Legacy system that doesn't support key auth
Host legacy-server
    HostName old.system.local
    User olduser
    PreferredAuthentications password
    # Only use password authentication
    PubkeyAuthentication no

# Usage: ssh legacy-server
# Will only prompt for password

Example 4: Multiple Keys with Fallback

# Try multiple keys before falling back to password
Host flexible-server
    HostName server.example.com
    User admin
    IdentityFile ~/.ssh/id_ed25519_primary
    IdentityFile ~/.ssh/id_rsa_backup
    PreferredAuthentications publickey,keyboard-interactive,password
    # 1. Tries id_ed25519_primary
    # 2. Tries id_rsa_backup
    # 3. Tries keyboard-interactive
    # 4. Tries password

Example 5: Jump Host with Key, Target with Password

# Jump host requires SSH key
Host jumphost
    HostName jump.company.com
    User jumpadmin
    IdentityFile ~/.ssh/id_ed25519_jump
    PreferredAuthentications publickey
    PasswordAuthentication no

# Target server behind jump host accepts password
Host internal-dev
    HostName 10.0.1.50
    User developer
    PreferredAuthentications password,publickey
    ProxyJump jumphost

# Usage: ssh internal-dev
# - Connects to jumphost using SSH key
# - Connects to internal-dev using password (with key fallback)

Example 6: Different Auth Methods per Environment

# Production: SSH key only (most secure)
Host prod-db prod-web prod-app
    User prodadmin
    IdentityFile ~/.ssh/id_ed25519_prod
    PreferredAuthentications publickey
    PasswordAuthentication no
    PubkeyAuthentication yes

# Staging: Prefer key, allow password
Host staging-*
    User staging
    IdentityFile ~/.ssh/id_ed25519_staging
    PreferredAuthentications publickey,password

# Development: Password first (for testing)
Host dev-*
    User dev
    PreferredAuthentications password,publickey
    IdentityFile ~/.ssh/id_ed25519_dev

Example 7: Complete Multi-Environment Setup

# Corporate bastion (key only, very secure)
Host bastion
    HostName bastion.corp.example.com
    User myuser
    IdentityFile ~/.ssh/id_ed25519_corp
    PreferredAuthentications publickey
    PasswordAuthentication no
    PubkeyAuthentication yes
    ServerAliveInterval 60

# Production database (through bastion, key only)
Host prod-db
    HostName 10.0.1.10
    User dbadmin
    IdentityFile ~/.ssh/id_ed25519_corp
    PreferredAuthentications publickey
    ProxyJump bastion
    LocalForward 5432 localhost:5432

# Development server (key preferred, password allowed)
Host dev-server
    HostName dev.example.com
    User developer
    IdentityFile ~/.ssh/id_ed25519_dev
    PreferredAuthentications publickey,password,keyboard-interactive

# Personal VPS (key only)
Host vps
    HostName vps.example.com
    User admin
    Port 2222
    IdentityFile ~/.ssh/id_ed25519_personal
    PreferredAuthentications publickey
    PasswordAuthentication no

# Client server (they manage it, password only)
Host client-server
    HostName client.example.com
    User clientuser
    PreferredAuthentications password
    # Client doesn't support key auth

Example 8: Specific Key with No Other Methods

# Force using ONLY this specific key, no other attempts
Host critical-server
    HostName secure.example.com
    User secadmin
    IdentityFile ~/.ssh/id_ed25519_critical
    IdentitiesOnly yes
    # IdentitiesOnly prevents trying other keys from ssh-agent
    PreferredAuthentications publickey
    PasswordAuthentication no
    PubkeyAuthentication yes

# This is most secure for critical systems:
# - Only uses the specified key
# - Won't try other keys from ssh-agent
# - Won't fall back to password
# - Authentication fails if key doesn't work

Testing Authentication Methods

# Test which authentication method is being used
ssh -v user@hostname 2>&1 | grep -i "authentication"

# Force only publickey authentication
ssh -o PreferredAuthentications=publickey user@hostname

# Force only password authentication
ssh -o PreferredAuthentications=password user@hostname

# Try multiple methods in order
ssh -o PreferredAuthentications=publickey,password user@hostname

# Debug authentication
ssh -vvv user@hostname
# Look for lines like:
# debug1: Next authentication method: publickey
# debug1: Next authentication method: password

Server-Side Configuration

# /etc/ssh/sshd_config on the server

# Allow only publickey (most secure)
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no

# Allow both publickey and password (less secure)
PubkeyAuthentication yes
PasswordAuthentication yes
ChallengeResponseAuthentication no

# Allow only password (least secure, not recommended)
PubkeyAuthentication no
PasswordAuthentication yes

# Restart SSH after changes
sudo systemctl restart sshd

Best Practices for PreferredAuthentications

# 1. Production and Critical Systems
PreferredAuthentications publickey
PasswordAuthentication no
# Never allow password authentication

# 2. Development and Staging
PreferredAuthentications publickey,password
# Prefer key, allow password as fallback

# 3. Automated Scripts and CI/CD
PreferredAuthentications publickey
IdentitiesOnly yes
PasswordAuthentication no
# Only use specific key, no interactive auth

# 4. Personal Servers
PreferredAuthentications publickey
PasswordAuthentication no
# Use keys exclusively

# 5. Temporary Access
PreferredAuthentications password,publickey
# Allow password for initial setup, switch to keys later

Common Scenarios

# Scenario 1: Initial server setup, then switch to keys
# First connection (before key deployed)
Host new-server-initial
    HostName 192.168.1.100
    User root
    PreferredAuthentications password

# After deploying key (normal usage)
Host new-server
    HostName 192.168.1.100
    User admin
    IdentityFile ~/.ssh/id_ed25519
    PreferredAuthentications publickey
    PasswordAuthentication no

# Scenario 2: Database server through bastion
Host bastion-db
    HostName bastion.example.com
    User jumper
    IdentityFile ~/.ssh/id_bastion
    PreferredAuthentications publickey
    ForwardAgent no

Host db-prod
    HostName 10.0.1.5
    User dbadmin
    IdentityFile ~/.ssh/id_db
    PreferredAuthentications publickey
    ProxyJump bastion-db
    LocalForward 5432 localhost:5432

# Scenario 3: Multi-key setup for different roles
Host work-*
    User workuser
    IdentityFile ~/.ssh/id_work_admin
    IdentityFile ~/.ssh/id_work_dev
    PreferredAuthentications publickey
    IdentitiesOnly yes

# Scenario 4: Emergency access configuration
Host emergency-access
    HostName server.example.com
    User emergency
    PreferredAuthentications publickey,keyboard-interactive,password
    # Try everything in case of issues

Port Forwarding

Local Port Forwarding (Access Remote Service Locally)

# Syntax: ssh -L local_port:destination_host:destination_port user@ssh_server

# Example 1: Forward local port 5432 to remote PostgreSQL
ssh -L 5432:localhost:5432 user@remote-server
# Now: localhost:5432 connects to PostgreSQL on remote-server

# Example 2: Forward local port 3306 to remote MySQL
ssh -L 3306:localhost:3306 user@remote-server
# Now: localhost:3306 connects to MySQL on remote-server

# Example 3: Access MySQL through jump host
ssh -L 3306:db-server:3306 user@jumphost
# Now: localhost:3306 connects to MySQL on db-server via jumphost

# Example 4: Forward different local port
ssh -L 13306:localhost:3306 user@remote-server
# Now: localhost:13306 connects to remote MySQL on port 3306

# Example 5: Multiple port forwards
ssh -L 5432:localhost:5432 -L 3306:localhost:3306 user@remote-server

# Example 6: Run in background
ssh -f -N -L 5432:localhost:5432 user@remote-server
# -f: background
# -N: no command execution

PostgreSQL Port Forwarding

# Method 1: Command line
ssh -L 5432:localhost:5432 user@db-server

# Method 2: Through jump host
ssh -L 5432:db-server:5432 user@jumphost

# Method 3: SSH config
# Add to ~/.ssh/config:
Host db-tunnel
    HostName jumphost.example.com
    User myuser
    IdentityFile ~/.ssh/id_ed25519
    LocalForward 5432 db-server:5432

# Usage: ssh -N db-tunnel
# Then connect: psql -h localhost -p 5432 -U dbuser database_name

# Method 4: Persistent tunnel with autossh
autossh -M 0 -f -N -L 5432:localhost:5432 user@db-server

# Method 5: Multiple database servers
ssh -L 5433:db1:5432 -L 5434:db2:5432 user@jumphost
# db1 available on localhost:5433
# db2 available on localhost:5434

MySQL Port Forwarding

# Method 1: Command line
ssh -L 3306:localhost:3306 user@db-server

# Method 2: Through jump host
ssh -L 3306:mysql-server:3306 user@jumphost

# Method 3: SSH config
# Add to ~/.ssh/config:
Host mysql-tunnel
    HostName jumphost.example.com
    User myuser
    IdentityFile ~/.ssh/id_ed25519
    LocalForward 3306 mysql-server:3306

# Usage: ssh -N mysql-tunnel
# Then connect: mysql -h 127.0.0.1 -P 3306 -u dbuser -p

# Method 4: Use different local port (if MySQL running locally)
ssh -L 13306:localhost:3306 user@db-server
# Connect: mysql -h 127.0.0.1 -P 13306 -u dbuser -p

# Method 5: Multiple MySQL servers
ssh -L 3307:mysql1:3306 -L 3308:mysql2:3306 user@jumphost

Complete Database Access Setup

# ~/.ssh/config
Host bastion
    HostName bastion.company.com
    User admin
    IdentityFile ~/.ssh/id_ed25519_company
    ServerAliveInterval 60

Host postgres-prod
    HostName 10.0.1.10
    User dbadmin
    IdentityFile ~/.ssh/id_ed25519_company
    ProxyJump bastion
    LocalForward 15432 localhost:5432

Host mysql-prod
    HostName 10.0.1.20
    User dbadmin
    IdentityFile ~/.ssh/id_ed25519_company
    ProxyJump bastion
    LocalForward 13306 localhost:3306

# Usage:
# Terminal 1: Start PostgreSQL tunnel
ssh -N postgres-prod

# Terminal 2: Start MySQL tunnel
ssh -N mysql-prod

# Terminal 3: Connect to PostgreSQL
psql -h localhost -p 15432 -U postgres -d production

# Terminal 4: Connect to MySQL
mysql -h 127.0.0.1 -P 13306 -u root -p

Remote Port Forwarding

# Syntax: ssh -R remote_port:local_host:local_port user@remote_server

# Example: Expose local web server on remote server
ssh -R 8080:localhost:80 user@remote-server
# Remote server port 8080 forwards to local port 80

# Example: Share local database
ssh -R 5432:localhost:5432 user@remote-server

Dynamic Port Forwarding (SOCKS Proxy)

# Create SOCKS proxy on local port
ssh -D 8080 user@remote-server

# Configure browser to use SOCKS5 proxy:
# Host: localhost
# Port: 8080

# Command line usage with curl
curl --socks5 localhost:8080 http://example.com

# SSH config:
Host proxy
    HostName remote-server.com
    User myuser
    DynamicForward 8080

SSH Security Best Practices

Disable Password Authentication

# Edit SSH config
sudo nano /etc/ssh/sshd_config

# Set these values:
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no
PermitRootLogin no
PermitEmptyPasswords no

# Restart SSH
sudo systemctl restart sshd

Harden SSH Configuration

# /etc/ssh/sshd_config
Port 22                                    # Or change to non-standard port
Protocol 2
PermitRootLogin no
MaxAuthTries 3
MaxSessions 2
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM no
X11Forwarding no
PrintMotd no
ClientAliveInterval 300
ClientAliveCountMax 2
AllowUsers specificuser                   # Whitelist users
DenyUsers baduser                          # Blacklist users
AllowGroups sshusers                       # Whitelist groups

# Restart SSH after changes
sudo systemctl restart sshd

Firewall Configuration

# UFW (Ubuntu)
sudo ufw allow 22/tcp
sudo ufw enable

# Firewalld (RHEL/CentOS)
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload

# iptables
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Limit connection rate (prevent brute force)
sudo ufw limit 22/tcp

# Or with iptables
sudo iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
sudo iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP

Use fail2ban

# Install fail2ban
sudo apt install fail2ban

# Configure
sudo nano /etc/fail2ban/jail.local

# Add:
[sshd]
enabled = true
port = 22
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600

# Restart
sudo systemctl restart fail2ban

# Check status
sudo fail2ban-client status sshd

SSH Key Management

Key Permissions (Critical!)

# Proper permissions for SSH files:
chmod 700 ~/.ssh                    # Directory
chmod 600 ~/.ssh/id_ed25519        # Private key
chmod 644 ~/.ssh/id_ed25519.pub    # Public key
chmod 600 ~/.ssh/authorized_keys   # Authorized keys
chmod 600 ~/.ssh/config            # Config file
chmod 600 ~/.ssh/known_hosts       # Known hosts

# On server:
sudo chmod 700 /home/user/.ssh
sudo chmod 600 /home/user/.ssh/authorized_keys
sudo chown -R user:user /home/user/.ssh

Change SSH Key Passphrase

# Change passphrase
ssh-keygen -p -f ~/.ssh/id_ed25519

# Remove passphrase (NOT RECOMMENDED)
ssh-keygen -p -P "old_passphrase" -N "" -f ~/.ssh/id_ed25519

Backup SSH Keys

# Backup private keys (encrypted storage!)
cp -r ~/.ssh ~/backup/ssh-keys-backup

# Or create encrypted archive
tar -czf - ~/.ssh | gpg -c > ssh-keys-backup.tar.gz.gpg

# Restore
gpg -d ssh-keys-backup.tar.gz.gpg | tar -xzf -

Troubleshooting

Debug SSH Connection

# Verbose connection
ssh -vvv user@hostname

# Check SSH service status
sudo systemctl status sshd

# Check SSH is listening
sudo netstat -tlnp | grep :22
sudo ss -tlnp | grep :22

# Test SSH from same machine
ssh localhost

# Check SSH config syntax
sudo sshd -t

# View SSH logs
sudo tail -f /var/log/auth.log        # Ubuntu/Debian
sudo tail -f /var/log/secure          # RHEL/CentOS
sudo journalctl -u sshd -f            # systemd

Common Issues

# Permission denied (publickey)
# - Check key permissions (600 for private, 644 for public)
# - Verify public key in authorized_keys
# - Check ssh-agent has key loaded
# - Verify SSH config allows PubkeyAuthentication

# Connection timeout
# - Check firewall rules
# - Verify SSH service is running
# - Check correct IP/hostname
# - Verify network connectivity

# Host key verification failed
# - Remove old key: ssh-keygen -R hostname
# - Or edit ~/.ssh/known_hosts

# Too many authentication failures
# - Limit keys in ssh-agent
# - Use IdentitiesOnly yes in SSH config

# Agent forwarding not working
# - Use ssh -A
# - Add ForwardAgent yes to SSH config
# - Check ssh-agent is running and key is added

Useful SSH Commands

File Transfer

# SCP (Secure Copy)
scp file.txt user@remote:/path/
scp user@remote:/path/file.txt ./
scp -r directory user@remote:/path/

# SCP through jump host
scp -o ProxyJump=jumphost file.txt user@remote:/path/

# SFTP (Interactive)
sftp user@remote
# Commands: put, get, ls, cd, pwd, exit

# rsync over SSH
rsync -avz -e ssh /local/path/ user@remote:/remote/path/

SSH Tunneling

# Keep tunnel alive
ssh -N -L 5432:localhost:5432 user@remote

# Reconnect automatically (autossh)
autossh -M 0 -N -L 5432:localhost:5432 user@remote

# Background tunnel
ssh -f -N -L 5432:localhost:5432 user@remote

Connection Multiplexing

# SSH config:
Host *
    ControlMaster auto
    ControlPath ~/.ssh/control-%r@%h:%p
    ControlPersist 10m

# Benefits:
# - Reuse existing connection
# - Faster subsequent connections
# - Reduced authentication overhead

Quick Reference

# Generate key with passphrase
ssh-keygen -t ed25519 -C "[email protected]"

# Copy key to server
ssh-copy-id user@host

# Use ssh-agent
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

# Connect through jump host
ssh -J jumpuser@jumphost user@targethost

# Local port forward (database)
ssh -L 5432:localhost:5432 user@dbhost

# SSH config (~/.ssh/config)
Host myserver
    HostName 192.168.1.100
    User admin
    IdentityFile ~/.ssh/id_ed25519
    ProxyJump jumphost

# Disable password auth (/etc/ssh/sshd_config)
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin no

# Restart SSH
sudo systemctl restart sshd

# Fix permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/config

Best Practices Summary

  1. Always use SSH keys instead of passwords
  2. Always protect keys with passphrase (in case key is stolen)
  3. Use ssh-agent to avoid typing passphrase repeatedly
  4. Disable password authentication on servers
  5. Use ed25519 keys (modern, secure, fast)
  6. Set correct file permissions (700 for .ssh, 600 for keys)
  7. Use ~/.ssh/config to simplify connections
  8. Use ProxyJump for accessing servers behind bastion
  9. Use LocalForward for secure database access
  10. Never share private keys or store in version control
  11. Use different keys for different purposes/environments
  12. Backup keys securely (encrypted storage)
  13. Regularly rotate keys (especially for critical systems)
  14. Monitor SSH logs for suspicious activity
  15. Use fail2ban to prevent brute force attacks