Diff to Knock.pm from http://www.shorewall.net/ManualChains.html
Changes:
* Support for more then one port.
* Clear state if knock out of order.
- if too early.
- if too late.
- this will break you if using the same port more then once.
* Fixed issue with logging, where DROP would log even when nothing dropped.
* WARNING, rejects a correct knock. This is a security (*)risk, but so
is knock, and it's essential with the above out of order tests.
More robust, YMMV.
(*) My thinking is who is going to probe around after finding one port
that rejects, if you have 3 or even 5 other ports it becomes impossible
to do anything with this. However one should note that this will lead
to an easy crack if you just have one port.
A web page that will keep your Knock going so the port is always open,
HTML 5 local storage enabled.
http://pastebin.com/bzDgL5BN
* This page depends on rejecting a correct knock, browsers don't
time-out easily.
===================================================================
RCS file: Knock.pm,v
retrieving revision 1.1
diff -u -r1.1 Knock.pm
--- Knock.pm 2012/04/26 16:44:39 1.1
+++ Knock.pm 2012/04/27 04:39:48
@@ -52,9 +52,9 @@
$name = 'Knock' . $name;
# We want one chain for all Knock rules that share a 'name' field
- my $chainref = $chains_created{$name};
+ my $chainref = $chains_created{"${name}_0"};
unless (defined $chainref) {
- $chainref = $chains_created{$name} = new_manual_chain($name);
+ $chainref = $chains_created{"${name}_0"} = new_manual_chain("${name}_0");
}
# Logging
@@ -67,36 +67,97 @@
'',
$args->{log_tag} || '',
'add',
- "-p $proto --dport $port -m recent --rcheck --name $name"
+ "-p $proto --dport $port -m recent --rcheck --name ${name}_$#knocker_ports"
);
+ }
+ }
+
+ # In this instance we make a chain for each port,
+ # identified by it's index number.
+
+ # Reset chain handles failure, used now populated later.
+ my $name_f = "${name}_f";
+ my $chainref_f = $chains_created{$name_f};
+ unless (defined $chainref_f) {
+ $chainref_f = $chains_created{$name_f} = new_manual_chain($name_f);
+ }
+
+ foreach my $trap (@trap_ports) {
+ add_rule($chainref, "-p $proto --dport $trap -j $name_f");
+ }
+
+ # Add the recent match rules to the correct chain
+ for (my $index=0;$index<@knocker_ports;$index++) {
+ my $knock = $knocker_ports[$index];
+ my $_name = "${name}_$index";
+ my $_namepp = "${name}_".($index+1);
+ my $_namemm = "${name}_".($index-1);
+ my $_chainref = $chains_created{$_name};
+ unless (defined $_chainref) {
+ $_chainref = $chains_created{$_name} = new_manual_chain($_name);
+ }
+ add_rule($chainref_f, "-m recent --name ${name}_$index --remove");
+ # Auto-trap the other ports?
+ # This will break you if using the same port more then once.
+ # First the root collection has "not yet" masq.
+ for (my $_index=$index+1;$_index<@knocker_ports;$_index++) {
+ my $trap=$knocker_ports[$_index];
+ # Port is too early check and pass to chain that clears state.
+ add_rule($chainref, "-p $proto --dport $trap -m recent ! --rcheck --seconds $seconds --name ${name}_$index -j $name_f");
+ }
+ # Then for the ports where it is too late.
+ for (my $_index=0;$_index<$index;$_index++) {
+ my $trap=$knocker_ports[$_index];
+ add_rule($_chainref, "-p $proto --dport $trap -j $name_f");
+ }
+ # This is the rule to catch the knock.
+ add_rule($_chainref, "-p $proto --dport $knock -m recent --name $_name --set -j REJECT");
+ # Add the rule(s) to pass any knock or target to the next chain, only 15 ports at a time.
+ if ($index<$#knocker_ports) {
+ unless (defined $chains_created{$_namepp}) {
+ $chains_created{$_namepp} = new_manual_chain($_namepp);
+ }
+ push(my @all_dest_ports, @target, @knocker_ports);
+ for (my $_index=0;$_index<$#all_dest_ports;$_index+=15) {
+ my @_all_dest_ports = @all_dest_ports;
+ my $all_dest_ports = join(',', splice(@_all_dest_ports, $_index, 15));
+ add_rule($_chainref, "-p $proto -m multiport --dports $all_dest_ports -m recent --rcheck --seconds $seconds --name $_name -j $_namepp");
+ }
+ }
+ }
+
+ # We only add the last port to the targest.
+ foreach my $port (@target) {
+ add_rule($chainref, "-p $proto --dport $port -m recent --rcheck --seconds $seconds --name ${name}_$#knocker_ports -j ACCEPT");
+ }
+ # Logging
+ if ($args->{log_level}) {
+ foreach my $port (@target) {
log_rule_limit($args->{log_level},
$chainref,
'Knock',
- 'DROP',
+ 'Filtered',
'',
$args->{log_tag} || '',
'add',
"-p $proto --dport ! $port"
);
}
+ log_rule_limit($args->{log_level},
+ $chainref_f,
+ 'Knock',
+ 'DROP',
+ '',
+ $args->{log_tag} || '',
+ 'add', ''
+ );
}
-
- # 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");
- }
+ add_rule($chainref_f, "-j DROP");
# 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);
+ push(my @all_dest_ports, @target, @knocker_ports, @trap_ports);
+ my $all_dest_ports = join(',', @all_dest_ports);
shorewall "$chainref->{name} $src $dest $proto $all_dest_ports - $original_dest";
return 1;