The Problem: SSH Port Scanners Find You Instantly
You’re running SSH on port 22. Within minutes of connecting to the internet, scanners find it and start trying default passwords. It’s relentless.
Most people deal with this by:
- Changing the port (security theater, but works)
- Failing logins (slow them down slightly)
- Disabling passwords (actually secure)
Port knocking is something different: hide the port entirely until you “knock” on it with a specific sequence. Then it opens. This isn’t security, but it’s defense-in-depth obscurity that stops automated scanners.
The trade-off: extra complexity for an annoying problem that probably has better solutions. Use this if you understand the limitations.
How Port Knocking Works
- Port 22 is closed (SYN drops)
- You send packets to ports 1234, 5678, 9999 (the “knock sequence”)
- A daemon (knockd) watches and sees your sequence
- It runs
iptablesto open port 22 only for your IP - You SSH in
- After a timeout, it closes the port again
From outside, it looks like the port doesn’t exist. Scanners never find it.
Install knockd
# Ubuntu/Debian:sudo apt install knockd
# RHEL/CentOS:sudo yum install knockd
# Verify:knockd --versionConfigure knockd
Edit /etc/knockd.conf:
[options] logfile = /var/log/knockd.log interface = eth0
# The knock sequence that opens SSH[openSSH] sequence = 1234,5678,9999 seq_timeout = 15 command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT tcpflags = syn
# Optional: close SSH after a timeout[closeSSH] sequence = 9999,8765,4321 seq_timeout = 15 command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT tcpflags = synWhat this does:
sequence: The port numbers to knock onseq_timeout: How many seconds between knocks before resettingcommand: What to run when sequence matches%IP%: Substituted with the source IP of the knockertcpflags = syn: Only match SYN packets (initial connection attempts)
Start knockd
# Enable at startup:sudo systemctl enable knockd
# Start it:sudo systemctl start knockd
# Verify it's running:sudo systemctl status knockdsudo journalctl -u knockd -n 20Create a Client Script
You need a tool that sends the knock sequence. Use knock (comes with knockd):
# Knock the sequence:knock example.com 1234 5678 9999
# Now you have a window (usually 60 seconds) to SSH:ssh user@example.com
# Knock the close sequence when done:knock example.com 9999 8765 4321Or manually with nmap:
nmap -p 1234,5678,9999 example.com --scanflags SYN -Pn
# Or with telnet (slower):(echo > /dev/tcp/example.com/1234) 2>/dev/null(echo > /dev/tcp/example.com/5678) 2>/dev/null(echo > /dev/tcp/example.com/9999) 2>/dev/null
# Now SSH:ssh user@example.comLock Down SSH While You’re At It
Even with port knocking, SSH should have these protections:
# /etc/ssh/sshd_config:Port 22PermitRootLogin noPasswordAuthentication noPubkeyAuthentication yesMaxAuthTries 3MaxSessions 2ClientAliveInterval 300Then restart SSH:
sudo systemctl restart sshFirewall Setup: Block SSH by Default
Make sure SSH is blocked by default and only accessible after knocking:
# Block SSH by default:sudo ufw default deny incomingsudo ufw allow 22/tcp # Allows SSH always (knockd will restrict further)
# Actually, better approach: don't allow 22 by defaultsudo ufw delete allow 22/tcpsudo ufw default deny incoming ssh
# Only open it via knockd:# (knockd will add the allow rule via iptables)Real Example: Automated Knock Client
Create a script that knocks and SSHes:
#!/bin/bashhost=$1user=${2:-$USER}
if [ -z "$host" ]; then echo "Usage: $0 <hostname> [user]" exit 1fi
echo "Knocking on $host..."knock $host 1234 5678 9999
echo "Waiting for port to open..."sleep 2
echo "Connecting..."ssh "$user@$host"
echo "Closing port..."knock $host 9999 8765 4321Usage:
chmod +x ssh-knock.sh./ssh-knock.sh example.com usernameWatch knockd in Action
# Terminal 1: Monitor knockd logssudo journalctl -u knockd -f
# Terminal 2: Knock and SSHknock example.com 1234 5678 9999ssh user@example.com
# Log shows:# Jan 10 12:34:56 server knockd: [1234/tcp] Knock from 192.168.1.100:54321# Jan 10 12:34:58 server knockd: [5678/tcp] Knock from 192.168.1.100:54322# Jan 10 12:34:59 server knockd: [9999/tcp] Knock from 192.168.1.100:54323# Jan 10 12:35:00 server knockd: 192.168.1.100: openSSH# Jan 10 12:35:01 server knockd: command: /sbin/iptables -A INPUT...Verify Port 22 Is Closed
From outside:
# Without knocking (port is closed):nmap -p 22 example.com# 22/tcp closed
# After knocking:knock example.com 1234 5678 9999nmap -p 22 example.com# 22/tcp open sshLimitations (Be Honest About This)
-
Not real security: This is obscurity, not authentication. Someone with a packet sniffer could see your knock sequence and reuse it.
-
Race conditions: If you knock from a different IP or multiple connections, timing could get weird.
-
Breaks legitimate SSH: Legitimate tools (monitoring, monitoring, scripts) can’t knock. You need to handle this.
-
Adds complexity: One more daemon to manage, one more potential bug surface.
Port knocking is valid for:
- Stopping automated scanners and bots
- Defense-in-depth (combined with key-only auth, fail2ban, etc.)
- Reducing noise in SSH logs
Don’t rely on it for:
- Protecting against actual attackers
- Replacing strong authentication
- Security against determined threats
Better Alternative (Probably)
Instead of port knocking, consider:
-
SSH on a non-standard port (1234 instead of 22):
- Stops 99% of bot attacks
- Way simpler than knockd
- Combined with key-only auth, that’s enough
-
VPN then SSH:
- SSH only accessible inside VPN
- Way more secure
-
Fail2ban + strong auth:
- Block after N failed attempts
- Combined with key-only auth
Port knocking is in that weird middle ground: more complex than changing the port, less secure than real solutions. Use it if it fits your threat model.
The Bottom Line
Port knocking is a legitimate defense-in-depth tactic. It stops automated scanners cold. The cost is extra complexity.
If you’re already running key-only SSH on a non-standard port and it’s still getting pounded by scanners, knockd is worth five minutes of setup. If you have a simpler solution, use that instead.
It’s obscurity, not security. But obscurity is a valid layer in defense-in-depth, as long as you don’t confuse it with real protection.