On 6/26/2014 10:17 PM, Gerhard Wiesinger wrote:
> On 24.06.2014 19:28, Gerhard Wiesinger wrote:
>> On 20.06.2014 20:03, Tornhoof wrote:
>>> Hi, I previously used (4.5.x, 4.6.0) the following Portknocking
>>> configuration (from here http://shorewall.net/Events.html):
>>>
>>
>> Please find attached a "real" stateful Port Knocking Module for
>> shorewall. Was quite a challenge to write a stateful iptables "program".
>>
>> Feedback is welcome.
>>
>> @Tom: Can you integrate it in the next version?
>>
>> Thank you.
>>
>> Ciao,
>> Gerhard
> 
> Any Feedback?
> 

Sorry to be slow responding. Very busy week at work this week.

I guess what I would like to do is to place this in the contrib
directory on the server and create a link to it from the port mapping page.

If the .pm is installed in site_perl, and if the user codes:

        ?PERL use KnockEnhanced; KnockEnhanced 'net', '$FW',...

then the script creates the appropriate rules.

I've taken the liberty of making a couple of modifications (attached). I
removed the comment from the heading that describes what
Shorewall::Config does and I have added 'use KnockEnhanced;' to the samples.

Comments?

Thanks,
-Tom
-- 
Tom Eastep        \ When I die, I want to go like my Grandfather who
Shoreline,         \ died peacefully in his sleep. Not screaming like
Washington, USA     \ all of the passengers in his car
http://shorewall.net \________________________________________________
#
#     This program is under GPL 
[http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt]
#
#     (c) 2012 - Gerhard Wiesinger (shorew...@wiesinger.com)
#     (c) by the authors of the links below
#         Code based on:
#           http://www.shorewall.net/ManualChains.html
#           
http://www.engardelinux.org/modules/index/list_archives.cgi?list=shorewall-users&page=0078.html&month=2011-04
#         Ideas based on:
#           
http://blog.franciscodavid.eu/en/2010/08/02/port-knocking-the-easy-way/
#           http://pub.ligatura.org/fs/netfilter/misc/portknock_multi
#
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of Version 2 of the GNU General Public License
#       as published by the Free Software Foundation.
#
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#       GNU General Public License for more details.
#
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
02110-1301 USA.
#
################################################################################
# CHANGELOG
################################################################################
# V1.0 release
# *) Enhancements
#    1.) All none specified ports are trap ports
#    2.) Knocking sequence must be as specified, implemented full correct state 
machine
#    3.) Mixture of tcp and udp ports possible in knocking sequence
#    4.) Mixture of allowed targets with tcp and udp is possible
#    
################################################################################
# Port Knocking (should be the last entries in /etc/shorewall/rules)
################################################################################
# Requires shorter log format in shorewallconf: LOGFORMAT="SW:%s:%s:"
#
# Debugging information:
# 1.) View iptables recent states
#     grep -H '.*' /proc/net/xt_recent/*
# 2.) Flush recent states (see man iptables in recent section for details)
#     echo / > /proc/net/xt_recent/KnockE_OpenVPN1_NO_VALID_KNOCK
# 3.) View iptables states
#     iptables -L -v -x -n | less -I
#
# Sample config, last entries in  /etc/shorewall/rules:
#PERL use KnockEnhanced; KnockEnhanced 'net', '$FW', {name => 'OpenVPN1', 
log_level => 3, proto => 'udp', target => 'openvpn', knocker => 
[8922,1489,29889,15073,36501,46778,54499,63874]};
#PERL use KnockEnhanced; KnockEnhanced 'net', '$FW', {name => 'OpenVPN2', 
log_level => 3, proto => 'udp', target => 'openvpn', knocker => [52145,15423]};
#PERL use KnockEnhanced; KnockEnhanced 'net', '$FW', {name => 'OpenVPN3', 
log_level => 3, proto => 'udp', target => 'openvpn', knocker => [8740,1120]};
#PERL use KnockEnhanced; KnockEnhanced 'net', '$FW', {name => 'OpenVPNT', 
log_level => 3, proto => 'udp', target => ['openvpn','https:tcp'], knocker => 
[8922,'1489:tcp',29889,15073,36501,46778,54499,63874]};
#PERL use KnockEnhanced; KnockEnhanced 'net', '$FW', {name => 'SSH1', log_level 
=> 3, proto => 'tcp', target => 'ssh', knocker => [52245,15623,19845]};

package KnockEnhanced;

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{KnockEnhanced};

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 get_proto_port
{
  my $protoport = shift;
  my $defaultproto = shift;

  if ($protoport =~ m/:/)
  {
    return split(':', $protoport);
  }
  return ($protoport, $defaultproto);
}

