Port knocking secures services like SSH by dropping all standard requests and only allowing access after a specific sequence of packets (a "knock") is detected in userspace.
1. Configure Iptables
First, drop all incoming SSH requests on port 22, then redirect specific secret port traffic to NFQUEUE queue 1:
iptables -t filter -I INPUT -p tcp --dport 22 -j DROP
iptables -t raw -I PREROUTING -p tcp --sport 65534 --dport 65535 -j NFQUEUE --queue-num 1
2. Userspace Port Knocking Script
The following Python script monitors queue 1. When it detects a packet with the correct SOURCEPORT (65534) and SECRETPORT (65535), it dynamically inserts an iptables rule to allow that source IP to access SSH for a defined EXPIRETIME (default 30 minutes).
#!/usr/bin/python3
from os import system
from netfilterqueue import NetfilterQueue
from scapy.layers.inet import IP
from time import time
SOURCEPORT=65534
SECRETPORT=65535
EXPIRETIME=30
ALLOWED={}
def portknocking(pkt):
packet=IP(pkt.get_payload())
currtime=time()
for item in list(ALLOWED):
if(currtime-ALLOWED[item] >= EXPIRETIME*60):
del ALLOWED[item]
if(packet.sport==SOURCEPORT and packet.dport==SECRETPORT and packet.src not in ALLOWED):
print(f"Port {packet.dport} knocked by {packet.src}:{packet.sport}")
system(f"iptables -I INPUT -p tcp --dport 22 -s {packet.src} -j ACCEPT")
system(f"echo 'iptables -D INPUT -p tcp --dport 22 -s {packet.src} -j ACCEPT' | at now + {EXPIRETIME} minutes")
ALLOWED[packet.src]=time()
pkt.drop()
nfqueue=NetfilterQueue()
nfqueue.bind(1, portknocking)
try:
nfqueue.run()
except KeyboardInterrupt:
print("\nExit with Keyboard Interrupt")
3. Perform the Knock
To trigger the access from a client machine, use nc (netcat) to send a packet to the server's secret ports:
nc -p 65534 SERVER 65535