From: Dietmar Maurer Date: Mon, 7 Apr 2014 10:31:45 +0000 (+0200) Subject: start API for IPSet X-Git-Url: https://git.proxmox.com/?p=pve-firewall.git;a=commitdiff_plain;h=009ee3ac58d8a0e88f7c5f734ce7267f2e5efea7 start API for IPSet --- diff --git a/src/PVE/API2/Firewall/Cluster.pm b/src/PVE/API2/Firewall/Cluster.pm index b76c924..b270010 100644 --- a/src/PVE/API2/Firewall/Cluster.pm +++ b/src/PVE/API2/Firewall/Cluster.pm @@ -8,6 +8,7 @@ use PVE::JSONSchema qw(get_standard_option); use PVE::Firewall; use PVE::API2::Firewall::Rules; use PVE::API2::Firewall::Groups; +use PVE::API2::Firewall::IPSet; #fixme: locking? @@ -178,4 +179,45 @@ __PACKAGE__->register_method({ return $res; }}); +__PACKAGE__->register_method({ + name => 'ipset', + path => 'ipset', + method => 'GET', + description => "List IPSets", + parameters => { + additionalProperties => 0, + }, + returns => { + type => 'array', + items => { + type => "object", + properties => { + name => { + description => "IPSet name.", + type => 'string', + }, + }, + }, + links => [ { rel => 'child', href => "{name}" } ], + }, + code => sub { + my ($param) = @_; + + my $cluster_conf = PVE::Firewall::load_clusterfw_conf(); + + my $res = []; + foreach my $name (keys %{$cluster_conf->{ipset}}) { + push @$res, { name => $name, count => scalar(@{$cluster_conf->{ipset}->{$name}}) }; + } + + return $res; + }}); + +__PACKAGE__->register_method ({ + subclass => "PVE::API2::Firewall::ClusterIPset", + path => 'ipset/{name}', + # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/' + fragmentDelimiter => '', +}); + 1; diff --git a/src/PVE/API2/Firewall/IPSet.pm b/src/PVE/API2/Firewall/IPSet.pm new file mode 100644 index 0000000..e96efc0 --- /dev/null +++ b/src/PVE/API2/Firewall/IPSet.pm @@ -0,0 +1,211 @@ +package PVE::API2::Firewall::IPSetBase; + +use strict; +use warnings; +use PVE::JSONSchema qw(get_standard_option); + +use PVE::Firewall; + +use base qw(PVE::RESTHandler); + +my $api_properties = { + cidr => { + description => "Network/IP specification in CIDR format.", + type => 'string', format => 'IPv4orCIDR', + }, + name => { + description => "IP set name.", + type => 'string', + }, + comment => { + type => 'string', + optional => 1, + }, + nomatch => { + type => 'boolean', + optional => 1, + }, +}; + +sub load_config { + my ($class, $param) = @_; + + die "implement this in subclass"; + + #return ($fw_conf, $rules); +} + +sub save_rules { + my ($class, $param, $fw_conf, $rules) = @_; + + die "implement this in subclass"; +} + +my $additional_param_hash = {}; + +sub additional_parameters { + my ($class, $new_value) = @_; + + if (defined($new_value)) { + $additional_param_hash->{$class} = $new_value; + } + + # return a copy + my $copy = {}; + my $org = $additional_param_hash->{$class} || {}; + foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; } + return $copy; +} + +sub register_get_ipset { + my ($class) = @_; + + my $properties = $class->additional_parameters(); + + $properties->{name} = $api_properties->{name}; + + $class->register_method({ + name => 'get_ipset', + path => '', + method => 'GET', + description => "List IPSet content", + parameters => { + additionalProperties => 0, + properties => $properties, + }, + returns => { + type => 'array', + items => { + type => "object", + properties => { + cidr => { + type => 'string', + }, + comment => { + type => 'string', + optional => 1, + }, + nomatch => { + type => 'boolean', + optional => 1, + }, + }, + }, + links => [ { rel => 'child', href => "{cidr}" } ], + }, + code => sub { + my ($param) = @_; + + my ($fw_conf, $ipset) = $class->load_config($param); + + return $ipset; + }}); +} + +sub register_add_ip { + my ($class) = @_; + + my $properties = $class->additional_parameters(); + + $properties->{name} = $api_properties->{name}; + $properties->{cidr} = $api_properties->{cidr}; + $properties->{nomatch} = $api_properties->{nomatch}; + $properties->{comment} = $api_properties->{comment}; + + $class->register_method({ + name => 'add_ip', + path => '', + method => 'POST', + description => "Add IP or Network to IPSet.", + protected => 1, + parameters => { + additionalProperties => 0, + properties => $properties, + }, + returns => { type => "null" }, + code => sub { + my ($param) = @_; + + my ($fw_conf, $ipset) = $class->load_config($param); + + my $data = { cidr => $param->{cidr} }; + $data->{nomatch} = 1 if $param->{nomatch}; + $data->{comment} = $param->{comment} if $param->{comment}; + + # fixme: verify + + unshift @$ipset, $data; + + $class->save_ipset($param, $fw_conf, $ipset); + + return undef; + }}); +} + +sub register_remove_ip { + my ($class) = @_; + + my $properties = $class->additional_parameters(); + + $properties->{name} = $api_properties->{name}; + $properties->{cidr} = $api_properties->{cidr}; + + $class->register_method({ + name => 'remove_ip', + path => '{cidr}', + method => 'DELETE', + description => "Remove IP or Network from IPSet.", + protected => 1, + parameters => { + additionalProperties => 0, + properties => $properties, + }, + returns => { type => "null" }, + code => sub { + my ($param) = @_; + + my ($fw_conf, $ipset) = $class->load_config($param); + + die "implement me $param->{cidr}"; + + $class->save_ipset($param, $fw_conf, $ipset); + + return undef; + }}); +} + +sub register_handlers { + my ($class) = @_; + + $class->register_get_ipset(); + $class->register_add_ip(); + $class->register_remove_ip(); +} + +package PVE::API2::Firewall::ClusterIPset; + +use strict; +use warnings; + +use base qw(PVE::API2::Firewall::IPSetBase); + +sub load_config { + my ($class, $param) = @_; + + my $fw_conf = PVE::Firewall::load_clusterfw_conf(); + my $ipset = $fw_conf->{ipset}->{$param->{name}}; + die "no such IPSet '$param->{name}'\n" if !defined($ipset); + + return ($fw_conf, $ipset); +} + +sub save_ipset { + my ($class, $param, $fw_conf, $ipset) = @_; + + $fw_conf->{ipset}->{$param->{name}} = $ipset; + PVE::Firewall::save_clusterfw_conf($fw_conf); +} + +__PACKAGE__->register_handlers(); + +1; diff --git a/src/PVE/API2/Firewall/Makefile b/src/PVE/API2/Firewall/Makefile index cb93029..acbf9a2 100644 --- a/src/PVE/API2/Firewall/Makefile +++ b/src/PVE/API2/Firewall/Makefile @@ -1,4 +1,5 @@ LIB_SOURCES= \ + IPSet.pm \ Rules.pm \ Cluster.pm \ Host.pm \ diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index 5168093..a20b995 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -10,7 +10,7 @@ use PVE::Exception qw(raise raise_param_exc); use PVE::JSONSchema qw(get_standard_option); use PVE::Cluster; use PVE::ProcFSTools; -use PVE::Tools; +use PVE::Tools qw($IPV4RE); use File::Basename; use File::Path; use IO::File; @@ -35,6 +35,18 @@ eval { $have_pve_manager = 1; }; +PVE::JSONSchema::register_format('IPv4orCIDR', \&pve_verify_ipv4_or_cidr); +sub pve_verify_ipv4_or_cidr { + my ($cidr, $noerr) = @_; + + if ($cidr =~ m!^(?:$IPV4RE)(/(\d+))?$! && (!$1 || (($2 > 7) && ($2 <= 32)))) { + return $cidr; + } + return undef if $noerr; + die "value does not look like a valid IP address or CIDR network\n"; +} + + my $feature_ipset_nomatch = 0; eval { my (undef, undef, $release) = POSIX::uname();