Skip to content
Go back

Port Knocking: Simple Obscurity for SSH Access

By SumGuy 6 min read
Port Knocking: Simple Obscurity for SSH Access

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:

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

  1. Port 22 is closed (SYN drops)
  2. You send packets to ports 1234, 5678, 9999 (the “knock sequence”)
  3. A daemon (knockd) watches and sees your sequence
  4. It runs iptables to open port 22 only for your IP
  5. You SSH in
  6. After a timeout, it closes the port again

From outside, it looks like the port doesn’t exist. Scanners never find it.

Install knockd

Terminal window
# Ubuntu/Debian:
sudo apt install knockd
# RHEL/CentOS:
sudo yum install knockd
# Verify:
knockd --version

Configure 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 = syn

What this does:

Start knockd

Terminal window
# Enable at startup:
sudo systemctl enable knockd
# Start it:
sudo systemctl start knockd
# Verify it's running:
sudo systemctl status knockd
sudo journalctl -u knockd -n 20

Create a Client Script

You need a tool that sends the knock sequence. Use knock (comes with knockd):

Terminal window
# 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 4321

Or manually with nmap:

Terminal window
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.com

Lock Down SSH While You’re At It

Even with port knocking, SSH should have these protections:

Terminal window
# /etc/ssh/sshd_config:
Port 22
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
MaxSessions 2
ClientAliveInterval 300

Then restart SSH:

Terminal window
sudo systemctl restart ssh

Firewall Setup: Block SSH by Default

Make sure SSH is blocked by default and only accessible after knocking:

Terminal window
# Block SSH by default:
sudo ufw default deny incoming
sudo ufw allow 22/tcp # Allows SSH always (knockd will restrict further)
# Actually, better approach: don't allow 22 by default
sudo ufw delete allow 22/tcp
sudo 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:

ssh-knock.sh
#!/bin/bash
host=$1
user=${2:-$USER}
if [ -z "$host" ]; then
echo "Usage: $0 <hostname> [user]"
exit 1
fi
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 4321

Usage:

Terminal window
chmod +x ssh-knock.sh
./ssh-knock.sh example.com username

Watch knockd in Action

Terminal window
# Terminal 1: Monitor knockd logs
sudo journalctl -u knockd -f
# Terminal 2: Knock and SSH
knock example.com 1234 5678 9999
ssh 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:

Terminal window
# Without knocking (port is closed):
nmap -p 22 example.com
# 22/tcp closed
# After knocking:
knock example.com 1234 5678 9999
nmap -p 22 example.com
# 22/tcp open ssh

Limitations (Be Honest About This)

  1. Not real security: This is obscurity, not authentication. Someone with a packet sniffer could see your knock sequence and reuse it.

  2. Race conditions: If you knock from a different IP or multiple connections, timing could get weird.

  3. Breaks legitimate SSH: Legitimate tools (monitoring, monitoring, scripts) can’t knock. You need to handle this.

  4. Adds complexity: One more daemon to manage, one more potential bug surface.

Port knocking is valid for:

Don’t rely on it for:

Better Alternative (Probably)

Instead of port knocking, consider:

  1. 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
  2. VPN then SSH:

    • SSH only accessible inside VPN
    • Way more secure
  3. 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.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it may appear here.


Previous Post
VLAN Basics for Home Labs: Segment Your Network Before It Segments You
Next Post
Building CLI Tools in Go: Because Shell Scripts Have a Maximum Complexity

Related Posts