The Complete SSH Guide: Keys, Config, Tunnels, and Security

7 min read
Intermediate SSH Security Linux Networking Guide

Prerequisites

  • Terminal access (Linux, Mac, or Windows with OpenSSH)
  • A remote server to connect to

Quick Answer: ssh user@server connects to a server. ssh-keygen -t ed25519 generates a key. ssh-copy-id user@server sets up passwordless login. ssh -L 8080:localhost:80 user@server creates a local tunnel. ssh -D 1080 user@server creates a SOCKS proxy.

SSH (Secure Shell) is the standard way to remotely manage Linux servers. It encrypts all traffic between your machine and the server — commands, file transfers, and even forwarded ports are all secured.

This guide covers everything from basic connections to advanced tunneling and security.


Part 1: Basic Connection

# Connect to a server
ssh [email protected]
ssh [email protected]

# Connect on a different port
ssh -p 2222 user@server

# Run a single command (doesn't open shell)
ssh user@server "uptime"
ssh user@server "df -h && free -m"

Part 2: SSH Keys

Passwords are slow and insecure. SSH keys are faster, stronger, and enable automation.

Generate a Key

# Ed25519 (recommended — modern, fast, secure)
ssh-keygen -t ed25519 -C "[email protected]"

# RSA (for older servers that don't support Ed25519)
ssh-keygen -t rsa -b 4096 -C "[email protected]"

This creates:

  • ~/.ssh/id_ed25519Private key (never share this)
  • ~/.ssh/id_ed25519.pubPublic key (copy to servers)

Copy Key to Server

# Automatic (easiest)
ssh-copy-id user@server

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

# From Windows PowerShell
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

Test

ssh user@server
# Should connect without asking for password

Key Permissions

SSH refuses to use keys with loose permissions:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519          # Private key
chmod 644 ~/.ssh/id_ed25519.pub      # Public key
chmod 600 ~/.ssh/authorized_keys     # On the server
chmod 600 ~/.ssh/config              # Config file

Full guide: How to Set Up SSH Keys


Part 3: SSH Config File

The config file saves connection details so you don't type them every time.

Create or edit ~/.ssh/config:

# Simple alias
Host myserver
    HostName 203.0.113.50
    User sam
    Port 2222

# Now just type: ssh myserver

# Different key per server
Host work
    HostName work.example.com
    User admin
    IdentityFile ~/.ssh/id_work

Host personal
    HostName 10.0.0.5
    User sam
    IdentityFile ~/.ssh/id_personal

# Wildcard defaults
Host *
    ServerAliveInterval 30
    ServerAliveCountMax 3
    AddKeysToAgent yes

Useful Config Options

Option What It Does Example
HostName Server IP or domain 203.0.113.50
User Login username sam
Port SSH port 2222
IdentityFile Which key to use ~/.ssh/id_work
ServerAliveInterval Send keepalive every N seconds 30
ServerAliveCountMax Disconnect after N missed keepalives 3
ProxyJump Jump through another server bastion
LocalForward Auto-create tunnel on connect 8080 localhost:80
DynamicForward Auto-create SOCKS proxy 1080
ForwardAgent Forward SSH agent to server yes
Compression Compress data (slow connections) yes

Full guide: SSH Config Guide


Part 4: SSH Tunnels

SSH tunnels encrypt traffic between ports on your local machine and a remote server.

Local Port Forwarding (-L)

Access a remote service as if it were local.

# Forward local port 8080 to remote port 80
ssh -L 8080:localhost:80 user@server

# Now open http://localhost:8080 — it goes to server:80

Use case: Access a database or web panel on a server that only listens on localhost.

# Access remote PostgreSQL
ssh -L 5433:localhost:5432 user@dbserver
# Connect to localhost:5433 — goes to server's PostgreSQL

# Access remote web admin panel
ssh -L 9090:localhost:9090 user@server
# Open http://localhost:9090 in your browser

Remote Port Forwarding (-R)

Make your local machine accessible from the server.

# Expose local port 3000 on the server as port 8080
ssh -R 8080:localhost:3000 user@server

# Someone on the server can now access your app at localhost:8080

Use case: Show a local development site to someone on the server.

Dynamic Port Forwarding (-D) — SOCKS Proxy

Create a SOCKS5 proxy through the SSH connection.

ssh -D 1080 -N -f user@server
  • -D 1080 — SOCKS proxy on localhost:1080
  • -N — Don't execute commands (tunnel only)
  • -f — Run in background

Configure your browser to use SOCKS5 proxy at 127.0.0.1:1080. All browsing goes through the server.

Full guide: SOCKS5 Proxy Setup

Keep Tunnels Alive

# autossh reconnects if the connection drops
sudo apt install autossh

autossh -M 0 -D 1080 -N -f user@server \
  -o "ServerAliveInterval=30" \
  -o "ServerAliveCountMax=3"

