This guide covers essential SSH hardening. Two changes eliminate most attack vectors.
Required Steps
Generate SSH Keys
On your local machine:
ssh-keygen -t ed25519 -C "your-email@domain.com" ssh-copy-id user@server-ip
Configure SSH
Edit /etc/ssh/sshd_config
:
PubkeyAuthentication yes PasswordAuthentication no
Restart SSH:
systemctl restart sshd
Configure Firewall
# Reset firewall sudo ufw --force reset # Allow SSH from VPN network only sudo ufw allow from 10.0.0.0/24 to any port 22 # Enable firewall sudo ufw enable
Why This Works
- No passwords means no brute force attacks
- WireGuard VPN means zero SSH exposure to internet
- Two security layers must both fail for compromise
Infrastructure Setup
You → WireGuard VPN → Internal Servers
Your servers have NO SSH ports exposed to the internet. Access requires:
- Your WireGuard private key
- Your SSH private key
WireGuard Setup
On Jump Host/VPN Server
# Install WireGuard sudo apt install wireguard # Generate server keys wg genkey | tee privatekey | wg pubkey > publickey # Configure /etc/wireguard/wg0.conf [Interface] PrivateKey = <server-private-key> Address = 10.0.0.1/24 ListenPort = 51820 [Peer] PublicKey = <client-public-key> AllowedIPs = 10.0.0.2/32
Enable and start:
sudo systemctl enable wg-quick@wg0 sudo systemctl start wg-quick@wg0
On Internal Servers
# Only allow SSH from VPN network sudo ufw allow from 10.0.0.0/24 to any port 22
That's it. SSH is now only accessible through VPN.
Client Configuration
Save as wg0.conf
:
[Interface] PrivateKey = <your-private-key> Address = 10.0.0.2/24 DNS = 10.0.0.1 [Peer] PublicKey = <server-public-key> Endpoint = your-vpn-server.com:51820 AllowedIPs = 10.0.0.0/24, 192.168.0.0/24 PersistentKeepalive = 25
Connect:
sudo wg-quick up ./wg0.conf
Multiple Services Example
# SSH from VPN network only sudo ufw allow from 10.0.0.0/24 to any port 22 # Web traffic from anywhere sudo ufw allow 80 sudo ufw allow 443 # Database from internal network only sudo ufw allow from 192.168.0.0/24 to any port 3306
Checking Status
# View rules sudo ufw status numbered # View with details sudo ufw status verbose
Expected output:
Status: active To Action From -- ------ ---- 22 ALLOW 10.0.0.0/24
Testing
From your machine (with VPN connected):
ssh user@10.0.0.10 # Internal server IP
Without VPN (should fail):
ssh user@public-server-ip # Times out - port not even open
Check logs:
# Connection attempts sudo tail -f /var/log/auth.log # Successful logins sudo grep "Accepted publickey" /var/log/auth.log
Common Commands
# Add rule sudo ufw allow from IP to any port PORT # Delete rule sudo ufw delete 1 # Reset sudo ufw --force reset # Disable temporarily sudo ufw disable
Common Mistakes
- Leaving password authentication enabled
- Not testing before applying changes
- No firewall rules - SSH open to internet
- Allowing root login
Backup Access
Before applying changes, ensure you have:
- Console access through hosting provider
- Second SSH key stored separately
- Management interface access
Test backup access first.