Skip to content

SSH (Secure Shell)

A comprehensive guide to SSH - secure network protocol for remote system access and management.

Table of Contents

Introduction

What is SSH?

SSH (Secure Shell) is a cryptographic network protocol for secure data communication, remote command execution, and network services over an unsecured network.

Features

  • Encrypted Communication: All data encrypted
  • Authentication: Password and public key authentication
  • Port Forwarding: Tunnel other protocols through SSH
  • File Transfer: SCP, SFTP
  • X11 Forwarding: Run GUI applications remotely
  • Agent Forwarding: Use local keys on remote servers

Installation

Linux

# Ubuntu/Debian
sudo apt update
sudo apt install openssh-client openssh-server

# RHEL/CentOS/Fedora
sudo dnf install openssh-clients openssh-server

# Enable and start SSH server
sudo systemctl enable sshd
sudo systemctl start sshd

# Check status
sudo systemctl status sshd

macOS

# SSH client pre-installed

# Enable SSH server
sudo systemsetup -setremotelogin on

# Disable SSH server
sudo systemsetup -setremotelogin off

Windows

# Windows 10/11 (OpenSSH Client)
# Add-WindowsCapability -Online -Name OpenSSH.Client

# OpenSSH Server
Add-WindowsCapability -Online -Name OpenSSH.Server
Start-Service sshd
Set-Service -Name sshd -StartupType 'Automatic'

Basic Usage

Connect to Remote Host

# Basic connection
ssh username@hostname

# Specify port
ssh -p 2222 username@hostname

# Specify identity file
ssh -i ~/.ssh/id_rsa username@hostname

# Run command remotely
ssh username@hostname 'ls -la'

# Interactive shell with command
ssh -t username@hostname 'sudo command'

# Verbose output (debugging)
ssh -v username@hostname
ssh -vv username@hostname  # More verbose
ssh -vvv username@hostname # Maximum verbosity

# Disconnect (type in SSH session)
~.

First Connection

# First time connecting shows fingerprint
The authenticity of host 'example.com (192.168.1.100)' can't be established.
ED25519 key fingerprint is SHA256:xxxxx.
Are you sure you want to continue connecting (yes/no)?

# Verify fingerprint on server
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub

SSH Keys

Generate SSH Key Pair

# RSA key (2048-bit)
ssh-keygen -t rsa -b 2048

# RSA key (4096-bit)
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

# Ed25519 key (recommended)
ssh-keygen -t ed25519 -C "your_email@example.com"

# ECDSA key
ssh-keygen -t ecdsa -b 521

# Specify file location
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_custom

# Generate without passphrase (not recommended)
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519

Copy Public Key to Server

# Using ssh-copy-id
ssh-copy-id username@hostname

# Specify key file
ssh-copy-id -i ~/.ssh/id_ed25519.pub username@hostname

# Specify port
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 username@hostname

# Manual copy
cat ~/.ssh/id_ed25519.pub | ssh username@hostname 'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys'

# Set permissions
ssh username@hostname 'chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys'

Manage SSH Keys

# List keys in ssh-agent
ssh-add -l

# Add key to ssh-agent
ssh-add ~/.ssh/id_ed25519

# Add with timeout (1 hour)
ssh-add -t 3600 ~/.ssh/id_ed25519

# Remove key from agent
ssh-add -d ~/.ssh/id_ed25519

# Remove all keys
ssh-add -D

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

# Show public key from private key
ssh-keygen -y -f ~/.ssh/id_ed25519

# Convert key formats
ssh-keygen -i -f old_key > new_key  # Import
ssh-keygen -e -f key > key.pem      # Export

Configuration

Client Configuration (~/.ssh/config)

# Host-specific configuration
Host myserver
    HostName 192.168.1.100
    User admin
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

# Wildcard matching
Host *.example.com
    User deployment
    IdentityFile ~/.ssh/deploy_key

# Default settings
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    Compression yes
    ForwardAgent no
    AddKeysToAgent yes

# Multiple hosts
Host server1 server2 server3
    User admin
    IdentityFile ~/.ssh/admin_key

