On Sun, Oct 28, 2007 at 09:23:43AM -0700, Tom Eastep wrote:
> Andrew Suffield wrote:
>
> > I've been playing with it, in an attempt to generate
> > similar-but-more-flexible behaviour for the SSHKnock action described
> > in http://www.shorewall.net/PortKnocking.html (since that's a familiar
> > example that should showcase how this feature can be used).
> >
> > The goal is something like:
> >
> > PERL Knock 'net', 'loc:192.168.1.5', {port => 22, knocker => 1600, trap =>
> > [1599, 1601]};
> >
> > where the 'net' and 'loc:192.168.1.5' arguments are handed off to
> > shorewall for normal processing, while the perl function takes care of
> > the rest.
>
> With the patches that I've posted, this should now be possible. When you get
> it working, please post it here and I'll include it in a new article that
> describes "Manual Chains".
Here's a first cut - I haven't yet tested this on a real firewall, and
it'll be a few days before I get a chance to do that. A diff of the
compiled output against that given by the example in the 'Port
Knocking' article shows no significant difference, so I think it's at
least as correct as that article. Would be nice if somebody who
actually uses the method in that article could try it, and see how it
matches their expectations.
Knock.pm is attached, and contains the main code; it should be placed
in the firewall config directory (/etc/shorewall or whatever). You
also have to create a 'compile' file, containing the following two
lines:
use Knock;
1;
Then, for the example rules lines in
http://www.shorewall.net/PortKnocking.html, here are translations:
#ACTION SOURCE DEST PROTO DEST PORT(S)
SSHKnock net $FW tcp 22,1599,1600,1601
becomes:
PERL Knock 'net', 'loc:192.168.1.5', {target => 22, knocker => 1600, trap =>
[1599, 1601]};
and:
#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
becomes:
DNAT- net loc:192.168.1.5 tcp 22 -
206.124.146.178
PERL Knock 'net', '$FW', {name => 'SSH', knocker => 1600, trap => [1599, 1601]};
PERL Knock 'net', 'loc:192.168.1.5', {name => 'SSH', target => 22,
original_dest => '206.124.136.178'};
The 'name' argument is used to tie together related
target/knocker/trap sets that appear in different rules - unlike the
original, you can just add more rules with different names to create
different sets. Also there's the obvious benefit that all the
configuration is now in the 'rules' file, rather than having half of
it hardcoded into the action script, so if you change the port numbers
in the rule then it actually works. Might be worth rewriting the 'Port
Knocking' article to use this method.
package Knock;
use strict;
use warnings;
use base qw{Exporter};
use Carp;
use Shorewall::Chains;
use Scalar::Util qw{reftype};
use Shorewall::Config qw{shorewall};
our @EXPORT = qw{Knock};
my %recent_names;
my %chains_created;
sub scalar_or_array {
my $arg = shift;
my $name = shift;
return () unless defined $arg;
return ($arg) unless reftype($arg);
return @$arg if reftype($arg) eq 'ARRAY';
croak "Expecting argument '$name' to be scalar or array ref";
}
sub Knock {
my $src = shift;
my $dest = shift;
my $args = shift;
my $proto = $args->{proto} || 'tcp';
my $seconds = $args->{seconds} || 60;
my $original_dest = $args->{original_dest} || '-';
my @target = scalar_or_array($args->{target}, 'target');
my @knocker_ports = scalar_or_array($args->{knocker}, 'knocker');
my @trap_ports = scalar_or_array($args->{trap}, 'trap');
if (not defined $args->{name}) {
# If you don't supply a name, then this must be the single-call
# variant, so you have to specify all the arguments
unless (scalar @target) {
croak "No 'target' ports specified";
}
unless (scalar @knocker_ports) {
croak "No 'knock' ports specified";
}
}
# We'll need a unique name for the recent match list. Construct one
# from the port and a serial number, if the user didn't supply one.
my $name = $args->{name} || ($target[0] . '_' . ++$recent_names{$target[0]});
$name = 'Knock' . $name;
# We want one chain for all Knock rules that share a 'name' field
my $chainref = $chains_created{$name};
unless (defined $chainref) {
$chainref = $chains_created{$name} = new_manual_chain($name);
}
# I have no idea what this does, copied blindly from the 'Port
# Knocking' article. Presumably it does something sensible.
if ($args->{log_level}) {
foreach my $port (@target) {
log_rule_limit($args->{log_level},
$chainref,
'Knock',
'ACCEPT',
'',
$args->{log_tag} || '',
'add',
"-p $proto --dport $port -m recent --rcheck --name $name"
);
log_rule_limit($args->{log_level},
$chainref,
'Knock',
'DROP',
'',
$args->{log_tag} || '',
'add',
"-p $proto --dport ! $port"
);
}
}
# Add the recent match rules to the manual chain
foreach my $knock (@knocker_ports) {
add_rule($chainref, "-p $proto --dport $knock -m recent --name $name --set -j DROP");
}
foreach my $trap (@trap_ports) {
add_rule($chainref, "-p $proto --dport $trap -m recent --name $name --remove -j DROP");
}
foreach my $port (@target) {
add_rule($chainref, "-p $proto --dport $port -m recent --rcheck --seconds $seconds --name $name -j ACCEPT");
}
# And add a rule to the main chain(s) to jump into the manual chain at the appropriate points
my $all_dest_ports = join(',', @target, @knocker_ports, @trap_ports);
shorewall "$chainref->{name} $src $dest $proto $all_dest_ports - $original_dest";
return 1;
}
1;
-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems? Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
_______________________________________________
Shorewall-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/shorewall-devel