2012-02-10

Bash script for simple port-knocking in iptables

This is a simple bash script that allows you to add (multiple) port-knocking rules into iptables. Usage is explained below.
KNOCK_NAME=$1

PORT_1=$2
PORT_2=$3
PORT_3=$4
PORT_4=$5

OPEN_PORT=$6
OPEN_TIME=$7




PHASE_1=p1_$KNOCK_NAME
PHASE_2=p2_$KNOCK_NAME
PHASE_3=p3_$KNOCK_NAME
PHASE_4=p4_$KNOCK_NAME

JUMP_TO_2=j2_$KNOCK_NAME
JUMP_TO_3=j3_$KNOCK_NAME
JUMP_TO_4=j4_$KNOCK_NAME


iptables -X $JUMP_TO_2
iptables -N $JUMP_TO_2
iptables -A $JUMP_TO_2 -m recent --name $PHASE_1 --remove
iptables -A $JUMP_TO_2 -m recent --name $PHASE_2 --set

iptables -X $JUMP_TO_3
iptables -N $JUMP_TO_3
iptables -A $JUMP_TO_3 -m recent --name $PHASE_2 --remove
iptables -A $JUMP_TO_3 -m recent --name $PHASE_3 --set

iptables -X $JUMP_TO_4
iptables -N $JUMP_TO_4
iptables -A $JUMP_TO_4 -m recent --name $PHASE_3 --remove
iptables -A $JUMP_TO_4 -m recent --name $PHASE_4 --set


iptables -A INPUT                        -m recent --update --name $PHASE_1
iptables -A INPUT -p udp --dport $PORT_1 -m recent --set    --name $PHASE_1
iptables -A INPUT -p udp --dport $PORT_2 -m recent --rcheck --name $PHASE_1 -j $JUMP_TO_2
iptables -A INPUT -p udp --dport $PORT_3 -m recent --rcheck --name $PHASE_2 -j $JUMP_TO_3
iptables -A INPUT -p udp --dport $PORT_4 -m recent --rcheck --name $PHASE_3 -j $JUMP_TO_4

iptables -A INPUT -p tcp --dport $OPEN_PORT -m recent --rcheck --seconds $OPEN_TIME --name $PHASE_4 -j ACCEPT
(credit: http://www.debian-administration.org/articles/268)

In this example I'm actually using UDP ports, because they are easier to knock on: no attempt is made to actually connect and (thus) timeout. If you prefer TCP knocking, and you don't know how to adjust the script, click here.


# setup all rules you currently have
... (important stuff!)

# add (several) port knocking triggers
./iptables-knocking.sh mail2 108 107 106 105 9992 15
./iptables-knocking.sh mail3 101 102 103 104 9993 15
                         |    |   |   |   |   |   -> timeout_to_close
                       name   |   | port3 | port_to_open
                            port1 |       |
                               port2    port4

# reject everything else
iptables -A INPUT -s 0/0 -d 0/0 -j REJECT

When working with IPTABLES, keep paying attention. It's WAY too easy to lock yourself out or 'ruin your uptime' one way or another. Needless to say, I am in no way responsible for any problems or damages that are the result of the usage of the above code. You have been warned.


Next up, a trivial Java portknocking client:
public static void knockUDP(String hostname, int... ports) {
    DatagramSocket socket = new DatagramSocket();
    for(int port: ports) {

       // account for a bit of packet loss
       for(int i=0; i<4; i++) {
          socket.send(new DatagramPacket(new byte[1], 1, new InetSocketAddress(hostname, port)));
       }

       // try to make it more likely the packets arrive in-order
       Thread.sleep(100);
    }
}

public static void knockTCP(String hostname, int... ports) {
    for(int port: ports) {
       Socket s = new Socket();
       try {
          s.connect(new InetSocketAddress(hostname, port), 100);
          s.close(); // it appears we actually connected, which is not what we wanted....
       } catch( IOException) {
          // ignore, really!
       }
    }
}

knockUDP("your-public-server.com", 101, 102, 103, 104);
knockTCP("your-public-server.com", 101, 102, 103, 104);

1 comment: