]>
git.proxmox.com Git - pve-firewall.git/blob - src/PVE/API2/Firewall/IPSet.pm
913dd86b4c58f83cfd711c0dbba32e7b2037fb4f
1 package PVE
::API2
::Firewall
::IPSetBase
;
5 use PVE
::Exception
qw(raise raise_param_exc);
6 use PVE
::JSONSchema
qw(get_standard_option);
10 use base
qw(PVE::RESTHandler);
12 my $api_properties = {
14 description
=> "Network/IP specification in CIDR format.",
15 type
=> 'string', format
=> 'IPorCIDRorAlias',
17 name
=> get_standard_option
('ipset-name'),
29 my ($class, $param, $code) = @_;
31 die "implement this in subclass";
35 my ($class, $param) = @_;
37 die "implement this in subclass";
39 #return ($cluster_conf, $fw_conf, $ipset);
43 my ($class, $param, $fw_conf) = @_;
45 die "implement this in subclass";
49 my ($class, $param) = @_;
51 die "implement this in subclass";
55 my ($class, $param, $fw_conf, $ipset) = @_;
57 if (!defined($ipset)) {
58 delete $fw_conf->{ipset
}->{$param->{name
}};
60 $fw_conf->{ipset
}->{$param->{name
}} = $ipset;
63 $class->save_config($param, $fw_conf);
66 my $additional_param_hash = {};
68 sub additional_parameters
{
69 my ($class, $new_value) = @_;
71 if (defined($new_value)) {
72 $additional_param_hash->{$class} = $new_value;
77 my $org = $additional_param_hash->{$class} || {};
78 foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
82 sub register_get_ipset
{
85 my $properties = $class->additional_parameters();
87 $properties->{name
} = $api_properties->{name
};
89 $class->register_method({
93 description
=> "List IPSet content",
94 permissions
=> PVE
::Firewall
::rules_audit_permissions
($class->rule_env()),
96 additionalProperties
=> 0,
97 properties
=> $properties,
115 digest
=> get_standard_option
('pve-config-digest', { optional
=> 0} ),
118 links
=> [ { rel
=> 'child', href
=> "{cidr}" } ],
123 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
125 return PVE
::Firewall
::copy_list_with_digest
($ipset);
129 sub register_delete_ipset
{
132 my $properties = $class->additional_parameters();
134 $properties->{name
} = get_standard_option
('ipset-name');
136 $class->register_method({
137 name
=> 'delete_ipset',
140 description
=> "Delete IPSet",
142 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
144 additionalProperties
=> 0,
145 properties
=> $properties,
147 returns
=> { type
=> 'null' },
151 $class->lock_config($param, sub {
154 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
156 die "IPSet '$param->{name}' is not empty\n"
159 $class->save_ipset($param, $fw_conf, undef);
167 sub register_create_ip
{
170 my $properties = $class->additional_parameters();
172 $properties->{name
} = $api_properties->{name
};
173 $properties->{cidr
} = $api_properties->{cidr
};
174 $properties->{nomatch
} = $api_properties->{nomatch
};
175 $properties->{comment
} = $api_properties->{comment
};
177 $class->register_method({
181 description
=> "Add IP or Network to IPSet.",
183 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
185 additionalProperties
=> 0,
186 properties
=> $properties,
188 returns
=> { type
=> "null" },
192 $class->lock_config($param, sub {
195 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
197 my $cidr = $param->{cidr
};
199 foreach my $entry (@$ipset) {
200 raise_param_exc
({ cidr
=> "address '$cidr' already exists" })
201 if $entry->{cidr
} eq $cidr;
204 raise_param_exc
({ cidr
=> "a zero prefix is not allowed in ipset entries" })
207 # make sure alias exists (if $cidr is an alias)
208 PVE
::Firewall
::resolve_alias
($cluster_conf, $fw_conf, $cidr)
209 if $cidr =~ m/^${PVE::Firewall::ip_alias_pattern}$/;
211 my $data = { cidr
=> $cidr };
213 $data->{nomatch
} = 1 if $param->{nomatch
};
214 $data->{comment
} = $param->{comment
} if $param->{comment
};
216 unshift @$ipset, $data;
218 $class->save_ipset($param, $fw_conf, $ipset);
226 sub register_read_ip
{
229 my $properties = $class->additional_parameters();
231 $properties->{name
} = $api_properties->{name
};
232 $properties->{cidr
} = $api_properties->{cidr
};
234 $class->register_method({
238 description
=> "Read IP or Network settings from IPSet.",
239 permissions
=> PVE
::Firewall
::rules_audit_permissions
($class->rule_env()),
242 additionalProperties
=> 0,
243 properties
=> $properties,
245 returns
=> { type
=> "object" },
249 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
251 my $list = PVE
::Firewall
::copy_list_with_digest
($ipset);
253 foreach my $entry (@$list) {
254 if ($entry->{cidr
} eq $param->{cidr
}) {
259 raise_param_exc
({ cidr
=> "no such IP/Network" });
263 sub register_update_ip
{
266 my $properties = $class->additional_parameters();
268 $properties->{name
} = $api_properties->{name
};
269 $properties->{cidr
} = $api_properties->{cidr
};
270 $properties->{nomatch
} = $api_properties->{nomatch
};
271 $properties->{comment
} = $api_properties->{comment
};
272 $properties->{digest
} = get_standard_option
('pve-config-digest');
274 $class->register_method({
278 description
=> "Update IP or Network settings",
280 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
282 additionalProperties
=> 0,
283 properties
=> $properties,
285 returns
=> { type
=> "null" },
289 my $found = $class->lock_config($param, sub {
292 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
294 my (undef, $digest) = PVE
::Firewall
::copy_list_with_digest
($ipset);
295 PVE
::Tools
::assert_if_modified
($digest, $param->{digest
});
297 foreach my $entry (@$ipset) {
298 if($entry->{cidr
} eq $param->{cidr
}) {
299 $entry->{nomatch
} = $param->{nomatch
};
300 $entry->{comment
} = $param->{comment
};
301 $class->save_ipset($param, $fw_conf, $ipset);
311 raise_param_exc
({ cidr
=> "no such IP/Network" });
315 sub register_delete_ip
{
318 my $properties = $class->additional_parameters();
320 $properties->{name
} = $api_properties->{name
};
321 $properties->{cidr
} = $api_properties->{cidr
};
322 $properties->{digest
} = get_standard_option
('pve-config-digest');
324 $class->register_method({
328 description
=> "Remove IP or Network from IPSet.",
330 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
332 additionalProperties
=> 0,
333 properties
=> $properties,
335 returns
=> { type
=> "null" },
339 $class->lock_config($param, sub {
342 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
344 my (undef, $digest) = PVE
::Firewall
::copy_list_with_digest
($ipset);
345 PVE
::Tools
::assert_if_modified
($digest, $param->{digest
});
349 foreach my $entry (@$ipset) {
350 push @$new, $entry if $entry->{cidr
} ne $param->{cidr
};
353 $class->save_ipset($param, $fw_conf, $new);
360 sub register_handlers
{
363 $class->register_delete_ipset();
364 $class->register_get_ipset();
365 $class->register_create_ip();
366 $class->register_read_ip();
367 $class->register_update_ip();
368 $class->register_delete_ip();
371 package PVE
::API2
::Firewall
::ClusterIPset
;
376 use base
qw(PVE::API2::Firewall::IPSetBase);
379 my ($class, $param) = @_;
385 my ($class, $param, $code) = @_;
387 PVE
::Firewall
::lock_clusterfw_conf
(10, $code, $param);
391 my ($class, $param) = @_;
393 my $fw_conf = PVE
::Firewall
::load_clusterfw_conf
();
394 my $ipset = $fw_conf->{ipset
}->{$param->{name
}};
395 die "no such IPSet '$param->{name}'\n" if !defined($ipset);
397 return (undef, $fw_conf, $ipset);
401 my ($class, $param, $fw_conf) = @_;
403 PVE
::Firewall
::save_clusterfw_conf
($fw_conf);
406 __PACKAGE__-
>register_handlers();
408 package PVE
::API2
::Firewall
::VMIPset
;
412 use PVE
::JSONSchema
qw(get_standard_option);
414 use base
qw(PVE::API2::Firewall::IPSetBase);
417 my ($class, $param) = @_;
422 __PACKAGE__-
>additional_parameters({
423 node
=> get_standard_option
('pve-node'),
424 vmid
=> get_standard_option
('pve-vmid'),
428 my ($class, $param, $code) = @_;
430 PVE
::Firewall
::lock_vmfw_conf
($param->{vmid
}, 10, $code, $param);
434 my ($class, $param) = @_;
436 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
437 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'vm', $param->{vmid
});
438 my $ipset = $fw_conf->{ipset
}->{$param->{name
}};
439 die "no such IPSet '$param->{name}'\n" if !defined($ipset);
441 return ($cluster_conf, $fw_conf, $ipset);
445 my ($class, $param, $fw_conf) = @_;
447 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
450 __PACKAGE__-
>register_handlers();
452 package PVE
::API2
::Firewall
::CTIPset
;
456 use PVE
::JSONSchema
qw(get_standard_option);
458 use base
qw(PVE::API2::Firewall::IPSetBase);
461 my ($class, $param) = @_;
466 __PACKAGE__-
>additional_parameters({
467 node
=> get_standard_option
('pve-node'),
468 vmid
=> get_standard_option
('pve-vmid'),
472 my ($class, $param, $code) = @_;
474 PVE
::Firewall
::lock_vmfw_conf
($param->{vmid
}, 10, $code, $param);
478 my ($class, $param) = @_;
480 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
481 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'ct', $param->{vmid
});
482 my $ipset = $fw_conf->{ipset
}->{$param->{name
}};
483 die "no such IPSet '$param->{name}'\n" if !defined($ipset);
485 return ($cluster_conf, $fw_conf, $ipset);
489 my ($class, $param, $fw_conf) = @_;
491 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
494 __PACKAGE__-
>register_handlers();
496 package PVE
::API2
::Firewall
::BaseIPSetList
;
500 use PVE
::JSONSchema
qw(get_standard_option);
501 use PVE
::Exception
qw(raise_param_exc);
504 use base
qw(PVE::RESTHandler);
507 my ($class, $param, $code) = @_;
509 die "implement this in subclass";
513 my ($class, $param) = @_;
515 die "implement this in subclass";
517 #return ($cluster_conf, $fw_conf);
521 my ($class, $param, $fw_conf) = @_;
523 die "implement this in subclass";
527 my ($class, $param) = @_;
529 die "implement this in subclass";
532 my $additional_param_hash_list = {};
534 sub additional_parameters
{
535 my ($class, $new_value) = @_;
537 if (defined($new_value)) {
538 $additional_param_hash_list->{$class} = $new_value;
543 my $org = $additional_param_hash_list->{$class} || {};
544 foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
548 my $get_ipset_list = sub {
552 foreach my $name (sort keys %{$fw_conf->{ipset
}}) {
556 if (my $comment = $fw_conf->{ipset_comments
}->{$name}) {
557 $data->{comment
} = $comment;
562 my ($list, $digest) = PVE
::Firewall
::copy_list_with_digest
($res);
564 return wantarray ?
($list, $digest) : $list;
570 my $properties = $class->additional_parameters();
572 $class->register_method({
573 name
=> 'ipset_index',
576 description
=> "List IPSets",
577 permissions
=> PVE
::Firewall
::rules_audit_permissions
($class->rule_env()),
579 additionalProperties
=> 0,
580 properties
=> $properties,
587 name
=> get_standard_option
('ipset-name'),
588 digest
=> get_standard_option
('pve-config-digest', { optional
=> 0} ),
595 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
600 my ($cluster_conf, $fw_conf) = $class->load_config($param);
602 return &$get_ipset_list($fw_conf);
606 sub register_create
{
609 my $properties = $class->additional_parameters();
611 $properties->{name
} = get_standard_option
('ipset-name');
613 $properties->{comment
} = { type
=> 'string', optional
=> 1 };
615 $properties->{digest
} = get_standard_option
('pve-config-digest');
617 $properties->{rename} = get_standard_option
('ipset-name', {
618 description
=> "Rename an existing IPSet. You can set 'rename' to the same value as 'name' to update the 'comment' of an existing IPSet.",
621 $class->register_method({
622 name
=> 'create_ipset',
625 description
=> "Create new IPSet",
627 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
629 additionalProperties
=> 0,
630 properties
=> $properties,
632 returns
=> { type
=> 'null' },
636 $class->lock_config($param, sub {
639 my ($cluster_conf, $fw_conf) = $class->load_config($param);
641 if ($param->{rename}) {
642 my (undef, $digest) = &$get_ipset_list($fw_conf);
643 PVE
::Tools
::assert_if_modified
($digest, $param->{digest
});
645 raise_param_exc
({ name
=> "IPSet '$param->{rename}' does not exist" })
646 if !$fw_conf->{ipset
}->{$param->{rename}};
648 # prevent overwriting existing ipset
649 raise_param_exc
({ name
=> "IPSet '$param->{name}' does already exist"})
650 if $fw_conf->{ipset
}->{$param->{name
}} &&
651 $param->{name
} ne $param->{rename};
653 my $data = delete $fw_conf->{ipset
}->{$param->{rename}};
654 $fw_conf->{ipset
}->{$param->{name
}} = $data;
655 if (my $comment = delete $fw_conf->{ipset_comments
}->{$param->{rename}}) {
656 $fw_conf->{ipset_comments
}->{$param->{name
}} = $comment;
658 $fw_conf->{ipset_comments
}->{$param->{name
}} = $param->{comment
} if defined($param->{comment
});
660 foreach my $name (keys %{$fw_conf->{ipset
}}) {
661 raise_param_exc
({ name
=> "IPSet '$name' already exists" })
662 if $name eq $param->{name
};
665 $fw_conf->{ipset
}->{$param->{name
}} = [];
666 $fw_conf->{ipset_comments
}->{$param->{name
}} = $param->{comment
} if defined($param->{comment
});
669 $class->save_config($param, $fw_conf);
676 sub register_handlers
{
679 $class->register_index();
680 $class->register_create();
683 package PVE
::API2
::Firewall
::ClusterIPSetList
;
689 use base
qw(PVE::API2::Firewall::BaseIPSetList);
692 my ($class, $param) = @_;
698 my ($class, $param, $code) = @_;
700 PVE
::Firewall
::lock_clusterfw_conf
(10, $code, $param);
704 my ($class, $param) = @_;
706 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
707 return (undef, $cluster_conf);
711 my ($class, $param, $fw_conf) = @_;
713 PVE
::Firewall
::save_clusterfw_conf
($fw_conf);
716 __PACKAGE__-
>register_handlers();
718 __PACKAGE__-
>register_method ({
719 subclass
=> "PVE::API2::Firewall::ClusterIPset",
721 # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
722 fragmentDelimiter
=> '',
725 package PVE
::API2
::Firewall
::VMIPSetList
;
729 use PVE
::JSONSchema
qw(get_standard_option);
732 use base
qw(PVE::API2::Firewall::BaseIPSetList);
734 __PACKAGE__-
>additional_parameters({
735 node
=> get_standard_option
('pve-node'),
736 vmid
=> get_standard_option
('pve-vmid'),
740 my ($class, $param) = @_;
746 my ($class, $param, $code) = @_;
748 PVE
::Firewall
::lock_vmfw_conf
($param->{vmid
}, 10, $code, $param);
752 my ($class, $param) = @_;
754 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
755 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'vm', $param->{vmid
});
756 return ($cluster_conf, $fw_conf);
760 my ($class, $param, $fw_conf) = @_;
762 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
765 __PACKAGE__-
>register_handlers();
767 __PACKAGE__-
>register_method ({
768 subclass
=> "PVE::API2::Firewall::VMIPset",
770 # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
771 fragmentDelimiter
=> '',
774 package PVE
::API2
::Firewall
::CTIPSetList
;
778 use PVE
::JSONSchema
qw(get_standard_option);
781 use base
qw(PVE::API2::Firewall::BaseIPSetList);
783 __PACKAGE__-
>additional_parameters({
784 node
=> get_standard_option
('pve-node'),
785 vmid
=> get_standard_option
('pve-vmid'),
789 my ($class, $param) = @_;
795 my ($class, $param, $code) = @_;
797 PVE
::Firewall
::lock_vmfw_conf
($param->{vmid
}, 10, $code, $param);
801 my ($class, $param) = @_;
803 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
804 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'ct', $param->{vmid
});
805 return ($cluster_conf, $fw_conf);
809 my ($class, $param, $fw_conf) = @_;
811 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
814 __PACKAGE__-
>register_handlers();
816 __PACKAGE__-
>register_method ({
817 subclass
=> "PVE::API2::Firewall::CTIPset",
819 # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
820 fragmentDelimiter
=> '',