[Ferm] [PATCH RFC] Automatically apply @ipfilter on dual-stack config
Faidon Liambotis
paravoid at debian.org
Thu Jun 13 10:35:34 CEST 2013
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.
---
src/ferm | 60 ++++++++++++++++++++++++++++++++--------
test/misc/address-magic.ferm | 30 ++++++++++++++++++++
test/misc/address-magic.result | 22 ++++++++++++++
3 files changed, 100 insertions(+), 12 deletions(-)
create mode 100644 test/misc/address-magic.ferm
create mode 100644 test/misc/address-magic.result
diff --git a/src/ferm b/src/ferm
index 92a4d88..de8df3d 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
@@ -433,6 +435,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, {};
@@ -1239,15 +1282,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");
@@ -1654,7 +1689,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};
}
@@ -2073,6 +2108,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);
@@ -2200,7 +2236,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 @@
+ at def $VARIABLE_TEST = (192.0.2.6 2001:db8::6);
+ at 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
--
1.7.2.5
More information about the Ferm
mailing list