# Use configuration
ssh myserver  # Connects using config above

Common Configuration Options

# ~/.ssh/config options

Host myhost
    # Connection
    HostName example.com
    Port 22
    User username

    # Authentication
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes
    PubkeyAuthentication yes
    PasswordAuthentication no

    # Connection management
    ServerAliveInterval 60
    ServerAliveCountMax 3
    TCPKeepAlive yes

    # Forwarding
    ForwardAgent yes
    ForwardX11 yes
    LocalForward 8080 localhost:80
    RemoteForward 9090 localhost:3000
    DynamicForward 1080

    # Proxy/Jump
    ProxyJump bastion
    ProxyCommand ssh -W %h:%p bastion

    # Ciphers and algorithms
    Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
    MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
    KexAlgorithms curve25519-sha256,diffie-hellman-group-exchange-sha256

    # Misc
    Compression yes
    LogLevel INFO
    StrictHostKeyChecking ask
    UserKnownHostsFile ~/.ssh/known_hosts

Server Configuration (/etc/ssh/sshd_config)

# Basic settings
Port 22
ListenAddress 0.0.0.0
Protocol 2

# Authentication
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM yes

# Key files
AuthorizedKeysFile .ssh/authorized_keys
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key

# Security
MaxAuthTries 3
MaxSessions 10
LoginGraceTime 60
ClientAliveInterval 300
ClientAliveCountMax 2

# Forwarding
AllowTcpForwarding yes
X11Forwarding yes
AllowAgentForwarding yes

# Logging
SyslogFacility AUTH
LogLevel VERBOSE

# Subsystems
Subsystem sftp /usr/lib/openssh/sftp-server

# Restart after changes
sudo systemctl restart sshd

Port Forwarding

Local Port Forwarding

# Forward local port to remote
ssh -L local_port:remote_host:remote_port username@ssh_server

# Example: Access remote database
ssh -L 5432:localhost:5432 username@db-server
# Connect to localhost:5432 locally

# Multiple forwards
ssh -L 8080:web:80 -L 5432:db:5432 username@server

# Keep connection open
ssh -N -L 8080:localhost:80 username@server

Remote Port Forwarding

# Forward remote port to local
ssh -R remote_port:local_host:local_port username@ssh_server

# Example: Share local web server
ssh -R 8080:localhost:3000 username@remote-server
# Access via remote-server:8080

# Bind to all interfaces on remote
ssh -R 0.0.0.0:8080:localhost:3000 username@server

Dynamic Port Forwarding (SOCKS Proxy)

# Create SOCKS proxy
ssh -D 1080 username@server

# Configure browser to use SOCKS proxy:
# - SOCKS Host: localhost
# - Port: 1080
# - SOCKS v5

# With autossh (auto-reconnect)
autossh -M 0 -D 1080 -N username@server

ProxyJump & Bastion

ProxyJump (SSH Jump Host)

# Jump through bastion
ssh -J bastion-user@bastion.example.com target-user@target-server

# Multiple jumps
ssh -J jump1,jump2,jump3 username@target

# In config file
Host target
    HostName target-server
    User target-user
    ProxyJump bastion-user@bastion.example.com

# Then simply:
ssh target

ProxyCommand

# Using ProxyCommand
Host target
    HostName 10.0.1.100
    User admin
    ProxyCommand ssh -W %h:%p bastion

# With netcat
Host target
    ProxyCommand ssh bastion nc %h %p

# Through SOCKS proxy
Host target
    ProxyCommand nc -X 5 -x localhost:1080 %h %p

File Transfer

SCP (Secure Copy)

# Copy file to remote
scp file.txt username@server:/remote/path/

# Copy from remote
scp username@server:/remote/file.txt /local/path/

# Copy directory recursively
scp -r directory/ username@server:/remote/path/

# Specify port
scp -P 2222 file.txt username@server:/path/

# Preserve attributes
scp -p file.txt username@server:/path/

# Limit bandwidth (KB/s)
scp -l 1024 file.txt username@server:/path/

# Through jump host
scp -J bastion file.txt username@target:/path/