sub KnockEnhanced
{
  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 $i = 0;
  my %chainrefs;
  my %knocknames;

  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";
    }
  }

  # 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 = 'KnockE_' . $name;

  my $lastname = $name;
  my $knocknameprefix = $name . '_KNOCK_';
  my $knockchainprefix = $name . '_CHAIN_';
  my $knockchaintrapprefix = $name . '_TRAP';
  my $knocknotvalidprefix = $name . '_NO_VALID_KNOCK';

  # 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);
  }

  ##################################################
  # Trap chain
  ##################################################
  my $chainknocktrapname = $knockchaintrapprefix;
  my $newknockchaintrapref = new_manual_chain( $chainknocktrapname );
  # Don't let the chain be optimized and deleted
  dont_delete( $newknockchaintrapref );
  
  ##################################################
  # Knocking chains & trap chain insertions
  ##################################################
  # Add the recent match rules to the manual chain
  $i = 1;
  foreach my $knock (@knocker_ports)
  {
    my $chainknockname = $knockchainprefix . $i;
    my $currentknockname = $knocknameprefix . $i;
    $knocknames{$i} = $currentknockname;

    # Add a rule for each knocker to the Trap chain for removal when trap 
detected
    add_rule( $newknockchaintrapref, "-m recent --name $currentknockname 
--remove" );

    my $newknockchainref = new_manual_chain( $chainknockname );
    # Don't let the chain be optimized and deleted
    dont_delete( $newknockchainref );
    $chainrefs{$i} = $newknockchainref;

    # Valid knock found, avoid trap checking later
    add_rule( $newknockchainref, "-m recent --remove --name 
$knocknotvalidprefix" );

    if ($i > 1)
    {
      add_rule( $newknockchainref, "-m recent --name " . $knocknameprefix . ($i 
- 1) . " --remove" );
    }
    add_rule( $newknockchainref, "-m recent --name " . $knocknameprefix . ($i + 
0) . " --set" );
    if ($args->{log_level} & 2)
    {
      log_rule_limit($args->{log_level},
                     $newknockchainref,
                     $knocknameprefix . ($i + 0),
                     '',
                     '',
                     $args->{log_tag} || '',
                     'add',
                     ""
                    );
    }
    $i++;
  }

  # Once again, the first knock (why should it be necessary?)
  # add_rule($chainref, "-m recent --update --name $knocknames{1}");

  # Add no valid knock, set it to no valid knock seen (reset when one is seen), 
necessary for other traps
  add_rule($chainref, "-m recent --set --name $knocknotvalidprefix");

  ##################################################
  # Knocking chain state machine
  ##################################################
  # State machine for detecting knocking ports
  $i = 1;
  foreach my $knock (@knocker_ports)
  {
    (my $current_port, my $current_proto) = get_proto_port($knock, $proto);

    if ($i == 1)
    {

      add_rule($chainref, "-p $current_proto --dport $current_port -m recent 
--set --name $knocknames{$i} -j $chainrefs{$i}->{name}");
    }
    else
    {
      add_rule($chainref, "-p $current_proto --dport $current_port -m recent 
--rcheck --name $knocknames{$i-1} -j $chainrefs{$i}->{name}");
    }
    $lastname = $knocknames{$i};
    $i++;
  }

  ##################################################
  # Allow target when the sequence is completed
  ##################################################
  foreach my $port (@target)
  {
    (my $current_port, my $current_proto) = get_proto_port($port, $proto);

    if ($args->{log_level} & 1)
    {
      log_rule_limit($args->{log_level},
                     $chainref,
                     $name,
                     'ACCEPT',
                     '',
                     $args->{log_tag} || '',
                     'add',
                     "-p $current_proto --dport $current_port -m recent 
--rcheck --seconds $seconds --name $lastname"
                    );
    }
    else
    {
      add_rule($chainref, "-p $current_proto --dport $current_port -m recent 
--rcheck --seconds $seconds --name $lastname -j ACCEPT");
    }
  }

  ##################################################
  # Add Trap chain
  ##################################################
  add_rule($chainref, "-m recent --rcheck --name $knocknotvalidprefix -j 
$knockchaintrapprefix");

  ##################################################
  # And add a rule to the main chain(s) to jump into the manual chain at the 
appropriate points
  ##################################################
  shorewall "$chainref->{name} $src $dest";

  return 1;
}

1;

Attachment: signature.asc
Description: OpenPGP digital signature

------------------------------------------------------------------------------
Open source business process management suite built on Java and Eclipse
Turn processes into business applications with Bonita BPM Community Edition
Quickly connect people, data, and systems into organized workflows
Winner of BOSSIE, CODIE, OW2 and Gartner awards
http://p.sf.net/sfu/Bonitasoft
_______________________________________________
Shorewall-users mailing list
Shorewall-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/shorewall-users

Reply via email to