]>
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 # make sure alias exists (if $cidr is an alias)
191 PVE
::Firewall
::resolve_alias
($cluster_conf, $fw_conf, $cidr)
192 if $cidr =~ m/^${PVE::Firewall::ip_alias_pattern}$/;
194 my $data = { cidr
=> $cidr };
196 $data->{nomatch
} = 1 if $param->{nomatch
};
197 $data->{comment
} = $param->{comment
} if $param->{comment
};
199 unshift @$ipset, $data;
201 $class->save_ipset($param, $fw_conf, $ipset);
207 sub register_read_ip
{
210 my $properties = $class->additional_parameters();
212 $properties->{name
} = $api_properties->{name
};
213 $properties->{cidr
} = $api_properties->{cidr
};
215 $class->register_method({
219 description
=> "Read IP or Network settings from IPSet.",
220 permissions
=> PVE
::Firewall
::rules_audit_permissions
($class->rule_env()),
223 additionalProperties
=> 0,
224 properties
=> $properties,
226 returns
=> { type
=> "object" },
230 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
232 my $list = PVE
::Firewall
::copy_list_with_digest
($ipset);
234 foreach my $entry (@$list) {
235 if ($entry->{cidr
} eq $param->{cidr
}) {
240 raise_param_exc
({ cidr
=> "no such IP/Network" });
244 sub register_update_ip
{
247 my $properties = $class->additional_parameters();
249 $properties->{name
} = $api_properties->{name
};
250 $properties->{cidr
} = $api_properties->{cidr
};
251 $properties->{nomatch
} = $api_properties->{nomatch
};
252 $properties->{comment
} = $api_properties->{comment
};
253 $properties->{digest
} = get_standard_option
('pve-config-digest');
255 $class->register_method({
259 description
=> "Update IP or Network settings",
261 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
263 additionalProperties
=> 0,
264 properties
=> $properties,
266 returns
=> { type
=> "null" },
270 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
272 my (undef, $digest) = PVE
::Firewall
::copy_list_with_digest
($ipset);
273 PVE
::Tools
::assert_if_modified
($digest, $param->{digest
});
275 foreach my $entry (@$ipset) {
276 if($entry->{cidr
} eq $param->{cidr
}) {
277 $entry->{nomatch
} = $param->{nomatch
};
278 $entry->{comment
} = $param->{comment
};
279 $class->save_ipset($param, $fw_conf, $ipset);
284 raise_param_exc
({ cidr
=> "no such IP/Network" });
288 sub register_delete_ip
{
291 my $properties = $class->additional_parameters();
293 $properties->{name
} = $api_properties->{name
};
294 $properties->{cidr
} = $api_properties->{cidr
};
295 $properties->{digest
} = get_standard_option
('pve-config-digest');
297 $class->register_method({
301 description
=> "Remove IP or Network from IPSet.",
303 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
305 additionalProperties
=> 0,
306 properties
=> $properties,
308 returns
=> { type
=> "null" },
312 my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
314 my (undef, $digest) = PVE
::Firewall
::copy_list_with_digest
($ipset);
315 PVE
::Tools
::assert_if_modified
($digest, $param->{digest
});
319 foreach my $entry (@$ipset) {
320 push @$new, $entry if $entry->{cidr
} ne $param->{cidr
};
323 $class->save_ipset($param, $fw_conf, $new);
329 sub register_handlers
{
332 $class->register_delete_ipset();
333 $class->register_get_ipset();
334 $class->register_create_ip();
335 $class->register_read_ip();
336 $class->register_update_ip();
337 $class->register_delete_ip();
340 package PVE
::API2
::Firewall
::ClusterIPset
;
345 use base
qw(PVE::API2::Firewall::IPSetBase);
348 my ($class, $param) = @_;
354 my ($class, $param) = @_;
356 my $fw_conf = PVE
::Firewall
::load_clusterfw_conf
();
357 my $ipset = $fw_conf->{ipset
}->{$param->{name
}};
358 die "no such IPSet '$param->{name}'\n" if !defined($ipset);
360 return (undef, $fw_conf, $ipset);
364 my ($class, $param, $fw_conf) = @_;
366 PVE
::Firewall
::save_clusterfw_conf
($fw_conf);
369 __PACKAGE__-
>register_handlers();
371 package PVE
::API2
::Firewall
::VMIPset
;
375 use PVE
::JSONSchema
qw(get_standard_option);
377 use base
qw(PVE::API2::Firewall::IPSetBase);
380 my ($class, $param) = @_;
385 __PACKAGE__-
>additional_parameters({
386 node
=> get_standard_option
('pve-node'),
387 vmid
=> get_standard_option
('pve-vmid'),
391 my ($class, $param) = @_;
393 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
394 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'vm', $param->{vmid
});
395 my $ipset = $fw_conf->{ipset
}->{$param->{name
}};
396 die "no such IPSet '$param->{name}'\n" if !defined($ipset);
398 return ($cluster_conf, $fw_conf, $ipset);
402 my ($class, $param, $fw_conf) = @_;
404 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
407 __PACKAGE__-
>register_handlers();
409 package PVE
::API2
::Firewall
::CTIPset
;
413 use PVE
::JSONSchema
qw(get_standard_option);
415 use base
qw(PVE::API2::Firewall::IPSetBase);
418 my ($class, $param) = @_;
423 __PACKAGE__-
>additional_parameters({
424 node
=> get_standard_option
('pve-node'),
425 vmid
=> get_standard_option
('pve-vmid'),
429 my ($class, $param) = @_;
431 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
432 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'ct', $param->{vmid
});
433 my $ipset = $fw_conf->{ipset
}->{$param->{name
}};
434 die "no such IPSet '$param->{name}'\n" if !defined($ipset);
436 return ($cluster_conf, $fw_conf, $ipset);
440 my ($class, $param, $fw_conf) = @_;
442 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
445 __PACKAGE__-
>register_handlers();
447 package PVE
::API2
::Firewall
::BaseIPSetList
;
451 use PVE
::JSONSchema
qw(get_standard_option);
452 use PVE
::Exception
qw(raise_param_exc);
455 use base
qw(PVE::RESTHandler);
458 my ($class, $param) = @_;
460 die "implement this in subclass";
462 #return ($cluster_conf, $fw_conf);
466 my ($class, $param, $fw_conf) = @_;
468 die "implement this in subclass";
472 my ($class, $param) = @_;
474 die "implement this in subclass";
477 my $additional_param_hash_list = {};
479 sub additional_parameters
{
480 my ($class, $new_value) = @_;
482 if (defined($new_value)) {
483 $additional_param_hash_list->{$class} = $new_value;
488 my $org = $additional_param_hash_list->{$class} || {};
489 foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
493 my $get_ipset_list = sub {
497 foreach my $name (keys %{$fw_conf->{ipset
}}) {
501 if (my $comment = $fw_conf->{ipset_comments
}->{$name}) {
502 $data->{comment
} = $comment;
507 my ($list, $digest) = PVE
::Firewall
::copy_list_with_digest
($res);
509 return wantarray ?
($list, $digest) : $list;
515 my $properties = $class->additional_parameters();
517 $class->register_method({
518 name
=> 'ipset_index',
521 description
=> "List IPSets",
522 permissions
=> PVE
::Firewall
::rules_audit_permissions
($class->rule_env()),
524 additionalProperties
=> 0,
525 properties
=> $properties,
532 name
=> get_standard_option
('ipset-name'),
533 digest
=> get_standard_option
('pve-config-digest', { optional
=> 0} ),
540 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
545 my ($cluster_conf, $fw_conf) = $class->load_config($param);
547 return &$get_ipset_list($fw_conf);
551 sub register_create
{
554 my $properties = $class->additional_parameters();
556 $properties->{name
} = get_standard_option
('ipset-name');
558 $properties->{comment
} = { type
=> 'string', optional
=> 1 };
560 $properties->{digest
} = get_standard_option
('pve-config-digest');
562 $properties->{rename} = get_standard_option
('ipset-name', {
563 description
=> "Rename an existing IPSet. You can set 'rename' to the same value as 'name' to update the 'comment' of an existing IPSet.",
566 $class->register_method({
567 name
=> 'create_ipset',
570 description
=> "Create new IPSet",
572 permissions
=> PVE
::Firewall
::rules_modify_permissions
($class->rule_env()),
574 additionalProperties
=> 0,
575 properties
=> $properties,
577 returns
=> { type
=> 'null' },
581 my ($cluster_conf, $fw_conf) = $class->load_config($param);
583 if ($param->{rename}) {
584 my (undef, $digest) = &$get_ipset_list($fw_conf);
585 PVE
::Tools
::assert_if_modified
($digest, $param->{digest
});
587 raise_param_exc
({ name
=> "IPSet '$param->{rename}' does not exists" })
588 if !$fw_conf->{ipset
}->{$param->{rename}};
590 # prevent overwriting existing ipset
591 raise_param_exc
({ name
=> "IPSet '$param->{name}' does already exist"})
592 if $fw_conf->{ipset
}->{$param->{name
}} &&
593 $param->{name
} ne $param->{rename};
595 my $data = delete $fw_conf->{ipset
}->{$param->{rename}};
596 $fw_conf->{ipset
}->{$param->{name
}} = $data;
597 if (my $comment = delete $fw_conf->{ipset_comments
}->{$param->{rename}}) {
598 $fw_conf->{ipset_comments
}->{$param->{name
}} = $comment;
600 $fw_conf->{ipset_comments
}->{$param->{name
}} = $param->{comment
} if defined($param->{comment
});
602 foreach my $name (keys %{$fw_conf->{ipset
}}) {
603 raise_param_exc
({ name
=> "IPSet '$name' already exists" })
604 if $name eq $param->{name
};
607 $fw_conf->{ipset
}->{$param->{name
}} = [];
608 $fw_conf->{ipset_comments
}->{$param->{name
}} = $param->{comment
} if defined($param->{comment
});
611 $class->save_config($param, $fw_conf);
617 sub register_handlers
{
620 $class->register_index();
621 $class->register_create();
624 package PVE
::API2
::Firewall
::ClusterIPSetList
;
630 use base
qw(PVE::API2::Firewall::BaseIPSetList);
633 my ($class, $param) = @_;
639 my ($class, $param) = @_;
641 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
642 return (undef, $cluster_conf);
646 my ($class, $param, $fw_conf) = @_;
648 PVE
::Firewall
::save_clusterfw_conf
($fw_conf);
651 __PACKAGE__-
>register_handlers();
653 __PACKAGE__-
>register_method ({
654 subclass
=> "PVE::API2::Firewall::ClusterIPset",
656 # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
657 fragmentDelimiter
=> '',
660 package PVE
::API2
::Firewall
::VMIPSetList
;
664 use PVE
::JSONSchema
qw(get_standard_option);
667 use base
qw(PVE::API2::Firewall::BaseIPSetList);
669 __PACKAGE__-
>additional_parameters({
670 node
=> get_standard_option
('pve-node'),
671 vmid
=> get_standard_option
('pve-vmid'),
675 my ($class, $param) = @_;
681 my ($class, $param) = @_;
683 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
684 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'vm', $param->{vmid
});
685 return ($cluster_conf, $fw_conf);
689 my ($class, $param, $fw_conf) = @_;
691 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
694 __PACKAGE__-
>register_handlers();
696 __PACKAGE__-
>register_method ({
697 subclass
=> "PVE::API2::Firewall::VMIPset",
699 # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
700 fragmentDelimiter
=> '',
703 package PVE
::API2
::Firewall
::CTIPSetList
;
707 use PVE
::JSONSchema
qw(get_standard_option);
710 use base
qw(PVE::API2::Firewall::BaseIPSetList);
712 __PACKAGE__-
>additional_parameters({
713 node
=> get_standard_option
('pve-node'),
714 vmid
=> get_standard_option
('pve-vmid'),
718 my ($class, $param) = @_;
724 my ($class, $param) = @_;
726 my $cluster_conf = PVE
::Firewall
::load_clusterfw_conf
();
727 my $fw_conf = PVE
::Firewall
::load_vmfw_conf
($cluster_conf, 'ct', $param->{vmid
});
728 return ($cluster_conf, $fw_conf);
732 my ($class, $param, $fw_conf) = @_;
734 PVE
::Firewall
::save_vmfw_conf
($param->{vmid
}, $fw_conf);
737 __PACKAGE__-
>register_handlers();
739 __PACKAGE__-
>register_method ({
740 subclass
=> "PVE::API2::Firewall::CTIPset",
742 # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
743 fragmentDelimiter
=> '',