A few months ago I came across a very brief how-to on using IPTables to implement portknocking. After I finished the project I found a more detailed how-to which explains the system in much greater detail.
I wrote up the original script with a few comments and now I source it from my main IPTables script. Warning: If you configure firewall scripts remotely be prepared for your own mistakes. When working with firewall scripts remotely it is a good idea to put in a cron job to flush the rules every 15 minutes so you don't lock yourself out. You can use iptables -F to flush all the rules. Note: There are some lines that are being auto-wrapped due to the narrowness of Blogger. Take care to edit them out if you cut and paste.
# Path to iptables
IPT=/sbin/iptables
# Port on which ssh listens
sshport=678
# Ports to knock to in order from 1 to 3
port1=14159
port2=26535
port3=8979
# Timeout
timeout=10
# Create new chains because we can't do two recent commands in one rule.
$IPT -N knock1
$IPT -N knock2
$IPT -N knock3
# Populate our custom chains.
# These chains basically just remove the host from the last table
# and add it to the next. This ensures each host must knock in
# order.
# Move from portknock1 to portknock2
$IPT -A knock1 -m recent --name portknock1 --remove
$IPT -A knock1 -m recent --name portknock2 --set -j DROP
# Move from portknock2 to portknock3
$IPT -A knock2 -m recent --name portknock2 --remove
$IPT -A knock2 -m recent --name portknock3 --set -j DROP
# Accept the packet and remove the entry from portknock3
$IPT -A knock3 -m recent --name portknock3 --remove -j ACCEPT
# These are the actual filter rules.
# This is the initial rule. Adds the host to portknock1
$IPT -A INPUT -m state --state NEW -p tcp --dport $port1 -m recent --name portknock1 --set -j DROP
# These two rules move from portknock1 to portknock2 and from
# portknock2 to portknock3 respectively when their associated
# port is knocked.
$IPT -A INPUT -m state --state NEW -p tcp --dport $port2 -m recent --rcheck --name portknock1 --seconds $timeout -j knock1
$IPT -A INPUT -m state --state NEW -p tcp --dport $port3 -m recent --rcheck --name portknock2 --seconds $timeout -j knock2
# This is the final check. When the packet is received to $sshport
# it must be in portknock3 to be accepted.
$IPT -A INPUT -m state --state NEW -p tcp --dport $sshport -m recent --rcheck --name portknock3 --seconds $timeout -j knock3
# These are anti-scanning rules. Though the best scanners (e.g.
# nmap) scan in a random order.
$IPT -A INPUT -m state --state NEW -m tcp -p tcp --dport $[port1 - 1] -m recent --name portknock3 --remove -j DROP
$IPT -A INPUT -m state --state NEW -m tcp -p tcp --dport $[port1 + 1] -m recent --name portknock3 --remove -j DROP
$IPT -A INPUT -m state --state NEW -m tcp -p tcp --dport $[port2 - 1] -m recent --name portknock2 --remove -j DROP
$IPT -A INPUT -m state --state NEW -m tcp -p tcp --dport $[port2 + 1] -m recent --name portknock2 --remove -j DROP
$IPT -A INPUT -m state --state NEW -m tcp -p tcp --dport $[port3 - 1] -m recent --name portknock1 --remove -j DROP
$IPT -A INPUT -m state --state NEW -m tcp -p tcp --dport $[port3 + 1] -m recent --name portknock1 --remove -j DROP
Once this is set up you will have to send a SYN packet (first part of a TCP connection) to $port1, $port2, and $port3 within $timeout seconds of each other. This will then open up $sshport for 10 seconds. It's your responsibility to have a daemon listening on that port.
Here's a quick script for connecting to an SSH port protected by the previous script.
#!/bin/sh
sshhost=myhost
sshport=678
port1=14159
port2=26535
port3=8979
for PORT in $port1 $port2 $port3
do
nc -w 1 -z $sshhost $PORT &
done
ssh -p $sshport $sshhost
No comments:
Post a Comment