]>
git.proxmox.com Git - pve-firewall.git/blob - src/PVE/API2/Firewall/IPSet.pm
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) = @_;
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 raise_param_exc
({ cidr
=> "a zero prefix is not allowed in ipset entries" })
193 # make sure alias exists (if $cidr is an alias)
194 PVE
::Firewall
::resolve_alias
($cluster_conf, $fw_conf, $cidr)
195 if $cidr =~ m/^${PVE::Firewall::ip_alias_pattern}$/;
197 my $data = { cidr
=> $cidr };
199 $data->{nomatch
} = 1 if $param->{nomatch
};
200 $data->{comment
} = $param->{comment
} if $param->{comment
};
202 unshift @$ipset, $data;
204 $class->save_ipset($param, $fw_conf, $ipset);
210 sub register_read_ip
{
213 my $properties = $class->additional_parameters();
215 $properties->{name
} = $api_properties->{name
};
216 $properties->{cidr
} = $api_properties->{cidr
};
218 $class->register_method({
222 description
=> "Read IP or Network settings from IPSet.",
223 permissions
=> PVE
::Firewall
::rules_audit_permissions
($class->rule_env()),
226 additionalProperties
=> 0,
227 properties
=> $properties,
229 returns
=> { type
=> "object" },
233 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
235 my $list = PVE
::Firewall
::copy_list_with_digest
($ipset);
237 foreach my $entry (@$list) {
238 if ($entry->{cidr
} eq $param->{cidr
}) {
243 raise_param_exc
({ cidr
=> "no such IP/Network" });
247 sub register_update_ip
{
250 my $properties = $class->additional_parameters();
252 $properties->{name
} = $api_properties->{name
};
253 $properties->{cidr
} = $api_properties->{cidr
};
254 $properties->{nomatch
} = $api_properties->{nomatch
};
255 $properties->{comment
} = $api_properties->{comment
};
256 $properties->{digest
} = get_standard_option
('pve-config-digest');
258 $class->register_method({
262 description
=> "Update IP or Network settings",
264 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
266 additionalProperties
=> 0,
267 properties
=> $properties,
269 returns
=> { type
=> "null" },
273 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
275 my (undef, $digest) = PVE
::Firewall
::copy_list_with_digest
($ipset);
276 PVE
::Tools
::assert_if_modified
($digest, $param->{digest
});
278 foreach my $entry (@$ipset) {
279 if($entry->{cidr
} eq $param->{cidr
}) {
280 $entry->{nomatch
} = $param->{nomatch
};
281 $entry->{comment
} = $param->{comment
};
282 $class->save_ipset($param, $fw_conf, $ipset);
287 raise_param_exc
({ cidr
=> "no such IP/Network" });
291 sub register_delete_ip
{
294 my $properties = $class->additional_parameters();
296 $properties->{name
} = $api_properties->{name
};
297 $properties->{cidr
} = $api_properties->{cidr
};
298 $properties->{digest
} = get_standard_option
('pve-config-digest');
300 $class->register_method({
304 description
=> "Remove IP or Network from IPSet.",
306 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
308 additionalProperties
=> 0,
309 properties
=> $properties,
311 returns
=> { type
=> "null" },
315 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
317 my (undef, $digest) = PVE
::Firewall
::copy_list_with_digest
($ipset);
318 PVE
::Tools
::assert_if_modified
($digest, $param->{digest
});
322 foreach my $entry (@$ipset) {
323 push @$new, $entry if $entry->{cidr
} ne $param->{cidr
};
326 $class->save_ipset($param, $fw_conf, $new);
332 sub register_handlers
{
335 $class->register_delete_ipset();
336 $class->register_get_ipset();
337 $class->register_create_ip();
338 $class->register_read_ip();
339 $class->register_update_ip();
340 $class->register_delete_ip();
343 package PVE
::API2
::Firewall
::ClusterIPset
;
348 use base
qw(PVE::API2::Firewall::IPSetBase);
351 my ($class, $param) = @_;
357 my ($class, $param) = @_;
359 my $fw_conf = PVE
::Firewall
::load_clusterfw_conf
();
360 my $ipset = $fw_conf->{ipset
}->{$param->{name
}};
361 die "no such IPSet '$param->{name}'\n" if !defined($ipset);
363 return (undef, $fw_conf, $ipset);
367 my ($class, $param, $fw_conf) = @_;
369 PVE
::Firewall
::save_clusterfw_conf
($fw_conf);
372 __PACKAGE__-
>register_handlers();
374 package PVE
::API2
::Firewall
::VMIPset
;
378 use PVE
::JSONSchema
qw(get_standard_option);
380 use base
qw(PVE::API2::Firewall::IPSetBase);
383 my ($class, $param) = @_;
388 __PACKAGE__-
>additional_parameters({
389 node
=> get_standard_option
('pve-node'),
390 vmid
=> get_standard_option
('pve-vmid'),
394 my ($class, $param) = @_;
396 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
397 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'vm', $param->{vmid
});
398 my $ipset = $fw_conf->{ipset
}->{$param->{name
}};
399 die "no such IPSet '$param->{name}'\n" if !defined($ipset);
401 return ($cluster_conf, $fw_conf, $ipset);
405 my ($class, $param, $fw_conf) = @_;
407 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
410 __PACKAGE__-
>register_handlers();
412 package PVE
::API2
::Firewall
::CTIPset
;
416 use PVE
::JSONSchema
qw(get_standard_option);
418 use base
qw(PVE::API2::Firewall::IPSetBase);
421 my ($class, $param) = @_;
426 __PACKAGE__-
>additional_parameters({
427 node
=> get_standard_option
('pve-node'),
428 vmid
=> get_standard_option
('pve-vmid'),
432 my ($class, $param) = @_;
434 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
435 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'ct', $param->{vmid
});
436 my $ipset = $fw_conf->{ipset
}->{$param->{name
}};
437 die "no such IPSet '$param->{name}'\n" if !defined($ipset);
439 return ($cluster_conf, $fw_conf, $ipset);
443 my ($class, $param, $fw_conf) = @_;
445 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
448 __PACKAGE__-
>register_handlers();
450 package PVE
::API2
::Firewall
::BaseIPSetList
;
454 use PVE
::JSONSchema
qw(get_standard_option);
455 use PVE
::Exception
qw(raise_param_exc);
458 use base
qw(PVE::RESTHandler);
461 my ($class, $param) = @_;
463 die "implement this in subclass";
465 #return ($cluster_conf, $fw_conf);
469 my ($class, $param, $fw_conf) = @_;
471 die "implement this in subclass";
475 my ($class, $param) = @_;
477 die "implement this in subclass";
480 my $additional_param_hash_list = {};
482 sub additional_parameters
{
483 my ($class, $new_value) = @_;
485 if (defined($new_value)) {
486 $additional_param_hash_list->{$class} = $new_value;
491 my $org = $additional_param_hash_list->{$class} || {};
492 foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
496 my $get_ipset_list = sub {
500 foreach my $name (sort keys %{$fw_conf->{ipset
}}) {
504 if (my $comment = $fw_conf->{ipset_comments
}->{$name}) {
505 $data->{comment
} = $comment;
510 my ($list, $digest) = PVE
::Firewall
::copy_list_with_digest
($res);
512 return wantarray ?
($list, $digest) : $list;
518 my $properties = $class->additional_parameters();
520 $class->register_method({
521 name
=> 'ipset_index',
524 description
=> "List IPSets",
525 permissions
=> PVE
::Firewall
::rules_audit_permissions
($class->rule_env()),
527 additionalProperties
=> 0,
528 properties
=> $properties,
535 name
=> get_standard_option
('ipset-name'),
536 digest
=> get_standard_option
('pve-config-digest', { optional
=> 0} ),
543 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
548 my ($cluster_conf, $fw_conf) = $class->load_config($param);
550 return &$get_ipset_list($fw_conf);
554 sub register_create
{
557 my $properties = $class->additional_parameters();
559 $properties->{name
} = get_standard_option
('ipset-name');
561 $properties->{comment
} = { type
=> 'string', optional
=> 1 };
563 $properties->{digest
} = get_standard_option
('pve-config-digest');
565 $properties->{rename} = get_standard_option
('ipset-name', {
566 description
=> "Rename an existing IPSet. You can set 'rename' to the same value as 'name' to update the 'comment' of an existing IPSet.",
569 $class->register_method({
570 name
=> 'create_ipset',
573 description
=> "Create new IPSet",
575 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
577 additionalProperties
=> 0,
578 properties
=> $properties,
580 returns
=> { type
=> 'null' },
584 my ($cluster_conf, $fw_conf) = $class->load_config($param);
586 if ($param->{rename}) {
587 my (undef, $digest) = &$get_ipset_list($fw_conf);
588 PVE
::Tools
::assert_if_modified
($digest, $param->{digest
});
590 raise_param_exc
({ name
=> "IPSet '$param->{rename}' does not exists" })
591 if !$fw_conf->{ipset
}->{$param->{rename}};
593 # prevent overwriting existing ipset
594 raise_param_exc
({ name
=> "IPSet '$param->{name}' does already exist"})
595 if $fw_conf->{ipset
}->{$param->{name
}} &&
596 $param->{name
} ne $param->{rename};
598 my $data = delete $fw_conf->{ipset
}->{$param->{rename}};
599 $fw_conf->{ipset
}->{$param->{name
}} = $data;
600 if (my $comment = delete $fw_conf->{ipset_comments
}->{$param->{rename}}) {
601 $fw_conf->{ipset_comments
}->{$param->{name
}} = $comment;
603 $fw_conf->{ipset_comments
}->{$param->{name
}} = $param->{comment
} if defined($param->{comment
});
605 foreach my $name (keys %{$fw_conf->{ipset
}}) {
606 raise_param_exc
({ name
=> "IPSet '$name' already exists" })
607 if $name eq $param->{name
};
610 $fw_conf->{ipset
}->{$param->{name
}} = [];
611 $fw_conf->{ipset_comments
}->{$param->{name
}} = $param->{comment
} if defined($param->{comment
});
614 $class->save_config($param, $fw_conf);
620 sub register_handlers
{
623 $class->register_index();
624 $class->register_create();
627 package PVE
::API2
::Firewall
::ClusterIPSetList
;
633 use base
qw(PVE::API2::Firewall::BaseIPSetList);
636 my ($class, $param) = @_;
642 my ($class, $param) = @_;
644 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
645 return (undef, $cluster_conf);
649 my ($class, $param, $fw_conf) = @_;
651 PVE
::Firewall
::save_clusterfw_conf
($fw_conf);
654 __PACKAGE__-
>register_handlers();
656 __PACKAGE__-
>register_method ({
657 subclass
=> "PVE::API2::Firewall::ClusterIPset",
659 # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
660 fragmentDelimiter
=> '',
663 package PVE
::API2
::Firewall
::VMIPSetList
;
667 use PVE
::JSONSchema
qw(get_standard_option);
670 use base
qw(PVE::API2::Firewall::BaseIPSetList);
672 __PACKAGE__-
>additional_parameters({
673 node
=> get_standard_option
('pve-node'),
674 vmid
=> get_standard_option
('pve-vmid'),
678 my ($class, $param) = @_;
684 my ($class, $param) = @_;
686 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
687 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'vm', $param->{vmid
});
688 return ($cluster_conf, $fw_conf);
692 my ($class, $param, $fw_conf) = @_;
694 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
697 __PACKAGE__-
>register_handlers();
699 __PACKAGE__-
>register_method ({
700 subclass
=> "PVE::API2::Firewall::VMIPset",
702 # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
703 fragmentDelimiter
=> '',
706 package PVE
::API2
::Firewall
::CTIPSetList
;
710 use PVE
::JSONSchema
qw(get_standard_option);
713 use base
qw(PVE::API2::Firewall::BaseIPSetList);
715 __PACKAGE__-
>additional_parameters({
716 node
=> get_standard_option
('pve-node'),
717 vmid
=> get_standard_option
('pve-vmid'),
721 my ($class, $param) = @_;
727 my ($class, $param) = @_;
729 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
730 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'ct', $param->{vmid
});
731 return ($cluster_conf, $fw_conf);
735 my ($class, $param, $fw_conf) = @_;
737 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
740 __PACKAGE__-
>register_handlers();
742 __PACKAGE__-
>register_method ({
743 subclass
=> "PVE::API2::Firewall::CTIPset",
745 # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
746 fragmentDelimiter
=> '',