SSH port knocking with OpenBSD 7.9
SSH port knocking with OpenBSD 7.9
Port knocking is mostly a bad<br>idea. But people keep wanting to do it, for some false sense of security. If<br>you don't consider it a security control but a way to keep garbage out of your<br>logs, it might be valid. In my case I'm using an old USG Pro<br>4 running OpenBSD as my firewall and I'd<br>prefer to avoid writing stuff to the logs, as I'd prefer the flash not to wear<br>out sooner than needed, definitely not thanks to background radiation on the<br>internet.
Here is a pf.conf fragment using the OpenBSD<br>7.9 source<br>limiter<br>feature:
# Chosen by fair dice roll<br>knock1 = "24601"<br>knock2 = "29202"
# no need for knocking for these hosts<br>table persist {<br>192.0.2.0/24 # replace with whatever you trust
table persist {}<br>table persist {}<br>table persist {}
source limiter "stage1" id 1 entries 1000 \<br>limit 2 rate 10/100 \<br>table above 1
source limiter "stage2" id 2 entries 1000 \<br>limit 2 rate 10/100 \<br>table above 1
source limiter "bad" id 3 entries 10000 \<br>limit 2 rate 10/100 \<br>table above 1
# ssh port knocking<br>anchor to self {<br>pass in quick proto tcp from { } to port {>= 1024, 22}<br>block return-rst in quick proto tcp from<br>block return-rst in quick proto tcp to port 22<br>pass in quick proto tcp to port $knock1 source limiter "stage1" (no-match)<br>pass in quick proto tcp from to port $knock2 source limiter "stage2" (no-match)<br># source limiter needs a "pass" rule, ensure you have rules to block access<br># to ports >= 1024 you need to protect.<br>pass in proto tcp to port >= 1024 source limiter "bad" (no-match)<br>block return-rst proto tcp to port >= 1024
Once this was configured, I had no more ssh brute force attempts in the logs:
$ zgrep 'Jun 2' /var/log/* 2>/dev/null
Ah, peaceful 🧘♂️
Using return-rst means that it is harder to observe when the host has been<br>blocked, essentially turning the source limiter into a thing which does not<br>block anything but instead sets state.
Configuring the ssh client
To get into this you need to hit the source limiter twice, for each port. We<br>can use OpenSSH's Match tagged<br>keyword to make this nicer.
Add something like this to the end of ~/.ssh/config:
Match Final Tagged knock Exec "telnet %h 24601; telnet %h 24601; telnet %h 29202; telnet %h 29202; true"
Then use it with ssh -P knock your-host. You should see 4 connection refused<br>lines from telnet, then your SSH connection.
Alternatively rather than using -P knock, you can use the LocalNetwork<br>match to make this happen automatically depending which network you are on.
Match Final LocalNetwork !10.x.y.0/24 Host *.domain Exec "telnet %h 24601; telnet %h 24601; telnet %h 29202; telnet %h 29202; true"
(Unfortunately because of the ssh config parser that has to be on one line.)
The limits are arranged so a host is more likely to get blocked than<br>accidentally find the ports, even via scanning. However this shouldn't be<br>treated as a security control, it's mostly a way to stop clogging the logs with<br>scans, without having to run yet another daemon to do it.
18th June 2026