# Port Knocking and Other Uses of 'Recent Match'

### Tom Eastep

Copyright © 2005, 2006 Thomas M. Eastep

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover, and with no Back-Cover Texts. A copy of the license is included in the section entitled “GNU Free Documentation License”.

2006-03-15

## What is Port Knocking?

Port knocking is a technique whereby attempting to connect to port A enables access to port B from that same host. For the example on which this article is based, see http://www.soloport.com/iptables.html which should be considered to be part of this documentation.

## Implementing Port Knocking in Shorewall

In order to implement this solution, your iptables and kernel must support the 'recent match' extension (see FAQ 42). These instructions also assume Shorewall version 2.2.0 or later.

In this example:

1. Attempting to connect to port 1600 enables SSH access. Access is enabled for 60 seconds.

2. Attempting to connect to port 1601 disables SSH access (note that in the article linked above, attempting to connect to port 1599 also disables access. This is an port scan defence as explained in the article).

To implement that approach:

1. Add an action named SSHKnock (see the Action documentation). Leave the `action.SSHKnock` file empty.

2. Create /etc/shorewall/SSHKnock with the following contents:

       if [ -n "$LEVEL" ]; then
           log_rule_limit $LEVEL $CHAIN SSHKnock ACCEPT "" "$TAG" -A -p tcp --dport 22   -m recent --rcheck --name SSH
           log_rule_limit $LEVEL $CHAIN SSHKnock DROP   "" "$TAG" -A -p tcp --dport ! 22
       fi
       run_iptables -A $CHAIN -p tcp --dport 22   -m recent --rcheck --seconds 60 --name SSH          -j ACCEPT
       run_iptables -A $CHAIN -p tcp --dport 1599 -m recent                       --name SSH --remove -j DROP
       run_iptables -A $CHAIN -p tcp --dport 1600 -m recent                       --name SSH --set    -j DROP
       run_iptables -A $CHAIN -p tcp --dport 1601 -m recent                       --name SSH --remove -j DROP

3. Now if you want to protect SSH access to the firewall from the Internet, add this rule in `/etc/shorewall/rules`:

       #ACTION          SOURCE            DEST           PROTO       DEST PORT(S)
       SSHKnock         net               $FW            tcp         22,1599,1600,1601

   If you want to log the DROPs and ACCEPTs done by SSHKnock, you can just add a log level as in:

       #ACTION          SOURCE            DEST           PROTO       DEST PORT(S)
       SSHKnock:info    net               $FW            tcp         22,1599,1600,1601

4. If you wish to use SSHKnock with a forwarded connection, you must be using Shorewall 2.3.1 or later for fullest protection. Assume that you forward port 22 from external IP address 206.124.146.178 to internal system 192.168.1.5. In /etc/shorewall/rules:

       #ACTION          SOURCE            DEST            PROTO       DEST PORT(S)  SOURCE      ORIGINAL
       #                                                                            PORT(S)     DEST
       DNAT-            net               loc:192.168.1.5 tcp         22            -           206.124.146.178
       SSHKnock         net               $FW             tcp         1599,1600,1601
       SSHKnock         net               loc:192.168.1.5 tcp         22            -           206.124.146.178

   ### Note

   You can use SSHKnock with DNAT on earlier releases provided that you omit the ORIGINAL DEST entry on the second SSHKnock rule. This rule will be quite secure provided that you specify 'norfc1918' on your external interface.

## Limiting Per-IP Connection Rate

Suppose that you wish to limit the number of connections to port 22 to 3/minute from individual internet hosts to the firewall.

1. Add an action named SSHLimit (see the Action documentation). Leave the `action.SSHLimit` file empty.

2. Create /etc/shorewall/SSHLimit with the following contents:

       run_iptables -A $CHAIN -m recent --name SSHA --set
       if [ -n "$LEVEL" ]; then
           run_iptables -N $CHAIN%
           log_rule_limit $LEVEL $CHAIN% SSHLimit REJECT "" "" -A
           run_iptables -A $CHAIN% -j reject
           run_iptables -A $CHAIN -m recent --name SSHA --update --seconds 60 --hitcount 4 -j $CHAIN%
       else
           run_iptables -A $CHAIN -m recent --update --name SSHA --seconds 60 --hitcount 4 -j reject
       fi
       run_iptables -A $CHAIN -j ACCEPT

3. Add this rule to /etc/shorewall/rules:

       #ACTION          SOURCE            DEST           PROTO       DEST PORT(S)
       SSHLimit         net               $FW            tcp         22

   If you wish to log the rejects at the 'info' level then use this rule instead:

       #ACTION          SOURCE            DEST           PROTO       DEST PORT(S)
       SSHLimit:info    net               $FW            tcp         22

4. If you wish to use SSHLimit with a forwarded connection, you must be using Shorewall 2.3.1 or later for fullest protection. Assume that you forward port 22 from external IP address 206.124.146.178 to internal system 192.168.1.5. In /etc/shorewall/rules:

       #ACTION          SOURCE            DEST            PROTO       DEST PORT(S)  SOURCE      ORIGINAL
       #                                                                            PORT(S)     DEST
       DNAT-            net               loc:192.168.1.5 tcp         22            -           206.124.146.178
       SSHLimit         net               loc:192.168.1.5 tcp         22            -           206.124.146.178

   ### Note

   You can use SSHLimit with DNAT on earlier releases provided that you omit the ORIGINAL DEST entry on the second SSHLimit rule. This rule will be quite secure provided that you specify 'norfc1918' on your external interface.

The above can be generalized into a flexible 'Limit' target.

### Note

'Limit' as described here is included as a standard part of Shorewall beginning with version 3.0.4. The following is included to show how 'Limit' is implemented; if you are running Shorewall 3.0.4 or later, you can omit the following two steps.

1. Add an action named Limit. Leave the action.Limit file empty.

2. Create /etc/shorewall/Limit with the following contents:

       set -- $(separate_list $TAG)

       run_iptables -A $CHAIN -m recent --name $1 --set

       if [ -n "$LEVEL" ]; then
           run_iptables -N $CHAIN%
           log_rule_limit $LEVEL $CHAIN% $1 REJECT "" "" -A
           run_iptables -A $CHAIN% -j reject
           run_iptables -A $CHAIN -m recent --name $1 --update --seconds $3 --hitcount $(( $2 + 1 )) -j $CHAIN%
       else
           run_iptables -A $CHAIN -m recent --update --name $1 --seconds $3 --hitcount $(( $2 + 1 )) -j reject
       fi

       run_iptables -A $CHAIN -j ACCEPT

Now if you want to limit the number of connections to port 22 to 3/munute from individual internet hosts to the filrewall, you can add this rule:

    #ACTION                SOURCE            DEST           PROTO       DEST PORT(S)
    Limit:none:SSHA,3,60   net               $FW            tcp         22

If you want rejected connections to be logged at the info level, use this rule instead:

    #ACTION                SOURCE            DEST           PROTO       DEST PORT(S)
    Limit:info:SSHA,3,60   net               $FW            tcp         22

As you can see, you pass four pieces of information to the Limit action:

- The log level. If you don't want to log, specify "none".

- The name of the recent set that you want to use ("SSHA" in this example).

- The maximum number of connections to accept (3 in this example).

- The number of seconds over which you are willing to accept that many connections (60 in this example).