From ed49d0d9239f8328d3ccc1d153e22935ff0184d1 Mon Sep 17 00:00:00 2001 From: Faidon Liambotis Date: Thu, 13 Jun 2013 11:35:34 +0300 Subject: [PATCH] Automatically apply @ipfilter on dual-stack config Add support for ferm to automatically apply @ipfilter on constructs such as: domain (ip ip6) chain INPUT { saddr (192.0.2.5 2001:db8::5) proto tcp dport ssh ACCEPT; } and do the obvious. This explicitly *not* modify the "domain ip" and "domain ip6" cases (single stack) as ferm should not silently discard such errors but let ip6?tables complain to the admin. --- Makefile | 2 +- NEWS | 1 + src/ferm | 60 +++++++++++++++++++++++++++++++++--------- test/misc/address-magic.ferm | 30 +++++++++++++++++++++ test/misc/address-magic.result | 22 ++++++++++++++++ 5 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 test/misc/address-magic.ferm create mode 100644 test/misc/address-magic.result diff --git a/Makefile b/Makefile index f56bb3f..c12a465 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ FERM_SCRIPTS += $(wildcard test/protocols/*.ferm) $(wildcard test/misc/*.ferm) FERM_SCRIPTS += $(wildcard test/ipv6/*.ferm) FERM_SCRIPTS += $(wildcard test/arptables/*.ferm) $(wildcard test/ebtables/*.ferm) -EXCLUDE_IMPORT = test/misc/subchain-domains.ferm test/misc/ipfilter.ferm test/ipv6/mixed.ferm +EXCLUDE_IMPORT = test/misc/subchain-domains.ferm test/misc/ipfilter.ferm test/ipv6/mixed.ferm test/misc/address-magic.ferm IMPORT_SCRIPTS = $(filter-out $(EXCLUDE_IMPORT) test/arptables/% test/ebtables/%,$(FERM_SCRIPTS)) # just a hack diff --git a/NEWS b/NEWS index 94756f9..7c57767 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ v2.1.3 - not yet released - support netfilter modules: * CT * TEE + - automatically apply @ipfilter on dual-stack config v2.1.2 - 17 Dec 2012 diff --git a/src/ferm b/src/ferm index c5498de..4230e13 100755 --- a/src/ferm +++ b/src/ferm @@ -141,6 +141,7 @@ sub rollback(); sub execute_fast($); sub execute_slow($); sub join_value($$); +sub ipfilter($@); # add a module definition sub add_def_x { @@ -227,7 +228,8 @@ add_proto_def 'udp', qw(); add_match_def '', # --source, --destination - qw(source! saddr:=source destination! daddr:=destination), + qw(source!&address_magic saddr:=source), + qw(destination!&address_magic daddr:=destination), # --in-interface qw(in-interface! interface:=in-interface if:=in-interface), # --out-interface @@ -434,6 +436,47 @@ sub multiport_params { } } +sub ipfilter($@) { + my $domain = shift; + my @ips; + # very crude IPv4/IPv6 address detection + if ($domain eq 'ip') { + @ips = grep { !/:[0-9a-f]*:/ } @_; + } elsif ($domain eq 'ip6') { + @ips = grep { !m,^[0-9./]+$,s } @_; + } + return @ips; +} + +sub address_magic { + my $rule = shift; + my $domain = $rule->{domain}; + my $value = getvalues(undef, allow_negation => 1); + + my @ips; + my $negated = 0; + if (ref $value and ref $value eq 'ARRAY') { + @ips = @$value; + } elsif (ref $value and ref $value eq 'negated') { + @ips = @$value; + $negated = 1; + } elsif (ref $value) { + die; + } else { + @ips = ($value); + } + + # only do magic on domain (ip ip6); do not process on a single-stack rule + # as to let admins spot their errors instead of silently ignoring them + @ips = ipfilter($domain, @ips) if defined $rule->{domain_both}; + + if ($negated && scalar @ips) { + return bless \@ips, 'negated'; + } else { + return \@ips; + } +} + # initialize stack: command line definitions unshift @stack, {}; @@ -1240,15 +1283,7 @@ sub getvalues { error('Usage: @ipfilter((ip1 ip2 ...))') unless @params == 1; my $domain = $stack[0]{auto}{DOMAIN}; error('No domain specified') unless defined $domain; - my @ips = to_array($params[0]); - - # very crude IPv4/IPv6 address detection - if ($domain eq 'ip') { - @ips = grep { !/:[0-9a-f]*:/ } @ips; - } elsif ($domain eq 'ip6') { - @ips = grep { !m,^[0-9./]+$,s } @ips; - } - + my @ips = ipfilter($domain, to_array($params[0])); return \@ips; } else { error("unknown ferm built-in function"); @@ -1655,7 +1690,7 @@ sub new_level(\%$) { $rule->{keywords} = $prev->{keywords}; $rule->{match} = { %{$prev->{match}} }; $rule->{options} = [@{$prev->{options}}]; - foreach my $key (qw(domain domain_family table chain protocol has_rule has_action)) { + foreach my $key (qw(domain domain_family domain_both table chain protocol has_rule has_action)) { $rule->{$key} = $prev->{$key} if exists $prev->{$key}; } @@ -2074,6 +2109,7 @@ sub enter($$) { my %inner; new_level(%inner, \%rule); set_domain(%inner, $domain) or next; + $inner{domain_both} = 1; $script->{base_level} = 0; $script->{tokens} = [ @$tokens ]; enter(0, \%inner); @@ -2201,7 +2237,7 @@ sub enter($$) { match => {}, options => [], ); - $inner{$_} = $rule{$_} foreach qw(domain domain_family table keywords); + $inner{$_} = $rule{$_} foreach qw(domain domain_family domain_both table keywords); $inner{chain} = $inner{auto}{CHAIN} = $subchain; if (exists $rule{protocol}) { diff --git a/test/misc/address-magic.ferm b/test/misc/address-magic.ferm new file mode 100644 index 0000000..43acb44 --- /dev/null +++ b/test/misc/address-magic.ferm @@ -0,0 +1,30 @@ +@def $VARIABLE_TEST = (192.0.2.6 2001:db8::6); +@def $IPFILTER_TEST = (192.0.2.7 2001:db8::7); + +domain (ip ip6) table filter chain INPUT { + saddr 192.0.2.1 proto tcp dport ssh ACCEPT; + saddr (192.0.2.2 192.0.2.3) proto tcp dport ssh ACCEPT; + saddr 2001:db8::1 proto tcp dport ssh ACCEPT; + saddr (2001:db8::2 2001:db8::3) proto tcp dport ssh ACCEPT; + + saddr (192.0.2.4 2001:db8::4) proto tcp dport ssh ACCEPT; + + saddr ! 192.0.2.5 proto tcp dport ssh ACCEPT; + saddr ! 2001:db8::5 proto tcp dport ssh ACCEPT; + + saddr $VARIABLE_TEST proto tcp dport ssh ACCEPT; + saddr @ipfilter($IPFILTER_TEST) proto tcp dport ssh ACCEPT; + + saddr localhost proto tcp dport ssh ACCEPT; + saddr localhost.localdomain proto tcp dport ssh ACCEPT; +} + +domain ip table filter chain INPUT { + saddr 192.0.2.11 proto tcp dport ssh ACCEPT; + saddr 2001:db8::11 proto tcp dport ssh ACCEPT; +} + +domain ip6 table filter chain INPUT { + saddr 192.0.2.21 proto tcp dport ssh ACCEPT; + saddr 2001:db8::21 proto tcp dport ssh ACCEPT; +} diff --git a/test/misc/address-magic.result b/test/misc/address-magic.result new file mode 100644 index 0000000..e15908d --- /dev/null +++ b/test/misc/address-magic.result @@ -0,0 +1,22 @@ +iptables -t filter -A INPUT -s 192.0.2.1 -p tcp --dport ssh -j ACCEPT +iptables -t filter -A INPUT -s 192.0.2.2 -p tcp --dport ssh -j ACCEPT +iptables -t filter -A INPUT -s 192.0.2.3 -p tcp --dport ssh -j ACCEPT +iptables -t filter -A INPUT -s 192.0.2.4 -p tcp --dport ssh -j ACCEPT +iptables -t filter -A INPUT ! -s 192.0.2.5 -p tcp --dport ssh -j ACCEPT +iptables -t filter -A INPUT -s 192.0.2.6 -p tcp --dport ssh -j ACCEPT +iptables -t filter -A INPUT -s 192.0.2.7 -p tcp --dport ssh -j ACCEPT +iptables -t filter -A INPUT -s localhost -p tcp --dport ssh -j ACCEPT +iptables -t filter -A INPUT -s localhost.localdomain -p tcp --dport ssh -j ACCEPT +iptables -t filter -A INPUT -s 192.0.2.11 -p tcp --dport ssh -j ACCEPT +iptables -t filter -A INPUT -s 2001:db8::11 -p tcp --dport ssh -j ACCEPT +ip6tables -t filter -A INPUT -s 2001:db8::1 -p tcp --dport ssh -j ACCEPT +ip6tables -t filter -A INPUT -s 2001:db8::2 -p tcp --dport ssh -j ACCEPT +ip6tables -t filter -A INPUT -s 2001:db8::3 -p tcp --dport ssh -j ACCEPT +ip6tables -t filter -A INPUT -s 2001:db8::4 -p tcp --dport ssh -j ACCEPT +ip6tables -t filter -A INPUT ! -s 2001:db8::5 -p tcp --dport ssh -j ACCEPT +ip6tables -t filter -A INPUT -s 2001:db8::6 -p tcp --dport ssh -j ACCEPT +ip6tables -t filter -A INPUT -s 2001:db8::7 -p tcp --dport ssh -j ACCEPT +ip6tables -t filter -A INPUT -s localhost -p tcp --dport ssh -j ACCEPT +ip6tables -t filter -A INPUT -s localhost.localdomain -p tcp --dport ssh -j ACCEPT +ip6tables -t filter -A INPUT -s 192.0.2.21 -p tcp --dport ssh -j ACCEPT +ip6tables -t filter -A INPUT -s 2001:db8::21 -p tcp --dport ssh -j ACCEPT -- 2.11.4.GIT