Logo

dvanced SSH Hardening: Ed25519, 2FA and port knocking

dvanced SSH Hardening: Ed25519, 2FA and port knocking

Secure your SSH access beyond just changing the port. This guide covers Ed25519 keys (safer and faster than RSA), TOTP 2FA authentication, port knocking and advanced hardening best practices.

Advanced SSH Hardening: Ed25519, 2FA and port knocking

Secure your SSH access beyond just changing the port. This guide covers Ed25519 keys (safer and faster than RSA), TOTP 2FA authentication, port knocking and advanced hardening best practices.

Introduction

SSH is the main entry point to your VPS. A password, even a strong one, is no longer sufficient in 2025: constant brute-force bots, credential leaks and growing compute power make any password authentication obsolete.

This guide goes beyond just "change the port and disable root": we build SSH authentication with:

  1. Ed25519 keys (modern algorithm)
  2. 2FA via TOTP (Google Authenticator)
  3. Port knocking (SSH port only opens after secret sequence)
  4. IP limitation + Match groups

Prerequisites

  • Linux VPS with SSH installed
  • Root or sudo access
  • A second SSH session open to avoid locking yourself out during tests
  • A TOTP app (Google Authenticator, Authy, Bitwarden, etc.)

Step 1: Backup current SSH config

Crucial: before touching anything, backup:

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

And open a second SSH session in parallel, leave it open. If you lock yourself out, you keep access.

Step 2: Generate an Ed25519 key

Ed25519 replaces RSA: safer, faster, shorter keys. On your local machine (not the VPS):

ssh-keygen -t ed25519 -C "[email protected]"

Press Enter for the default path, and set a passphrase (strengthens private key security).

Two files are created:

  • ~/.ssh/id_ed25519 (private key — never share)
  • ~/.ssh/id_ed25519.pub (public key — to copy to server)

Step 3: Copy public key to VPS

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@VPS_IP

Or manually:

cat ~/.ssh/id_ed25519.pub | ssh user@VPS_IP "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

Test key login:

ssh -i ~/.ssh/id_ed25519 user@VPS_IP

You should log in without password (just local key passphrase).

Step 4: Disable password authentication

⚠️ Only do this once the key is tested AND with a second session open.

sudo nano /etc/ssh/sshd_config

Modify or add:

# Disable password
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no

# Disable root login
PermitRootLogin no

# Force protocol 2 (already default)
Protocol 2

# Modern algorithms only
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256
KexAlgorithms [email protected],curve25519-sha256,diffie-hellman-group16-sha512
Ciphers [email protected],[email protected],[email protected],aes256-ctr,aes192-ctr,aes128-ctr
MACs [email protected],[email protected],[email protected]

# Limits
MaxAuthTries 3
MaxSessions 5
LoginGraceTime 30

# Disable X11 (rarely needed on a server)
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no

# Logs
LogLevel VERBOSE

Test config:

sudo sshd -t

No error? Reload:

sudo systemctl reload sshd

Test from your second session or open a third to validate.

