use base qw(PVE::RESTHandler);
-my $api_properties = {
+my $api_properties = {
cidr => {
description => "Network/IP specification in CIDR format.",
type => 'string', format => 'IPorCIDRorAlias',
},
};
+sub lock_config {
+ my ($class, $param, $code) = @_;
+
+ die "implement this in subclass";
+}
+
sub load_config {
my ($class, $param) = @_;
sub rule_env {
my ($class, $param) = @_;
-
+
die "implement this in subclass";
}
type => 'boolean',
optional => 1,
},
- digest => get_standard_option('pve-config-digest', { optional => 0} ),
+ digest => get_standard_option('pve-config-digest', { optional => 0} ),
},
},
links => [ { rel => 'child', href => "{cidr}" } ],
my $properties = $class->additional_parameters();
$properties->{name} = get_standard_option('ipset-name');
+ $properties->{force} = {
+ type => 'boolean',
+ optional => 1,
+ description => 'Delete all members of the IPSet, if there are any.',
+ };
$class->register_method({
name => 'delete_ipset',
returns => { type => 'null' },
code => sub {
my ($param) = @_;
-
- my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
- die "IPSet '$param->{name}' is not empty\n"
- if scalar(@$ipset);
+ $class->lock_config($param, sub {
+ my ($param) = @_;
+
+ my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
- $class->save_ipset($param, $fw_conf, undef);
+ die "IPSet '$param->{name}' is not empty\n"
+ if scalar(@$ipset) && !$param->{force};
+
+ $class->save_ipset($param, $fw_conf, undef);
+
+ });
return undef;
}});
code => sub {
my ($param) = @_;
- my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
+ $class->lock_config($param, sub {
+ my ($param) = @_;
+
+ my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
+
+ my $cidr = $param->{cidr};
+ if ($cidr =~ m@^(dc/|guest/)?(${PVE::Firewall::ip_alias_pattern})$@) {
+ my $scope = $1 // "";
+ my $alias = $2;
+ # make sure alias exists (if $cidr is an alias)
+ PVE::Firewall::resolve_alias($cluster_conf, $fw_conf, $alias, $scope);
+ } else {
+ $cidr = PVE::Firewall::clean_cidr($cidr);
+ # normalize like config parser, otherwise duplicates might slip through
+ $cidr = PVE::Firewall::parse_ip_or_cidr($cidr);
+ }
+
+ foreach my $entry (@$ipset) {
+ raise_param_exc({ cidr => "address '$cidr' already exists" })
+ if $entry->{cidr} eq $cidr;
+ }
+
+ raise_param_exc({ cidr => "a zero prefix is not allowed in ipset entries" })
+ if $cidr =~ m!/0+$!;
- my $cidr = $param->{cidr};
-
- foreach my $entry (@$ipset) {
- raise_param_exc({ cidr => "address '$cidr' already exists" })
- if $entry->{cidr} eq $cidr;
- }
- # make sure alias exists (if $cidr is an alias)
- PVE::Firewall::resolve_alias($cluster_conf, $fw_conf, $cidr);
+ my $data = { cidr => $cidr };
- my $data = { cidr => $cidr };
+ $data->{nomatch} = 1 if $param->{nomatch};
+ $data->{comment} = $param->{comment} if $param->{comment};
- $data->{nomatch} = 1 if $param->{nomatch};
- $data->{comment} = $param->{comment} if $param->{comment};
+ unshift @$ipset, $data;
- unshift @$ipset, $data;
+ $class->save_ipset($param, $fw_conf, $ipset);
- $class->save_ipset($param, $fw_conf, $ipset);
+ });
return undef;
}});
$properties->{name} = $api_properties->{name};
$properties->{cidr} = $api_properties->{cidr};
-
+
$class->register_method({
name => 'read_ip',
path => '{cidr}',
code => sub {
my ($param) = @_;
- my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
+ my $found = $class->lock_config($param, sub {
+ my ($param) = @_;
- my (undef, $digest) = PVE::Firewall::copy_list_with_digest($ipset);
- PVE::Tools::assert_if_modified($digest, $param->{digest});
+ my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
- foreach my $entry (@$ipset) {
- if($entry->{cidr} eq $param->{cidr}) {
- $entry->{nomatch} = $param->{nomatch};
- $entry->{comment} = $param->{comment};
- $class->save_ipset($param, $fw_conf, $ipset);
- return;
+ my (undef, $digest) = PVE::Firewall::copy_list_with_digest($ipset);
+ PVE::Tools::assert_if_modified($digest, $param->{digest});
+
+ foreach my $entry (@$ipset) {
+ if($entry->{cidr} eq $param->{cidr}) {
+ $entry->{nomatch} = $param->{nomatch};
+ $entry->{comment} = $param->{comment};
+ $class->save_ipset($param, $fw_conf, $ipset);
+ return 1;
+ }
}
- }
+
+ return 0;
+ });
+
+ return if $found;
raise_param_exc({ cidr => "no such IP/Network" });
}});
code => sub {
my ($param) = @_;
- my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
+ $class->lock_config($param, sub {
+ my ($param) = @_;
- my (undef, $digest) = PVE::Firewall::copy_list_with_digest($ipset);
- PVE::Tools::assert_if_modified($digest, $param->{digest});
+ my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
- my $new = [];
-
- foreach my $entry (@$ipset) {
- push @$new, $entry if $entry->{cidr} ne $param->{cidr};
- }
+ my (undef, $digest) = PVE::Firewall::copy_list_with_digest($ipset);
+ PVE::Tools::assert_if_modified($digest, $param->{digest});
+
+ my $new = [];
+
+ foreach my $entry (@$ipset) {
+ push @$new, $entry if $entry->{cidr} ne $param->{cidr};
+ }
+
+ $class->save_ipset($param, $fw_conf, $new);
+ });
- $class->save_ipset($param, $fw_conf, $new);
-
return undef;
}});
}
sub rule_env {
my ($class, $param) = @_;
-
+
return 'cluster';
}
+sub lock_config {
+ my ($class, $param, $code) = @_;
+
+ PVE::Firewall::lock_clusterfw_conf(10, $code, $param);
+}
+
sub load_config {
my ($class, $param) = @_;
sub rule_env {
my ($class, $param) = @_;
-
+
return 'vm';
}
-__PACKAGE__->additional_parameters({
+__PACKAGE__->additional_parameters({
node => get_standard_option('pve-node'),
- vmid => get_standard_option('pve-vmid'),
+ vmid => get_standard_option('pve-vmid'),
});
+sub lock_config {
+ my ($class, $param, $code) = @_;
+
+ PVE::Firewall::lock_vmfw_conf($param->{vmid}, 10, $code, $param);
+}
+
sub load_config {
my ($class, $param) = @_;
sub rule_env {
my ($class, $param) = @_;
-
+
return 'ct';
}
-__PACKAGE__->additional_parameters({
+__PACKAGE__->additional_parameters({
node => get_standard_option('pve-node'),
- vmid => get_standard_option('pve-vmid'),
+ vmid => get_standard_option('pve-vmid'),
});
+sub lock_config {
+ my ($class, $param, $code) = @_;
+
+ PVE::Firewall::lock_vmfw_conf($param->{vmid}, 10, $code, $param);
+}
+
sub load_config {
my ($class, $param) = @_;
use base qw(PVE::RESTHandler);
+sub lock_config {
+ my ($class, $param, $code) = @_;
+
+ die "implement this in subclass";
+}
+
sub load_config {
my ($class, $param) = @_;
-
+
die "implement this in subclass";
#return ($cluster_conf, $fw_conf);
sub rule_env {
my ($class, $param) = @_;
-
+
die "implement this in subclass";
}
my ($fw_conf) = @_;
my $res = [];
- foreach my $name (keys %{$fw_conf->{ipset}}) {
- my $data = {
+ foreach my $name (sort keys %{$fw_conf->{ipset}}) {
+ my $data = {
name => $name,
};
if (my $comment = $fw_conf->{ipset_comments}->{$name}) {
type => 'array',
items => {
type => "object",
- properties => {
+ properties => {
name => get_standard_option('ipset-name'),
digest => get_standard_option('pve-config-digest', { optional => 0} ),
- comment => {
+ comment => {
type => 'string',
optional => 1,
}
},
code => sub {
my ($param) = @_;
-
+
my ($cluster_conf, $fw_conf) = $class->load_config($param);
- return &$get_ipset_list($fw_conf);
+ return &$get_ipset_list($fw_conf);
}});
}
returns => { type => 'null' },
code => sub {
my ($param) = @_;
-
- my ($cluster_conf, $fw_conf) = $class->load_config($param);
- if ($param->{rename}) {
- my (undef, $digest) = &$get_ipset_list($fw_conf);
- PVE::Tools::assert_if_modified($digest, $param->{digest});
+ $class->lock_config($param, sub {
+ my ($param) = @_;
- raise_param_exc({ name => "IPSet '$param->{rename}' does not exists" })
- if !$fw_conf->{ipset}->{$param->{rename}};
+ my ($cluster_conf, $fw_conf) = $class->load_config($param);
- my $data = delete $fw_conf->{ipset}->{$param->{rename}};
- $fw_conf->{ipset}->{$param->{name}} = $data;
- if (my $comment = delete $fw_conf->{ipset_comments}->{$param->{rename}}) {
- $fw_conf->{ipset_comments}->{$param->{name}} = $comment;
- }
- $fw_conf->{ipset_comments}->{$param->{name}} = $param->{comment} if defined($param->{comment});
- } else {
- foreach my $name (keys %{$fw_conf->{ipset}}) {
- raise_param_exc({ name => "IPSet '$name' already exists" })
- if $name eq $param->{name};
- }
+ if ($param->{rename}) {
+ my (undef, $digest) = &$get_ipset_list($fw_conf);
+ PVE::Tools::assert_if_modified($digest, $param->{digest});
- $fw_conf->{ipset}->{$param->{name}} = [];
- $fw_conf->{ipset_comments}->{$param->{name}} = $param->{comment} if defined($param->{comment});
- }
+ raise_param_exc({ name => "IPSet '$param->{rename}' does not exist" })
+ if !$fw_conf->{ipset}->{$param->{rename}};
+
+ # prevent overwriting existing ipset
+ raise_param_exc({ name => "IPSet '$param->{name}' does already exist"})
+ if $fw_conf->{ipset}->{$param->{name}} &&
+ $param->{name} ne $param->{rename};
- $class->save_config($param, $fw_conf);
+ my $data = delete $fw_conf->{ipset}->{$param->{rename}};
+ $fw_conf->{ipset}->{$param->{name}} = $data;
+ if (my $comment = delete $fw_conf->{ipset_comments}->{$param->{rename}}) {
+ $fw_conf->{ipset_comments}->{$param->{name}} = $comment;
+ }
+ $fw_conf->{ipset_comments}->{$param->{name}} = $param->{comment} if defined($param->{comment});
+ } else {
+ foreach my $name (keys %{$fw_conf->{ipset}}) {
+ raise_param_exc({ name => "IPSet '$name' already exists" })
+ if $name eq $param->{name};
+ }
+
+ $fw_conf->{ipset}->{$param->{name}} = [];
+ $fw_conf->{ipset_comments}->{$param->{name}} = $param->{comment} if defined($param->{comment});
+ }
+
+ $class->save_config($param, $fw_conf);
+ });
return undef;
}});
sub rule_env {
my ($class, $param) = @_;
-
+
return 'cluster';
}
+sub lock_config {
+ my ($class, $param, $code) = @_;
+
+ PVE::Firewall::lock_clusterfw_conf(10, $code, $param);
+}
+
sub load_config {
my ($class, $param) = @_;
-
+
my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
return (undef, $cluster_conf);
}
__PACKAGE__->register_handlers();
__PACKAGE__->register_method ({
- subclass => "PVE::API2::Firewall::ClusterIPset",
+ subclass => "PVE::API2::Firewall::ClusterIPset",
path => '{name}',
- # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
- fragmentDelimiter => '',
+ # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
+ fragmentDelimiter => '',
});
package PVE::API2::Firewall::VMIPSetList;
use base qw(PVE::API2::Firewall::BaseIPSetList);
-__PACKAGE__->additional_parameters({
+__PACKAGE__->additional_parameters({
node => get_standard_option('pve-node'),
- vmid => get_standard_option('pve-vmid'),
+ vmid => get_standard_option('pve-vmid'),
});
sub rule_env {
my ($class, $param) = @_;
-
+
return 'vm';
}
+sub lock_config {
+ my ($class, $param, $code) = @_;
+
+ PVE::Firewall::lock_vmfw_conf($param->{vmid}, 10, $code, $param);
+}
+
sub load_config {
my ($class, $param) = @_;
-
+
my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
my $fw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, 'vm', $param->{vmid});
return ($cluster_conf, $fw_conf);
__PACKAGE__->register_handlers();
__PACKAGE__->register_method ({
- subclass => "PVE::API2::Firewall::VMIPset",
+ subclass => "PVE::API2::Firewall::VMIPset",
path => '{name}',
- # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
- fragmentDelimiter => '',
+ # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
+ fragmentDelimiter => '',
});
package PVE::API2::Firewall::CTIPSetList;
use base qw(PVE::API2::Firewall::BaseIPSetList);
-__PACKAGE__->additional_parameters({
+__PACKAGE__->additional_parameters({
node => get_standard_option('pve-node'),
- vmid => get_standard_option('pve-vmid'),
+ vmid => get_standard_option('pve-vmid'),
});
sub rule_env {
my ($class, $param) = @_;
-
+
return 'ct';
}
+sub lock_config {
+ my ($class, $param, $code) = @_;
+
+ PVE::Firewall::lock_vmfw_conf($param->{vmid}, 10, $code, $param);
+}
+
sub load_config {
my ($class, $param) = @_;
-
+
my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
my $fw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, 'ct', $param->{vmid});
return ($cluster_conf, $fw_conf);
__PACKAGE__->register_handlers();
__PACKAGE__->register_method ({
- subclass => "PVE::API2::Firewall::CTIPset",
+ subclass => "PVE::API2::Firewall::CTIPset",
path => '{name}',
- # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
- fragmentDelimiter => '',
+ # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
+ fragmentDelimiter => '',
});
1;