mirror of
https://gitlab.com/shorewall/code.git
synced 2024-11-23 16:13:18 +01:00
dbb555f56d
git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@6676 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
315 lines
12 KiB
XML
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>
|
|
<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>
|
|
<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>
|
|
<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 <set name>,<max connections>,<interval> 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 <set name>,<max connections>,<interval> 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->{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->{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> |