shorewall_code/docs/PortKnocking.xml
2007-06-28 20:41:32 +00:00

315 lines
12 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<article>
<!--$Id$-->
<articleinfo>
<title>Port Knocking and Other Uses of 'Recent Match'</title>
<authorgroup>
<author>
<firstname>Tom</firstname>
<surname>Eastep</surname>
</author>
</authorgroup>
<pubdate><?dbtimestamp format="Y/m/d"?></pubdate>
<copyright>
<year>2005</year>
<year>2006</year>
<holder>Thomas M. Eastep</holder>
</copyright>
<legalnotice>
<para>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
<quote><ulink url="GnuCopyright.htm">GNU Free Documentation
License</ulink></quote>.</para>
</legalnotice>
</articleinfo>
<note>
<para>The feature described in this article require '<ulink
url="http://snowman.net/projects/ipt_recent/">Recent Match</ulink>' in
your iptables and kernel. See the output of <command>shorewall show
capabilities</command> to see if you have that match.</para>
</note>
<section id="What">
<title>What is Port Knocking?</title>
<para>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 <ulink
url="http://www.soloport.com/iptables.html">http://www.soloport.com/iptables.html</ulink>
which should be considered to be part of this documentation.</para>
</section>
<section id="How">
<title>Implementing Port Knocking in Shorewall</title>
<para>In order to implement this solution, your iptables and kernel must
support the 'recent match' extension (see <ulink url="FAQ.htm#faq42">FAQ
42</ulink>). These instructions also assume Shorewall version 2.2.0 or
later.</para>
<para>In this example:</para>
<orderedlist>
<listitem>
<para>Attempting to connect to port 1600 enables SSH access. Access is
enabled for 60 seconds.</para>
</listitem>
<listitem>
<para>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).</para>
</listitem>
</orderedlist>
<para>To implement that approach:</para>
<orderedlist>
<listitem>
<para>Add an action named SSHKnock (see the <ulink
url="Actions.html">Action documentation</ulink>). Leave the
<filename>action.SSHKnock</filename> file empty.</para>
</listitem>
<listitem>
<para>Create /etc/shorewall/SSHKnock with the following
contents.</para>
<para>If using Shorewall-shell:</para>
<programlisting>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</programlisting>
<para>If using Shorewall-perl:<programlisting>use Shorewall::Chains;
if ( $level ) {
log_rule_limit( $level,
$chainref,
'SSHKnock',
'ACCEPT',
'',
$tag,
'add',
'-p tcp --dport 22 -m recent --rcheck --name SSH );
log_rule_limit( $level,
$chainref,
'SSHKnock'
'DROP'
'',
$tag,
'add',
'-p tcp --dport ! 22' );
}
add_rule( $chainref, '-p tcp --dport 22 -m recent --rcheck --seconds 60 --name SSH -j ACCEPT' );
add_rule( $chainref, '-p tcp --dport 1599 -m recent --name SSH --remove -j DROP' );
add_rule( $chainref, '-p tcp --dport 1600 -m recent --name SSH --set -j DROP' );
add_rule( $chainref, '-p tcp --dport 1601 -m recent --name SSH --remove -j DROP' );
1;</programlisting></para>
</listitem>
<listitem>
<para>Now if you want to protect SSH access to the firewall from the
Internet, add this rule in
<filename>/etc/shorewall/rules</filename>:</para>
<programlisting>#ACTION SOURCE DEST PROTO DEST PORT(S)
SSHKnock net $FW tcp 22,1599,1600,1601</programlisting>
<para>If you want to log the DROPs and ACCEPTs done by SSHKnock, you
can just add a log level as in:</para>
<programlisting>#ACTION SOURCE DEST PROTO DEST PORT(S)
SSHKnock:info net $FW tcp 22,1599,1600,1601</programlisting>
</listitem>
<listitem>
<para>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:</para>
<programlisting>#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</programlisting>
<note>
<para>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.</para>
</note>
</listitem>
</orderedlist>
</section>
<section id="Limit">
<title>Limiting Per-IP Connection Rate</title>
<important>
<para>Debian users. This feature is broken in the Debian version 3.0.7
of Shorewall (and possibly in other versions). The file
<filename>/usr/share/shorewall/Limit</filename> was inadvertently
dropped from the .deb. That file may be obtained from <ulink
url="???">Shorewall SVN</ulink> and installed manually.</para>
</important>
<para>Beginning with Shorewall 3.0.4, Shorewall has a 'Limit' <ulink
url="Actions.html">action</ulink>. Limit is invoked with a comma-separated
list in place of a logging tag. The list has three elements:</para>
<orderedlist>
<listitem>
<para>The name of a 'recent' set; you select the set name which must
conform to the rules for a valid chain name. Different rules that
specify the same set name will use the same set of counters.</para>
</listitem>
<listitem>
<para>The number of connections permitted in a specified time
period.</para>
</listitem>
<listitem>
<para>The time period, expressed in seconds.</para>
</listitem>
</orderedlist>
<para>Connections that exceed the specified rate are dropped.</para>
<para>For example,to use a recent set name of <emphasis
role="bold">SSHA</emphasis>, and to limiting SSH to 3 per minute, use this
entry in <filename>/etc/shorewall/rules</filename>:</para>
<programlisting>#ACTION SOURCE DEST PROTO DEST PORT(S)
Limit:none:SSHA,3,60 net $FW tcp 22</programlisting>
<para>If you want dropped connections to be logged at the info level, use
this rule instead:</para>
<programlisting>#ACTION SOURCE DEST PROTO DEST PORT(S)
Limit:info:SSHA,3,60 net $FW tcp 22</programlisting>
<para>To summarize, you pass four pieces of information to the Limit
action:</para>
<itemizedlist>
<listitem>
<para>The log level. If you don't want to log, specify "none".</para>
</listitem>
<listitem>
<para>The name of the recent set that you want to use ("SSHA" in this
example).</para>
</listitem>
<listitem>
<para>The maximum number of connections to accept (3 in this
example).</para>
</listitem>
<listitem>
<para>The number of seconds over which you are willing to accept that
many connections (60 in this example).</para>
</listitem>
</itemizedlist>
<section id="LimitImp">
<title>How Limit is Implemented</title>
<para>For those who are curious, the Limit action is implemented in
Shorewall 3.0 and Shorewall 3.2 as follows:</para>
<itemizedlist>
<listitem>
<para>The file
<filename>/usr/share/shorewall/action</filename>.Limit is
empty.</para>
</listitem>
<listitem>
<para>The file <filename>/usr/share/shorewall/Limit</filename> is as
follows:</para>
<programlisting>set -- $(separate_list $TAG)
[ $# -eq 3 ] || fatal_error "Rule must include &lt;set name&gt;,&lt;max connections&gt;,&lt;interval&gt; as the log tag"
run_iptables -A $CHAIN -m recent --name $1 --set
if [ -n "$LEVEL" ]; then
run_iptables -N $CHAIN%
log_rule_limit $LEVEL $CHAIN% $1 DROP "" "" -A
run_iptables -A $CHAIN% -j DROP
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 DROP
fi
run_iptables -A $CHAIN -j ACCEPT</programlisting>
</listitem>
</itemizedlist>
<para>In Shorewall 3.3, Limit is made into a built-in action; basically
that means that the above code now lives inside of Shorewall rather than
in a separate file.</para>
<para>For completeness, here's the above
<filename>/usr/share/shorewall/Limit</filename> for use with
Shorewall-perl:</para>
<programlisting>my @tag = split /,/, $tag;
fatal_error 'Limit rules must include &lt;set name&gt;,&lt;max connections&gt;,&lt;interval&gt; as the log tag (' . join( ':', 'Limit', $level eq '' ? 'none' : $level , $tag ) . ')'
unless @tag == 3;
my $set = $tag[0];
for ( @tag[1,2] ) {
fatal_error 'Max connections and interval in Limit rules must be numeric (' . join( ':', 'Limit', $level eq '' ? 'none' : $level, $tag ) . ')' unless /^\d+$/
}
my $count = $tag[1] + 1;
add_rule $chainref, "-m recent --name $set --set";
if ( $level ) {
my $xchainref = new_chain 'filter' , "$chainref-&gt;{name}%";
log_rule_limit $level, $xchainref, $tag[0], 'DROP', '', '', 'add', '';
add_rule $xchainref, '-j DROP';
add_rule $chainref, "-m recent --name $set --update --seconds $tag[2] --hitcount $count -j $xchainref-&gt;{name}";
} else {
add_rule $chainref, "-m recent --update --name $set --seconds $tag[2] --hitcount $count -j DROP";
}
add_rule $chainref, '-j ACCEPT';
1; </programlisting>
</section>
</section>
</article>