diff options
23 files changed, 532 insertions, 7 deletions
diff --git a/Makefile.am b/Makefile.am index 62150f4..c771b11 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,9 +8,11 @@ curver_DATA = cfg-version/firewall@3 sbin_SCRIPTS += scripts/firewall/vyatta-firewall.pl sbin_SCRIPTS += scripts/firewall/firewall.init +sbin_SCRIPTS += scripts/firewall/vyatta-ipset.pl -share_perl5_DATA = lib/Vyatta/IpTables/Rule.pm +share_perl5_DATA = lib/Vyatta/IpTables/Rule.pm share_perl5_DATA += lib/Vyatta/IpTables/AddressFilter.pm +share_perl5_DATA += lib/Vyatta/IpTables/IpSet.pm cpiop = find . ! -regex '\(.*~\|.*\.bak\|.*\.swp\|.*\#.*\#\)' -print0 | \ cpio -0pd diff --git a/debian/control b/debian/control index c1d3152..2de8ed1 100644 --- a/debian/control +++ b/debian/control @@ -20,7 +20,8 @@ Depends: sed (>= 4.1.5), busybox, whois, sudo, - snmpd + snmpd, + ipset Suggests: util-linux (>= 2.13-5), net-tools, ethtool, diff --git a/lib/Vyatta/IpTables/AddressFilter.pm b/lib/Vyatta/IpTables/AddressFilter.pm index fe17c09..dacba1a 100755 --- a/lib/Vyatta/IpTables/AddressFilter.pm +++ b/lib/Vyatta/IpTables/AddressFilter.pm @@ -15,13 +15,14 @@ # General Public License for more details. # # This code was originally developed by Vyatta, Inc. -# Portions created by Vyatta are Copyright (C) 2006, 2007, 2008 Vyatta, Inc. +# Portions created by Vyatta are Copyright (C) 2006-2009 Vyatta, Inc. # All Rights Reserved. # **** End License **** package Vyatta::IpTables::AddressFilter; require Vyatta::Config; +require Vyatta::IpTables::IpSet; use Vyatta::Misc qw(getPortRuleString); use Vyatta::TypeChecker; @@ -45,6 +46,9 @@ my %fields = ( _protocol => undef, _src_mac => undef, _ip_version => undef, + _address_group => undef, + _network_group => undef, + _port_group => undef, ); sub new { @@ -93,8 +97,12 @@ sub setup { } } - $self->{_port} = $config->returnValue("port"); - $self->{_src_mac} = $config->returnValue("mac-address"); + $self->{_port} = $config->returnValue("port"); + $self->{_src_mac} = $config->returnValue("mac-address"); + + $self->{_address_group} = $config->returnValue("group address-group"); + $self->{_network_group} = $config->returnValue("group network-group"); + $self->{_port_group} = $config->returnValue("group port-group"); return 0; } @@ -128,8 +136,12 @@ sub setupOrig { } } - $self->{_port} = $config->returnOrigValue("port"); - $self->{_src_mac} = $config->returnValue("mac-address"); + $self->{_port} = $config->returnOrigValue("port"); + $self->{_src_mac} = $config->returnValue("mac-address"); + + $self->{_address_group} = $config->returnOrigValue("group address-group"); + $self->{_network_group} = $config->returnOrigValue("group network-group"); + $self->{_port_group} = $config->returnOrigValue("group port-group"); return 0; } @@ -223,6 +235,30 @@ sub rule { $rule .= ("-m iprange $negate--dst-range $start-$self->{_range_stop} "); } } + # so far ipset only supports IPv4 + if ($self->{_ip_version} eq "ipv4") { + if (defined($self->{_address_group})) { + my $name = $self->{_address_group}; + my $group = new Vyatta::IpTables::IpSet($name, 'address'); + my ($set_rule, $err_str) = $group->rule($self->{_srcdst}); + return ($err_str, ) if ! defined $set_rule; + $rule .= $set_rule; + } + if (defined($self->{_network_group})) { + my $name = $self->{_network_group}; + my $group = new Vyatta::IpTables::IpSet($name, 'network'); + my ($set_rule, $err_str) = $group->rule($self->{_srcdst}); + return ($err_str, ) if ! defined $set_rule; + $rule .= $set_rule; + } + if (defined($self->{_port_group})) { + my $name = $self->{_port_group}; + my $group = new Vyatta::IpTables::IpSet($name, 'port'); + my ($set_rule, $err_str) = $group->rule($self->{_srcdst}); + return ($err_str, ) if ! defined $set_rule; + $rule .= $set_rule; + } + } my ($port_str, $port_err) = getPortRuleString($self->{_port}, $can_use_port, diff --git a/lib/Vyatta/IpTables/IpSet.pm b/lib/Vyatta/IpTables/IpSet.pm new file mode 100755 index 0000000..c8508c6 --- /dev/null +++ b/lib/Vyatta/IpTables/IpSet.pm @@ -0,0 +1,225 @@ +#!/usr/bin/perl +# +# Module: IpSet.pm +# +# **** License **** +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 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. +# +# This code was originally developed by Vyatta, Inc. +# Portions created by Vyatta are Copyright (C) 2009 Vyatta, Inc. +# All Rights Reserved. +# +# Author: Stig Thormodsrud +# Date: January 2009 +# Description: vyatta interface to ipset +# +# **** End License **** +# + +package Vyatta::IpTables::IpSet; + +use Vyatta::Config; +use Vyatta::TypeChecker; +use Vyatta::Misc; +use NetAddr::IP; + +use strict; +use warnings; + +my %fields = ( + _name => undef, + _type => undef, + _debug => undef, +); + +my $logger = 'logger -t IpSet.pm -p local0.warn --'; + +sub new { + my ($that, $name, $type) = @_; + + my $class = ref ($that) || $that; + my $self = { + %fields, + }; + $self->{_name} = $name; + $self->{_type} = $type; + + bless $self, $class; + return $self; +} + +sub debug { + my ($self, $onoff) = @_; + + $self->{_debug} = undef; + $self->{_debug} = 1 if $onoff eq "on"; +} + +sub exists { + my ($self) = @_; + + return 0 if ! defined $self->{_name}; + my $func = (caller(0))[3]; + my $cmd = "ipset -L $self->{_name}"; + my $rc = system("sudo $cmd > /dev/null &>2"); + system("$logger [$func] [$cmd] = [$rc]") if defined $self->{_debug}; + return $rc ? 0 : 1; +} + +sub create { + my ($self) = @_; + + return "Error: undefined group name" if ! defined $self->{_name}; + return "Error: undefined group type" if ! defined $self->{_type}; + return "Error: group [$self->{_name}] already exists" if $self->exists(); + + my $ipset_param; + if ($self->{_type} eq 'address') { + $ipset_param = 'iphash'; + } elsif ($self->{_type} eq 'network') { + $ipset_param = 'nethash'; + } elsif ($self->{_type} eq 'port') { + $ipset_param = 'portmap --from 1 --to 65535'; + } else { + return "Error: invalid group type"; + } + + my $func = (caller(0))[3]; + my $cmd = "ipset -N $self->{_name} $ipset_param"; + my $rc = system("sudo $cmd"); + system("$logger [$func] [$cmd] = [$rc]") if defined $self->{_debug}; + return "Error: call to ipset failed [$rc]" if $rc; + return undef; +} + +sub references { + my ($self) = @_; + + return 0 if ! $self->exists(); + my @lines = `sudo ipset -L $self->{_name}`; + foreach my $line (@lines) { + if ($line =~ /^References:\s+(\d+)$/) { + return $1; + } + } + return 0; +} + +sub delete { + my ($self) = @_; + + return "Error: undefined group name" if ! defined $self->{_name}; + return "Error: group [$self->{_name}] doesn't exists\n" if !$self->exists(); + + my $refs = $self->references(); + return "Error: group [$self->{_name}] still in use.\n" if $refs != 0; + + my $func = (caller(0))[3]; + my $cmd = "ipset -X $self->{_name}"; + my $rc = system("sudo $cmd"); + system("$logger [$func] [$cmd] = [$rc]") if defined $self->{_debug}; + return "Error: call to ipset failed [$rc]" if $rc; + return undef; +} + +sub check_member { + my ($self, $member) = @_; + + return "Error: undefined group name" if ! defined $self->{_name}; + return "Error: undefined group type" if ! defined $self->{_type}; + + if ($self->{_type} eq 'address') { + if (!Vyatta::TypeChecker::validateType('ipv4', $member, 1)) { + return "Error: [$member] isn't valid IPv4 address\n"; + } + } elsif ($self->{_type} eq 'network') { + if (!Vyatta::TypeChecker::validateType('ipv4net', $member, 1)) { + return "Error: [$member] isn't valid IPv4 network\n"; + } + } elsif ($self->{_type} eq 'port') { + if ($member =~ /^\d/) { + my ($success, $err) = Vyatta::Misc::isValidPortNumber($member); + if (!defined $success) { + return "Error: [$member] isn't valid port number\n"; + } + } else { + my ($success, $err) = Vyatta::Misc::isValidPortName($member); + if (!defined $success) { + return "Error: [$member] isn't valid port name\n"; + } + } + } else { + return "Error: invalid set type [$self->{_type}]"; + } + return undef; +} + +sub member_exists { + my ($self, $member) = @_; + + my $func = (caller(0))[3]; + my $cmd = "ipset -T $self->{_name} $member -q"; + my $rc = system("sudo $cmd"); + system("$logger [$func] [$cmd] = [$rc]") if defined $self->{_debug}; + return $rc ? 0 : 1; +} + +sub add_member { + my ($self, $member) = @_; + + return "Error: undefined group name" if ! defined $self->{_name}; + return "Error: group [$self->{_name}] doesn't exists\n" if !$self->exists(); + + if ($self->member_exists($member)) { + return "Error: member [$member] already exists in [$self->{_name}]\n"; + } + my $func = (caller(0))[3]; + my $cmd = "ipset -A $self->{_name} $member"; + my $rc = system("sudo $cmd"); + system("$logger [$func] [$cmd] = [$rc]") if defined $self->{_debug}; + return "Error: call to ipset failed [$rc]" if $rc; + return undef; +} + +sub delete_member { + my ($self, $member) = @_; + + return "Error: undefined group name" if ! defined $self->{_name}; + return "Error: group [$self->{_name}] doesn't exists\n" if !$self->exists(); + + if (!$self->member_exists($member)) { + return "Error: member [$member] doesn't exists in [$self->{_name}]\n"; + } + my $func = (caller(0))[3]; + my $cmd = "ipset -D $self->{_name} $member"; + my $rc = system("sudo $cmd"); + system("$logger [$func] [$cmd] = [$rc]") if defined $self->{_debug}; + return "Error: call to ipset failed [$rc]" if $rc; + return undef; +} + +sub rule { + my ($self, $direction) = @_; + + if (! $self->exists()) { + my $rc = $self->create(); + return (undef, $rc) if $rc; + } + + my $srcdst; + my $grp = $self->{_name}; + $srcdst = 'src' if $direction eq 'source'; + $srcdst = 'dst' if $direction eq 'destination'; + + return (undef, "Invalid direction [$direction]") if ! defined $srcdst; + return ("-m set --set $grp $srcdst ", ); +} + +1; diff --git a/scripts/firewall/vyatta-ipset.pl b/scripts/firewall/vyatta-ipset.pl new file mode 100755 index 0000000..0c0ee86 --- /dev/null +++ b/scripts/firewall/vyatta-ipset.pl @@ -0,0 +1,112 @@ +#!/usr/bin/perl +# +# Module: vyatta-ipset.pl +# +# **** License **** +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 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. +# +# This code was originally developed by Vyatta, Inc. +# Portions created by Vyatta are Copyright (C) 2009 Vyatta, Inc. +# All Rights Reserved. +# +# Author: Stig Thormodsrud +# Date: January 2009 +# Description: Script to configure ipset to support firewall groups +# +# **** End License **** +# + +use Getopt::Long; +use POSIX; + +use lib "/opt/vyatta/share/perl5"; +use Vyatta::Config; +use Vyatta::TypeChecker; +use Vyatta::Misc; +use Vyatta::IpTables::IpSet; + +use warnings; +use strict; + + +sub ipset_create { + my ($set_name, $set_type) = @_; + + my $group = new Vyatta::IpTables::IpSet($set_name, $set_type); + + return $group->create(); + +} + +sub ipset_delete { + my $set_name = shift; + + my $group = new Vyatta::IpTables::IpSet($set_name); + return $group->delete(); +} + +sub ipset_check_member { + my ($set_name, $set_type, $member) = @_; + + die "undefined type or member" if ! defined $set_type or ! defined $member; + + my $group = new Vyatta::IpTables::IpSet($set_name, $set_type); + return $group->check_member($member); +} + +sub ipset_add_member { + my ($set_name, $member) = @_; + + die "Error: undefined member" if ! defined $member; + my $group = new Vyatta::IpTables::IpSet($set_name); + return $group->add_member($member); +} + +sub ipset_delete_member { + my ($set_name, $member) = @_; + + die "Error: undefined member" if ! defined $member; + my $group = new Vyatta::IpTables::IpSet($set_name); + return $group->delete_member($member); +} + + +# +# main +# +my ($action, $set_name, $set_type, $member); + +GetOptions("action=s" => \$action, + "set-name=s" => \$set_name, + "set-type=s" => \$set_type, + "member=s" => \$member, +); + +die "undefined action" if ! defined $action; + +my $rc; +$rc = ipset_create($set_name, $set_type) if $action eq 'create-set'; + +$rc = ipset_delete($set_name) if $action eq 'delete-set'; + +$rc = ipset_check_member($set_name, $set_type, $member) + if $action eq 'check-member'; + +$rc = ipset_add_member($set_name, $member) if $action eq 'add-member'; + +$rc = ipset_delete_member($set_name, $member) if $action eq 'delete-member'; + +if (defined $rc) { + print $rc; + exit 1; +} +exit 0; + +# end of file diff --git a/templates/firewall/group/address-group/node.def b/templates/firewall/group/address-group/node.def new file mode 100644 index 0000000..bc4fb68 --- /dev/null +++ b/templates/firewall/group/address-group/node.def @@ -0,0 +1,24 @@ +tag: +type: txt +help: Set a firewall address-group + +syntax:expression: exec " \ + if [ `echo $VAR(@) | wc -c` -gt 31 ]; then \ + echo group name must be 31 characters or less;\ + exit 1 ; \ + fi ; " + +syntax:expression: pattern $VAR(@) "^[^-]" ; \ + "Firewall group name cannot start with \"-\"" + +create: sudo /opt/vyatta/sbin/vyatta-ipset.pl \ + --action=create-set \ + --set-type=address \ + --set-name="$VAR(@)" + + +delete: sudo /opt/vyatta/sbin/vyatta-ipset.pl \ + --action=delete-set \ + --set-name="$VAR(@)" + +comp_help: Enter the name of the firewall address-group diff --git a/templates/firewall/group/address-group/node.tag/address/node.def b/templates/firewall/group/address-group/node.tag/address/node.def new file mode 100644 index 0000000..e0f8026 --- /dev/null +++ b/templates/firewall/group/address-group/node.tag/address/node.def @@ -0,0 +1,17 @@ +multi: +type: ipv4 +help: Set a address-group member + +syntax:expression: exec "/opt/vyatta/sbin/vyatta-ipset.pl \ + --action=check-member \ + --set-name=$VAR(../@) \ + --set-type=address \ + --member=\"$VAR(@)\"; " + +create: sudo /opt/vyatta/sbin/vyatta-ipset.pl --action=add-member \ + --set-name=$VAR(../@) \ + --member="$VAR(@) " + +delete: sudo /opt/vyatta/sbin/vyatta-ipset.pl --action=delete-member \ + --set-name=$VAR(../@) \ + --member="$VAR(@) " diff --git a/templates/firewall/group/address-group/node.tag/description/node.def b/templates/firewall/group/address-group/node.tag/description/node.def new file mode 100644 index 0000000..05f7e51 --- /dev/null +++ b/templates/firewall/group/address-group/node.tag/description/node.def @@ -0,0 +1,2 @@ +type: txt +help: Set address-group description diff --git a/templates/firewall/group/network-group/node.def b/templates/firewall/group/network-group/node.def new file mode 100644 index 0000000..2d8bf60 --- /dev/null +++ b/templates/firewall/group/network-group/node.def @@ -0,0 +1,24 @@ +tag: +type: txt +help: Set a firewall network-group + +syntax:expression: exec " \ + if [ `echo $VAR(@) | wc -c` -gt 31 ]; then \ + echo group name must be 31 characters or less;\ + exit 1 ; \ + fi ; " + +syntax:expression: pattern $VAR(@) "^[^-]" ; \ + "Firewall group name cannot start with \"-\"" + +create: sudo /opt/vyatta/sbin/vyatta-ipset.pl \ + --action=create-set \ + --set-type=network \ + --set-name="$VAR(@)" + + +delete: sudo /opt/vyatta/sbin/vyatta-ipset.pl \ + --action=delete-set \ + --set-name="$VAR(@)" + +comp_help: Enter the name of the firewall network-group diff --git a/templates/firewall/group/network-group/node.tag/description/node.def b/templates/firewall/group/network-group/node.tag/description/node.def new file mode 100644 index 0000000..3c50208 --- /dev/null +++ b/templates/firewall/group/network-group/node.tag/description/node.def @@ -0,0 +1,2 @@ +type: txt +help: Set network-group description diff --git a/templates/firewall/group/network-group/node.tag/network/node.def b/templates/firewall/group/network-group/node.tag/network/node.def new file mode 100644 index 0000000..1f33ba9 --- /dev/null +++ b/templates/firewall/group/network-group/node.tag/network/node.def @@ -0,0 +1,20 @@ +multi: +type: ipv4net +help: Set a network-group member + +syntax:expression: exec "/opt/vyatta/sbin/vyatta-ipset.pl \ + --action=check-member \ + --set-name=$VAR(../@) \ + --set-type=network \ + --member=\"$VAR(@)\"; " + +syntax:expression: exec " \ + /opt/vyatta/sbin/check_prefix_boundary $VAR(@)" \ + +create: sudo /opt/vyatta/sbin/vyatta-ipset.pl --action=add-member \ + --set-name=$VAR(../@) \ + --member="$VAR(@) " + +delete: sudo /opt/vyatta/sbin/vyatta-ipset.pl --action=delete-member \ + --set-name=$VAR(../@) \ + --member="$VAR(@) " diff --git a/templates/firewall/group/node.def b/templates/firewall/group/node.def new file mode 100644 index 0000000..d45d3d9 --- /dev/null +++ b/templates/firewall/group/node.def @@ -0,0 +1,3 @@ +help: Set a firewall group + +comp_help: Enter the name of the firewall group diff --git a/templates/firewall/group/port-group/node.def b/templates/firewall/group/port-group/node.def new file mode 100644 index 0000000..0ec803f --- /dev/null +++ b/templates/firewall/group/port-group/node.def @@ -0,0 +1,24 @@ +tag: +type: txt +help: Set a firewall port-group + +syntax:expression: exec " \ + if [ `echo $VAR(@) | wc -c` -gt 31 ]; then \ + echo group name must be 31 characters or less;\ + exit 1 ; \ + fi ; " + +syntax:expression: pattern $VAR(@) "^[^-]" ; \ + "Firewall group name cannot start with \"-\"" + +create: sudo /opt/vyatta/sbin/vyatta-ipset.pl \ + --action=create-set \ + --set-type=port \ + --set-name="$VAR(@)" + + +delete: sudo /opt/vyatta/sbin/vyatta-ipset.pl \ + --action=delete-set \ + --set-name="$VAR(@)" + +comp_help: Enter the name of the firewall port-group diff --git a/templates/firewall/group/port-group/node.tag/description/node.def b/templates/firewall/group/port-group/node.tag/description/node.def new file mode 100644 index 0000000..90124a9 --- /dev/null +++ b/templates/firewall/group/port-group/node.tag/description/node.def @@ -0,0 +1,2 @@ +type: txt +help: Set port-group description diff --git a/templates/firewall/group/port-group/node.tag/port/node.def b/templates/firewall/group/port-group/node.tag/port/node.def new file mode 100644 index 0000000..3f9c530 --- /dev/null +++ b/templates/firewall/group/port-group/node.tag/port/node.def @@ -0,0 +1,17 @@ +multi: +type: txt +help: Set a port-group member + +syntax:expression: exec "/opt/vyatta/sbin/vyatta-ipset.pl \ + --action=check-member \ + --set-name=$VAR(../@) \ + --set-type=port \ + --member=\"$VAR(@)\"; " + +create: sudo /opt/vyatta/sbin/vyatta-ipset.pl --action=add-member \ + --set-name=$VAR(../@) \ + --member="$VAR(@) " + +delete: sudo /opt/vyatta/sbin/vyatta-ipset.pl --action=delete-member \ + --set-name=$VAR(../@) \ + --member="$VAR(@) " diff --git a/templates/firewall/name/node.tag/rule/node.tag/destination/group/address-group/node.def b/templates/firewall/name/node.tag/rule/node.tag/destination/group/address-group/node.def new file mode 100644 index 0000000..51953bb --- /dev/null +++ b/templates/firewall/name/node.tag/rule/node.tag/destination/group/address-group/node.def @@ -0,0 +1,2 @@ +type: txt +help: Set group of addresses diff --git a/templates/firewall/name/node.tag/rule/node.tag/destination/group/network-group/node.def b/templates/firewall/name/node.tag/rule/node.tag/destination/group/network-group/node.def new file mode 100644 index 0000000..cd91233 --- /dev/null +++ b/templates/firewall/name/node.tag/rule/node.tag/destination/group/network-group/node.def @@ -0,0 +1,2 @@ +type: txt +help: Set group of networks diff --git a/templates/firewall/name/node.tag/rule/node.tag/destination/group/node.def b/templates/firewall/name/node.tag/rule/node.tag/destination/group/node.def new file mode 100644 index 0000000..f3d9347 --- /dev/null +++ b/templates/firewall/name/node.tag/rule/node.tag/destination/group/node.def @@ -0,0 +1 @@ +help: Set group to match diff --git a/templates/firewall/name/node.tag/rule/node.tag/destination/group/port-group/node.def b/templates/firewall/name/node.tag/rule/node.tag/destination/group/port-group/node.def new file mode 100644 index 0000000..c9ec6ac --- /dev/null +++ b/templates/firewall/name/node.tag/rule/node.tag/destination/group/port-group/node.def @@ -0,0 +1,2 @@ +type: txt +help: Set group of ports diff --git a/templates/firewall/name/node.tag/rule/node.tag/source/group/address-group/node.def b/templates/firewall/name/node.tag/rule/node.tag/source/group/address-group/node.def new file mode 100644 index 0000000..51953bb --- /dev/null +++ b/templates/firewall/name/node.tag/rule/node.tag/source/group/address-group/node.def @@ -0,0 +1,2 @@ +type: txt +help: Set group of addresses diff --git a/templates/firewall/name/node.tag/rule/node.tag/source/group/network-group/node.def b/templates/firewall/name/node.tag/rule/node.tag/source/group/network-group/node.def new file mode 100644 index 0000000..cd91233 --- /dev/null +++ b/templates/firewall/name/node.tag/rule/node.tag/source/group/network-group/node.def @@ -0,0 +1,2 @@ +type: txt +help: Set group of networks diff --git a/templates/firewall/name/node.tag/rule/node.tag/source/group/node.def b/templates/firewall/name/node.tag/rule/node.tag/source/group/node.def new file mode 100644 index 0000000..f3d9347 --- /dev/null +++ b/templates/firewall/name/node.tag/rule/node.tag/source/group/node.def @@ -0,0 +1 @@ +help: Set group to match diff --git a/templates/firewall/name/node.tag/rule/node.tag/source/group/port-group/node.def b/templates/firewall/name/node.tag/rule/node.tag/source/group/port-group/node.def new file mode 100644 index 0000000..c9ec6ac --- /dev/null +++ b/templates/firewall/name/node.tag/rule/node.tag/source/group/port-group/node.def @@ -0,0 +1,2 @@ +type: txt +help: Set group of ports |