Port 22 receives 99% of brute-forces. Changing port kills 99% of noise (security by obscurity, doesn't replace hardening).

sudo nano /etc/ssh/sshd_config
Port 22022

Open the new port:

sudo ufw allow 22022/tcp

On Netrix (VeryCloud), also allow the custom port.

Reload:

sudo systemctl reload sshd

From now on connect with:

ssh -i ~/.ssh/id_ed25519 -p 22022 user@VPS_IP

To simplify, add in local ~/.ssh/config:

Host myvps
    HostName VPS_IP
    Port 22022
    User user
    IdentityFile ~/.ssh/id_ed25519

Connection: ssh myvps.

Step 6: Add TOTP 2FA

Installation:

sudo apt install -y libpam-google-authenticator

On the relevant user (not root):

google-authenticator

Answer:

  • Time-based tokens: y
  • Update ~/.google_authenticator: y
  • Disallow multiple uses: y
  • Increase time window: n
  • Rate limit: y

A QR code is displayed. Scan it with your TOTP app (Google Authenticator, Authy, Bitwarden).

Note the emergency backup codes displayed: if you lose your phone, they're your only way to reconnect.

Step 7: Configure PAM for SSH 2FA

sudo nano /etc/pam.d/sshd

At the top of the file (before @include common-auth):

auth required pam_google_authenticator.so

Then:

sudo nano /etc/ssh/sshd_config

Add/modify:

UsePAM yes
AuthenticationMethods publickey,keyboard-interactive
ChallengeResponseAuthentication yes
KbdInteractiveAuthentication yes

⚠️ AuthenticationMethods publickey,keyboard-interactive forces SSH key AND 2FA (both methods mandatory).

Reload:

sudo systemctl reload sshd

Test: connection now asks for key + TOTP code. Perfect.

Step 8: Port knocking (advanced)

Port knocking completely hides the SSH port: it's permanently closed and only opens for your IP after a "knock" sequence on predefined ports.

sudo apt install -y knockd

Configure:

sudo nano /etc/knockd.conf
[options]
    UseSyslog

[openSSH]
    sequence    = 7000,8000,9000
    seq_timeout = 5
    command     = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22022 -j ACCEPT
    tcpflags    = syn

[closeSSH]
    sequence    = 9000,8000,7000
    seq_timeout = 5
    command     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22022 -j ACCEPT
    tcpflags    = syn

Enable knockd:

sudo nano /etc/default/knockd
START_KNOCKD=1
sudo systemctl enable --now knockd

On firewall side, close the SSH port by default:

sudo ufw deny 22022/tcp

Now, to connect (from your local machine):

# Install knock client-side (Linux: apt install knockd, macOS: brew install knock)

# Knock
knock VPS_IP 7000 8000 9000

# Then SSH
ssh myvps

To close: knock VPS_IP 9000 8000 7000.

Step 9: Group and IP restrictions

Add to sshd_config:

# Only ssh-users group can connect
AllowGroups ssh-users

# Whitelist IP for a specific user
Match User admin
    AllowUsers [email protected]

Create the group:

sudo groupadd ssh-users
sudo usermod -aG ssh-users your_user

Step 10: Limit attempts per IP with ufw

UFW can limit SSH connections (complementary anti brute-force to Fail2ban / CrowdSec):

sudo ufw limit 22022/tcp

Limits to 6 connections per 30 seconds per IP. Beyond, temporary block.

Step 11: Audit the config

sudo apt install -y ssh-audit
ssh-audit localhost -p 22022

The tool checks all negotiated algorithms and gives a grade. Goal: all green.

Troubleshooting

Locked out of VPS

If you kept a session open, restore:

sudo cp /etc/ssh/sshd_config.bak /etc/ssh/sshd_config
sudo systemctl reload sshd

Otherwise, use noVNC from VeryCloud customer area (KVM console doesn't go through SSH).

"Permission denied (publickey)"

Check:

ls -la ~/.ssh/

Expected permissions:

drwx------ ~/.ssh
-rw------- ~/.ssh/authorized_keys
-rw------- ~/.ssh/id_ed25519
-rw-r--r-- ~/.ssh/id_ed25519.pub

If issue:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub

2FA blocks too often

If VPS clock is out of sync, TOTP code is invalid:

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

Useful commands

# Test SSH config
sudo sshd -t

# Reload sshd
sudo systemctl reload sshd

# View active sessions
who
w
last -10

# View failed attempts
sudo journalctl -u ssh | grep -i "failed\|invalid"

# SSH audit
ssh-audit localhost -p 22022

# Temporarily disable a key
mv ~/.ssh/authorized_keys ~/.ssh/authorized_keys.disabled

# View enrolled TOTP peers
ls -la /home/*/.google_authenticator

Conclusion

Your SSH is now locked down across multiple layers:

  1. ✅ Ed25519 key (modern crypto)
  2. ✅ Password disabled
  3. ✅ Non-standard port
  4. ✅ Mandatory TOTP 2FA
  5. ✅ Port knocking (port closed except after sequence)
  6. ✅ Group/IP restrictions
  7. ✅ UFW rate limiting

Even if an attacker finds the private key, they still need 2FA. Even with everything, the port is hidden by knocking.

Going further:

  • Combine with CrowdSec or Fail2ban (auto-ban malicious IPs)
  • Use a dedicated bastion if you have multiple VPS
  • Set up SSH certificate authorities for large-scale key rotation

Resources

Join our Discord community server

For any questions, suggestions, or just to chat with the community, join us on Discord!

900+Members