# Copy between remote hosts
scp user1@host1:/file user2@host2:/path/

SFTP (SSH File Transfer Protocol)

# Connect to SFTP server
sftp username@hostname

# SFTP commands
sftp> ls                 # List remote files
sftp> lls                # List local files
sftp> cd /remote/path    # Change remote directory
sftp> lcd /local/path    # Change local directory
sftp> get file.txt       # Download file
sftp> get -r directory/  # Download directory
sftp> put file.txt       # Upload file
sftp> put -r directory/  # Upload directory
sftp> mkdir newdir       # Create remote directory
sftp> rm file.txt        # Delete remote file
sftp> rmdir directory    # Remove remote directory
sftp> pwd                # Print remote working directory
sftp> lpwd               # Print local working directory
sftp> quit               # Exit

# Batch mode
sftp -b commands.txt username@server

# commands.txt:
cd /remote/path
get file1.txt
put file2.txt
quit

rsync over SSH

# Sync directory to remote
rsync -avz -e ssh /local/path/ username@server:/remote/path/

# Sync from remote
rsync -avz -e ssh username@server:/remote/path/ /local/path/

# Delete files on destination that don't exist on source
rsync -avz --delete -e ssh /local/path/ username@server:/remote/path/

# Show progress
rsync -avz --progress -e ssh /local/path/ username@server:/remote/path/

# Dry run
rsync -avz --dry-run -e ssh /local/path/ username@server:/remote/path/

# Specify port
rsync -avz -e "ssh -p 2222" /local/path/ username@server:/remote/path/

# Through jump host
rsync -avz -e "ssh -J bastion" /local/path/ username@target:/remote/path/

Security

SSH Hardening

# /etc/ssh/sshd_config

# Disable root login
PermitRootLogin no

# Disable password authentication
PasswordAuthentication no
PubkeyAuthentication yes

# Limit users
AllowUsers alice bob
DenyUsers eve

# Limit groups
AllowGroups sshusers

# Change default port
Port 2222

# Disable X11 forwarding (if not needed)
X11Forwarding no

# Disable agent forwarding
AllowAgentForwarding no

# Use strong ciphers only
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
KexAlgorithms curve25519-sha256,diffie-hellman-group-exchange-sha256

# Rate limiting
MaxAuthTries 3
MaxStartups 10:30:60

# Idle timeout
ClientAliveInterval 300
ClientAliveCountMax 2

Fail2Ban

# Install fail2ban
sudo apt install fail2ban

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

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

# Restart fail2ban
sudo systemctl restart fail2ban

# Check status
sudo fail2ban-client status sshd

Two-Factor Authentication

# Install Google Authenticator
sudo apt install libpam-google-authenticator

# Configure for user
google-authenticator

# Edit /etc/pam.d/sshd
auth required pam_google_authenticator.so

# Edit /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive

# Restart SSH
sudo systemctl restart sshd

Troubleshooting

Common Issues

# Permission denied (publickey)
# Fix permissions:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_*
chmod 644 ~/.ssh/id_*.pub

# Connection timeout
# Check firewall
sudo ufw status
sudo ufw allow 22/tcp

# Check SSH service
sudo systemctl status sshd

# Check listening ports
sudo netstat -tulpn | grep ssh

# Too many authentication failures
# Limit keys tried
ssh -o IdentitiesOnly=yes -i ~/.ssh/specific_key user@host

# Host key verification failed
# Remove old host key
ssh-keygen -R hostname

# Or edit known_hosts
nano ~/.ssh/known_hosts

# Debug connection
ssh -vvv username@hostname

# Check server logs
sudo tail -f /var/log/auth.log  # Debian/Ubuntu
sudo tail -f /var/log/secure    # RHEL/CentOS

Performance Issues

# Disable DNS lookup (server-side)
UseDNS no

# Enable compression
Compression yes

# Use faster cipher
Ciphers chacha20-poly1305@openssh.com

# Disable GSSAPI
GSSAPIAuthentication no

# Reuse connections
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600

Additional Resources


Last updated: 2025-11-16