Or use tunnelforge for managed SSH tunnels with TUI, TLS obfuscation, and Telegram alerts.


Part 5: File Transfer

SCP (Simple Copy)

# Upload file
scp file.txt user@server:/remote/path/

# Download file
scp user@server:/remote/path/file.txt ./local/

# Upload directory
scp -r mydir/ user@server:/remote/path/

# With custom port
scp -P 2222 file.txt user@server:/path/

rsync (Better for Large Transfers)

# Sync directory (only copies changes)
rsync -avz local-dir/ user@server:/remote-dir/

# With progress
rsync -avz --progress local-dir/ user@server:/remote-dir/

# Delete files on remote that don't exist locally
rsync -avz --delete local-dir/ user@server:/remote-dir/

# With custom port
rsync -avz -e "ssh -p 2222" local-dir/ user@server:/remote-dir/

# Dry run (see what would change)
rsync -avzn local-dir/ user@server:/remote-dir/

SFTP (Interactive)

sftp user@server

# Commands inside SFTP:
ls                   # List remote files
lls                  # List local files
cd /path             # Change remote directory
lcd /path            # Change local directory
get file.txt         # Download
put file.txt         # Upload
get -r dir/          # Download directory
put -r dir/          # Upload directory
exit

Part 6: Jump Hosts (ProxyJump)

Access a server through an intermediate bastion/jump host.

Your machine → Bastion (public) → Internal Server (private)
# One command
ssh -J bastion-user@bastion internal-user@internal-server

# In SSH config (cleaner)
Host bastion
    HostName 203.0.113.50
    User admin

Host internal
    HostName 10.0.0.5
    User sam
    ProxyJump bastion

# Now just: ssh internal

Multi-Hop

# Through two jump hosts
ssh -J host1,host2 final-server

# In config
Host final
    HostName 10.0.0.100
    ProxyJump host1,host2

Part 7: SSH Agent

The SSH agent holds your decrypted keys in memory so you don't type passphrases repeatedly.

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

# Add your key
ssh-add ~/.ssh/id_ed25519

# List loaded keys
ssh-add -l

# Remove all keys from agent
ssh-add -D

Agent Forwarding

Use your local keys on a remote server (for git pull on the server without copying keys):

ssh -A user@server

# Or in config:
Host server
    ForwardAgent yes

Security warning: Only use agent forwarding to servers you trust. A compromised server could use your forwarded agent to access other servers.


Part 8: Security Hardening

Essential Changes (/etc/ssh/sshd_config)

Port 2222                        # Change from default 22
PermitRootLogin no               # Never SSH as root
PasswordAuthentication no        # Keys only
MaxAuthTries 3                   # Limit login attempts
ClientAliveInterval 300          # Send keepalive every 5 min (disconnect after 10 min with CountMax 2)
ClientAliveCountMax 2            # After 2 missed keepalives
AllowUsers sam admin             # Only these users can SSH
X11Forwarding no                 # Disable if not needed
sudo sshd -t                    # Test config
sudo systemctl restart sshd     # Apply

Install Fail2ban

sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban

Automatically bans IPs after failed login attempts.

Full guides:


Part 9: Troubleshooting

Debug Connection

ssh -v user@server               # Verbose
ssh -vv user@server              # More verbose
ssh -vvv user@server             # Maximum debug output

Common Problems

Problem Fix
Permission denied (publickey) Key not on server. Run ssh-copy-id user@server
Connection refused SSH not running or wrong port. Check `ss -tlnp \ grep ssh` on server
Connection timed out Firewall blocking. Check ufw status or cloud security groups
Host key verification failed Server was reinstalled. Remove old key: ssh-keygen -R server-ip
Key too open chmod 600 ~/.ssh/id_ed25519
Agent has no identities ssh-add ~/.ssh/id_ed25519
Broken pipe / disconnect Add ServerAliveInterval 30 to SSH config

Check What's Listening

# On the server
sudo ss -tlnp | grep ssh
# Should show sshd on port 22 (or custom port)

Part 10: Useful One-Liners

# Mount remote directory locally (SSHFS)
sudo apt install sshfs
sshfs user@server:/remote/path /local/mount

# Run local script on remote server
ssh user@server 'bash -s' < local-script.sh

# Copy SSH public key to clipboard (Mac)
pbcopy < ~/.ssh/id_ed25519.pub

# Copy SSH public key to clipboard (Linux)
xclip -sel clip < ~/.ssh/id_ed25519.pub

# Generate a key without prompts (for automation)
ssh-keygen -t ed25519 -f ~/.ssh/id_auto -N "" -q

# Test SSH connection without logging in
ssh -o ConnectTimeout=5 -o BatchMode=yes user@server echo ok

Related Guides on SamNet

SSH Setup:

Security:

Tunnels & Proxy:

Tools: