70acab4bdfb56f94cf277f3533a4d324d68e7752
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
=> 'IPv4orCIDRorAlias',
17 name
=> get_standard_option
('ipset-name'),
29 my ($class, $param) = @_;
31 die "implement this in subclass";
33 #return ($cluster_conf, $fw_conf, $ipset);
37 my ($class, $param, $fw_conf) = @_;
39 die "implement this in subclass";
43 my ($class, $param) = @_;
45 die "implement this in subclass";
49 my ($class, $param, $fw_conf, $ipset) = @_;
51 if (!defined($ipset)) {
52 delete $fw_conf->{ipset
}->{$param->{name
}};
54 $fw_conf->{ipset
}->{$param->{name
}} = $ipset;
57 $class->save_config($param, $fw_conf);
60 my $additional_param_hash = {};
62 sub additional_parameters
{
63 my ($class, $new_value) = @_;
65 if (defined($new_value)) {
66 $additional_param_hash->{$class} = $new_value;
71 my $org = $additional_param_hash->{$class} || {};
72 foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
76 sub register_get_ipset
{
79 my $properties = $class->additional_parameters();
81 $properties->{name
} = $api_properties->{name
};
83 $class->register_method({
87 description
=> "List IPSet content",
88 permissions
=> PVE
::Firewall
::rules_audit_permissions
($class->rule_env()),
90 additionalProperties
=> 0,
91 properties
=> $properties,
109 digest
=> get_standard_option
('pve-config-digest', { optional
=> 0} ),
112 links
=> [ { rel
=> 'child', href
=> "{cidr}" } ],
117 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
119 return PVE
::Firewall
::copy_list_with_digest
($ipset);
123 sub register_delete_ipset
{
126 my $properties = $class->additional_parameters();
128 $properties->{name
} = get_standard_option
('ipset-name');
130 $class->register_method({
131 name
=> 'delete_ipset',
134 description
=> "Delete IPSet",
136 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
138 additionalProperties
=> 0,
139 properties
=> $properties,
141 returns
=> { type
=> 'null' },
145 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
147 die "IPSet '$param->{name}' is not empty\n"
150 $class->save_ipset($param, $fw_conf, undef);
156 sub register_create_ip
{
159 my $properties = $class->additional_parameters();
161 $properties->{name
} = $api_properties->{name
};
162 $properties->{cidr
} = $api_properties->{cidr
};
163 $properties->{nomatch
} = $api_properties->{nomatch
};
164 $properties->{comment
} = $api_properties->{comment
};
166 $class->register_method({
170 description
=> "Add IP or Network to IPSet.",
172 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
174 additionalProperties
=> 0,
175 properties
=> $properties,
177 returns
=> { type
=> "null" },
181 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
183 my $cidr = $param->{cidr
};
185 foreach my $entry (@$ipset) {
186 raise_param_exc
({ cidr
=> "address '$cidr' already exists" })
187 if $entry->{cidr
} eq $cidr;
190 # make sure alias exists (if $cidr is an alias)
191 PVE
::Firewall
::resolve_alias
($cluster_conf, $fw_conf, $cidr);
193 my $data = { cidr
=> $cidr };
195 $data->{nomatch
} = 1 if $param->{nomatch
};
196 $data->{comment
} = $param->{comment
} if $param->{comment
};
198 unshift @$ipset, $data;
200 $class->save_ipset($param, $fw_conf, $ipset);
206 sub register_read_ip
{
209 my $properties = $class->additional_parameters();
211 $properties->{name
} = $api_properties->{name
};
212 $properties->{cidr
} = $api_properties->{cidr
};
214 $class->register_method({
218 description
=> "Read IP or Network settings from IPSet.",
219 permissions
=> PVE
::Firewall
::rules_audit_permissions
($class->rule_env()),
222 additionalProperties
=> 0,
223 properties
=> $properties,
225 returns
=> { type
=> "object" },
229 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
231 my $list = PVE
::Firewall
::copy_list_with_digest
($ipset);
233 foreach my $entry (@$list) {
234 if ($entry->{cidr
} eq $param->{cidr
}) {
239 raise_param_exc
({ cidr
=> "no such IP/Network" });
243 sub register_update_ip
{
246 my $properties = $class->additional_parameters();
248 $properties->{name
} = $api_properties->{name
};
249 $properties->{cidr
} = $api_properties->{cidr
};
250 $properties->{nomatch
} = $api_properties->{nomatch
};
251 $properties->{comment
} = $api_properties->{comment
};
252 $properties->{digest
} = get_standard_option
('pve-config-digest');
254 $class->register_method({
258 description
=> "Update IP or Network settings",
260 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
262 additionalProperties
=> 0,
263 properties
=> $properties,
265 returns
=> { type
=> "null" },
269 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
271 my (undef, $digest) = PVE
::Firewall
::copy_list_with_digest
($ipset);
272 PVE
::Tools
::assert_if_modified
($digest, $param->{digest
});
274 foreach my $entry (@$ipset) {
275 if($entry->{cidr
} eq $param->{cidr
}) {
276 $entry->{nomatch
} = $param->{nomatch
};
277 $entry->{comment
} = $param->{comment
};
278 $class->save_ipset($param, $fw_conf, $ipset);
283 raise_param_exc
({ cidr
=> "no such IP/Network" });
287 sub register_delete_ip
{
290 my $properties = $class->additional_parameters();
292 $properties->{name
} = $api_properties->{name
};
293 $properties->{cidr
} = $api_properties->{cidr
};
294 $properties->{digest
} = get_standard_option
('pve-config-digest');
296 $class->register_method({
300 description
=> "Remove IP or Network from IPSet.",
302 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
304 additionalProperties
=> 0,
305 properties
=> $properties,
307 returns
=> { type
=> "null" },
311 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
313 my (undef, $digest) = PVE
::Firewall
::copy_list_with_digest
($ipset);
314 PVE
::Tools
::assert_if_modified
($digest, $param->{digest
});
318 foreach my $entry (@$ipset) {
319 push @$new, $entry if $entry->{cidr
} ne $param->{cidr
};
322 $class->save_ipset($param, $fw_conf, $new);
328 sub register_handlers
{
331 $class->register_delete_ipset();
332 $class->register_get_ipset();
333 $class->register_create_ip();
334 $class->register_read_ip();
335 $class->register_update_ip();
336 $class->register_delete_ip();
339 package PVE
::API2
::Firewall
::ClusterIPset
;
344 use base
qw(PVE::API2::Firewall::IPSetBase);
347 my ($class, $param) = @_;
353 my ($class, $param) = @_;
355 my $fw_conf = PVE
::Firewall
::load_clusterfw_conf
();
356 my $ipset = $fw_conf->{ipset
}->{$param->{name
}};
357 die "no such IPSet '$param->{name}'\n" if !defined($ipset);
359 return (undef, $fw_conf, $ipset);
363 my ($class, $param, $fw_conf) = @_;
365 PVE
::Firewall
::save_clusterfw_conf
($fw_conf);
368 __PACKAGE__-
>register_handlers();
370 package PVE
::API2
::Firewall
::VMIPset
;
374 use PVE
::JSONSchema
qw(get_standard_option);
376 use base
qw(PVE::API2::Firewall::IPSetBase);
379 my ($class, $param) = @_;
384 __PACKAGE__-
>additional_parameters({
385 node
=> get_standard_option
('pve-node'),
386 vmid
=> get_standard_option
('pve-vmid'),
390 my ($class, $param) = @_;
392 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
393 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'vm', $param->{vmid
});
394 my $ipset = $fw_conf->{ipset
}->{$param->{name
}};
395 die "no such IPSet '$param->{name}'\n" if !defined($ipset);
397 return ($cluster_conf, $fw_conf, $ipset);
401 my ($class, $param, $fw_conf) = @_;
403 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
406 __PACKAGE__-
>register_handlers();
408 package PVE
::API2
::Firewall
::CTIPset
;
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) = @_;
430 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
431 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'ct', $param->{vmid
});
432 my $ipset = $fw_conf->{ipset
}->{$param->{name
}};
433 die "no such IPSet '$param->{name}'\n" if !defined($ipset);
435 return ($cluster_conf, $fw_conf, $ipset);
439 my ($class, $param, $fw_conf) = @_;
441 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
444 __PACKAGE__-
>register_handlers();
446 package PVE
::API2
::Firewall
::BaseIPSetList
;
450 use PVE
::JSONSchema
qw(get_standard_option);
451 use PVE
::Exception
qw(raise_param_exc);
454 use base
qw(PVE::RESTHandler);
457 my ($class, $param) = @_;
459 die "implement this in subclass";
461 #return ($cluster_conf, $fw_conf);
465 my ($class, $param, $fw_conf) = @_;
467 die "implement this in subclass";
471 my ($class, $param) = @_;
473 die "implement this in subclass";
476 my $additional_param_hash_list = {};
478 sub additional_parameters
{
479 my ($class, $new_value) = @_;
481 if (defined($new_value)) {
482 $additional_param_hash_list->{$class} = $new_value;
487 my $org = $additional_param_hash_list->{$class} || {};
488 foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
492 my $get_ipset_list = sub {
496 foreach my $name (keys %{$fw_conf->{ipset
}}) {
500 if (my $comment = $fw_conf->{ipset_comments
}->{$name}) {
501 $data->{comment
} = $comment;
506 my ($list, $digest) = PVE
::Firewall
::copy_list_with_digest
($res);
508 return wantarray ?
($list, $digest) : $list;
514 my $properties = $class->additional_parameters();
516 $class->register_method({
517 name
=> 'ipset_index',
520 description
=> "List IPSets",
521 permissions
=> PVE
::Firewall
::rules_audit_permissions
($class->rule_env()),
523 additionalProperties
=> 0,
524 properties
=> $properties,
531 name
=> get_standard_option
('ipset-name'),
532 digest
=> get_standard_option
('pve-config-digest', { optional
=> 0} ),
539 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
544 my ($cluster_conf, $fw_conf) = $class->load_config($param);
546 return &$get_ipset_list($fw_conf);
550 sub register_create
{
553 my $properties = $class->additional_parameters();
555 $properties->{name
} = get_standard_option
('ipset-name');
557 $properties->{comment
} = { type
=> 'string', optional
=> 1 };
559 $properties->{digest
} = get_standard_option
('pve-config-digest');
561 $properties->{rename} = get_standard_option
('ipset-name', {
562 description
=> "Rename an existing IPSet. You can set 'rename' to the same value as 'name' to update the 'comment' of an existing IPSet.",
565 $class->register_method({
566 name
=> 'create_ipset',
569 description
=> "Create new IPSet",
571 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
573 additionalProperties
=> 0,
574 properties
=> $properties,
576 returns
=> { type
=> 'null' },
580 my ($cluster_conf, $fw_conf) = $class->load_config($param);
582 if ($param->{rename}) {
583 my (undef, $digest) = &$get_ipset_list($fw_conf);
584 PVE
::Tools
::assert_if_modified
($digest, $param->{digest
});
586 raise_param_exc
({ name
=> "IPSet '$param->{rename}' does not exists" })
587 if !$fw_conf->{ipset
}->{$param->{rename}};
589 my $data = delete $fw_conf->{ipset
}->{$param->{rename}};
590 $fw_conf->{ipset
}->{$param->{name
}} = $data;
591 if (my $comment = delete $fw_conf->{ipset_comments
}->{$param->{rename}}) {
592 $fw_conf->{ipset_comments
}->{$param->{name
}} = $comment;
594 $fw_conf->{ipset_comments
}->{$param->{name
}} = $param->{comment
} if defined($param->{comment
});
596 foreach my $name (keys %{$fw_conf->{ipset
}}) {
597 raise_param_exc
({ name
=> "IPSet '$name' already exists" })
598 if $name eq $param->{name
};
601 $fw_conf->{ipset
}->{$param->{name
}} = [];
602 $fw_conf->{ipset_comments
}->{$param->{name
}} = $param->{comment
} if defined($param->{comment
});
605 $class->save_config($param, $fw_conf);
611 sub register_handlers
{
614 $class->register_index();
615 $class->register_create();
618 package PVE
::API2
::Firewall
::ClusterIPSetList
;
624 use base
qw(PVE::API2::Firewall::BaseIPSetList);
627 my ($class, $param) = @_;
633 my ($class, $param) = @_;
635 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
636 return (undef, $cluster_conf);
640 my ($class, $param, $fw_conf) = @_;
642 PVE
::Firewall
::save_clusterfw_conf
($fw_conf);
645 __PACKAGE__-
>register_handlers();
647 __PACKAGE__-
>register_method ({
648 subclass
=> "PVE::API2::Firewall::ClusterIPset",
650 # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
651 fragmentDelimiter
=> '',
654 package PVE
::API2
::Firewall
::VMIPSetList
;
658 use PVE
::JSONSchema
qw(get_standard_option);
661 use base
qw(PVE::API2::Firewall::BaseIPSetList);
663 __PACKAGE__-
>additional_parameters({
664 node
=> get_standard_option
('pve-node'),
665 vmid
=> get_standard_option
('pve-vmid'),
669 my ($class, $param) = @_;
675 my ($class, $param) = @_;
677 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
678 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'vm', $param->{vmid
});
679 return ($cluster_conf, $fw_conf);
683 my ($class, $param, $fw_conf) = @_;
685 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
688 __PACKAGE__-
>register_handlers();
690 __PACKAGE__-
>register_method ({
691 subclass
=> "PVE::API2::Firewall::VMIPset",
693 # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
694 fragmentDelimiter
=> '',
697 package PVE
::API2
::Firewall
::CTIPSetList
;
701 use PVE
::JSONSchema
qw(get_standard_option);
704 use base
qw(PVE::API2::Firewall::BaseIPSetList);
706 __PACKAGE__-
>additional_parameters({
707 node
=> get_standard_option
('pve-node'),
708 vmid
=> get_standard_option
('pve-vmid'),
712 my ($class, $param) = @_;
718 my ($class, $param) = @_;
720 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
721 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'ct', $param->{vmid
});
722 return ($cluster_conf, $fw_conf);
726 my ($class, $param, $fw_conf) = @_;
728 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
731 __PACKAGE__-
>register_handlers();
733 __PACKAGE__-
>register_method ({
734 subclass
=> "PVE::API2::Firewall::CTIPset",
736 # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
737 fragmentDelimiter
=> '',