]> git.proxmox.com Git - pve-firewall.git/blame - src/PVE/API2/Firewall/IPSet.pm
support comments on ipset sections
[pve-firewall.git] / src / PVE / API2 / Firewall / IPSet.pm
CommitLineData
009ee3ac
DM
1package PVE::API2::Firewall::IPSetBase;
2
3use strict;
4use warnings;
4a11bba5 5use PVE::Exception qw(raise raise_param_exc);
009ee3ac
DM
6use PVE::JSONSchema qw(get_standard_option);
7
8use PVE::Firewall;
9
10use base qw(PVE::RESTHandler);
11
12my $api_properties = {
13 cidr => {
14 description => "Network/IP specification in CIDR format.",
15 type => 'string', format => 'IPv4orCIDR',
16 },
e74a87f5 17 name => get_standard_option('ipset-name'),
009ee3ac
DM
18 comment => {
19 type => 'string',
20 optional => 1,
21 },
22 nomatch => {
23 type => 'boolean',
24 optional => 1,
25 },
26};
27
28sub load_config {
29 my ($class, $param) = @_;
30
31 die "implement this in subclass";
32
33 #return ($fw_conf, $rules);
34}
35
36sub save_rules {
37 my ($class, $param, $fw_conf, $rules) = @_;
38
39 die "implement this in subclass";
40}
41
42my $additional_param_hash = {};
43
44sub additional_parameters {
45 my ($class, $new_value) = @_;
46
47 if (defined($new_value)) {
48 $additional_param_hash->{$class} = $new_value;
49 }
50
51 # return a copy
52 my $copy = {};
53 my $org = $additional_param_hash->{$class} || {};
54 foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
55 return $copy;
56}
57
58sub register_get_ipset {
59 my ($class) = @_;
60
61 my $properties = $class->additional_parameters();
62
63 $properties->{name} = $api_properties->{name};
64
65 $class->register_method({
66 name => 'get_ipset',
67 path => '',
68 method => 'GET',
69 description => "List IPSet content",
70 parameters => {
71 additionalProperties => 0,
72 properties => $properties,
73 },
74 returns => {
75 type => 'array',
76 items => {
77 type => "object",
78 properties => {
79 cidr => {
80 type => 'string',
81 },
82 comment => {
83 type => 'string',
84 optional => 1,
85 },
86 nomatch => {
87 type => 'boolean',
88 optional => 1,
d72c631c
DM
89 },
90 digest => get_standard_option('pve-config-digest', { optional => 0} ),
009ee3ac
DM
91 },
92 },
93 links => [ { rel => 'child', href => "{cidr}" } ],
94 },
95 code => sub {
96 my ($param) = @_;
97
98 my ($fw_conf, $ipset) = $class->load_config($param);
99
d72c631c
DM
100 my $digest = $fw_conf->{digest};
101
102 my $res = [];
103 foreach my $entry (@$ipset) {
104 my $data = {digest => $digest};
105 foreach my $k (qw(cidr comment nomatch)) {
106 $data->{$k} = $entry->{$k} if $entry->{$k};
107 }
108 push @$res, $data;
109 }
110
111 return $res;
009ee3ac
DM
112 }});
113}
114
a33c74f6 115sub register_create_ip {
009ee3ac
DM
116 my ($class) = @_;
117
118 my $properties = $class->additional_parameters();
119
120 $properties->{name} = $api_properties->{name};
121 $properties->{cidr} = $api_properties->{cidr};
122 $properties->{nomatch} = $api_properties->{nomatch};
123 $properties->{comment} = $api_properties->{comment};
d72c631c 124
009ee3ac 125 $class->register_method({
a33c74f6 126 name => 'create_ip',
009ee3ac
DM
127 path => '',
128 method => 'POST',
129 description => "Add IP or Network to IPSet.",
130 protected => 1,
131 parameters => {
132 additionalProperties => 0,
133 properties => $properties,
134 },
135 returns => { type => "null" },
136 code => sub {
137 my ($param) = @_;
138
139 my ($fw_conf, $ipset) = $class->load_config($param);
140
4a11bba5
DM
141 my $cidr = $param->{cidr};
142
143 foreach my $entry (@$ipset) {
144 raise_param_exc({ cidr => "address '$cidr' already exists" })
145 if $entry->{cidr} eq $cidr;
146 }
147
148 my $data = { cidr => $cidr };
009ee3ac
DM
149 $data->{nomatch} = 1 if $param->{nomatch};
150 $data->{comment} = $param->{comment} if $param->{comment};
151
009ee3ac
DM
152 unshift @$ipset, $data;
153
154 $class->save_ipset($param, $fw_conf, $ipset);
155
156 return undef;
157 }});
158}
159
a33c74f6
DM
160sub register_read_ip {
161 my ($class) = @_;
162
163 my $properties = $class->additional_parameters();
164
165 $properties->{name} = $api_properties->{name};
166 $properties->{cidr} = $api_properties->{cidr};
167
168 $class->register_method({
169 name => 'read_ip',
170 path => '{cidr}',
171 method => 'GET',
172 description => "Read IP or Network settings from IPSet.",
173 protected => 1,
174 parameters => {
175 additionalProperties => 0,
176 properties => $properties,
177 },
178 returns => { type => "object" },
179 code => sub {
180 my ($param) = @_;
181
182 my ($fw_conf, $ipset) = $class->load_config($param);
d72c631c 183 my $digest = $fw_conf->{digest};
a33c74f6
DM
184
185 foreach my $entry (@$ipset) {
d72c631c
DM
186 if ($entry->{cidr} eq $param->{cidr}) {
187 $entry->{digest} = $digest;
188 return $entry;
189 }
a33c74f6
DM
190 }
191
192 raise_param_exc({ cidr => "no such IP/Network" });
193 }});
194}
195
196sub register_update_ip {
197 my ($class) = @_;
198
199 my $properties = $class->additional_parameters();
200
201 $properties->{name} = $api_properties->{name};
202 $properties->{cidr} = $api_properties->{cidr};
203 $properties->{nomatch} = $api_properties->{nomatch};
204 $properties->{comment} = $api_properties->{comment};
d72c631c
DM
205 $properties->{digest} = get_standard_option('pve-config-digest');
206
a33c74f6
DM
207 $class->register_method({
208 name => 'update_ip',
209 path => '{cidr}',
210 method => 'PUT',
211 description => "Update IP or Network settings",
212 protected => 1,
213 parameters => {
214 additionalProperties => 0,
215 properties => $properties,
216 },
217 returns => { type => "null" },
218 code => sub {
219 my ($param) = @_;
220
221 my ($fw_conf, $ipset) = $class->load_config($param);
222
d72c631c
DM
223 PVE::Tools::assert_if_modified($fw_conf->{digest}, $param->{digest});
224
a33c74f6
DM
225 foreach my $entry (@$ipset) {
226 if($entry->{cidr} eq $param->{cidr}) {
227 $entry->{nomatch} = $param->{nomatch};
228 $entry->{comment} = $param->{comment};
229 $class->save_ipset($param, $fw_conf, $ipset);
230 return;
231 }
232 }
233
234 raise_param_exc({ cidr => "no such IP/Network" });
235 }});
236}
237
238sub register_delete_ip {
009ee3ac
DM
239 my ($class) = @_;
240
241 my $properties = $class->additional_parameters();
242
243 $properties->{name} = $api_properties->{name};
244 $properties->{cidr} = $api_properties->{cidr};
d72c631c
DM
245 $properties->{digest} = get_standard_option('pve-config-digest');
246
009ee3ac
DM
247 $class->register_method({
248 name => 'remove_ip',
249 path => '{cidr}',
250 method => 'DELETE',
251 description => "Remove IP or Network from IPSet.",
252 protected => 1,
253 parameters => {
254 additionalProperties => 0,
255 properties => $properties,
256 },
257 returns => { type => "null" },
258 code => sub {
259 my ($param) = @_;
260
261 my ($fw_conf, $ipset) = $class->load_config($param);
262
d72c631c
DM
263 PVE::Tools::assert_if_modified($fw_conf->{digest}, $param->{digest});
264
4a11bba5
DM
265 my $new = [];
266
267 foreach my $entry (@$ipset) {
268 push @$new, $entry if $entry->{cidr} ne $param->{cidr};
269 }
009ee3ac 270
4a11bba5
DM
271 $class->save_ipset($param, $fw_conf, $new);
272
009ee3ac
DM
273 return undef;
274 }});
275}
276
277sub register_handlers {
278 my ($class) = @_;
279
280 $class->register_get_ipset();
a33c74f6
DM
281 $class->register_create_ip();
282 $class->register_read_ip();
283 $class->register_update_ip();
284 $class->register_delete_ip();
009ee3ac
DM
285}
286
287package PVE::API2::Firewall::ClusterIPset;
288
289use strict;
290use warnings;
291
292use base qw(PVE::API2::Firewall::IPSetBase);
293
294sub load_config {
295 my ($class, $param) = @_;
296
297 my $fw_conf = PVE::Firewall::load_clusterfw_conf();
298 my $ipset = $fw_conf->{ipset}->{$param->{name}};
299 die "no such IPSet '$param->{name}'\n" if !defined($ipset);
300
301 return ($fw_conf, $ipset);
302}
303
304sub save_ipset {
305 my ($class, $param, $fw_conf, $ipset) = @_;
306
307 $fw_conf->{ipset}->{$param->{name}} = $ipset;
308 PVE::Firewall::save_clusterfw_conf($fw_conf);
309}
310
311__PACKAGE__->register_handlers();
312
c85c87f9
DM
313package PVE::API2::Firewall::BaseIPSetList;
314
315use strict;
316use warnings;
e74a87f5 317use PVE::JSONSchema qw(get_standard_option);
c85c87f9 318use PVE::Exception qw(raise_param_exc);
e74a87f5 319use PVE::Firewall;
c85c87f9
DM
320
321use base qw(PVE::RESTHandler);
322
323sub register_index {
324 my ($class) = @_;
325
326 $class->register_method({
327 name => 'ipset_index',
328 path => '',
329 method => 'GET',
330 description => "List IPSets",
331 parameters => {
332 additionalProperties => 0,
333 },
334 returns => {
335 type => 'array',
336 items => {
337 type => "object",
338 properties => {
e74a87f5 339 name => get_standard_option('ipset-name'),
d72c631c
DM
340 digest => get_standard_option('pve-config-digest', { optional => 0} ),
341 comment => {
342 type => 'string',
343 optional => 1,
344 }
c85c87f9
DM
345 },
346 },
347 links => [ { rel => 'child', href => "{name}" } ],
348 },
349 code => sub {
350 my ($param) = @_;
351
352 my $fw_conf = $class->load_config();
353
d72c631c
DM
354 my $digest = $fw_conf->{digest};
355
c85c87f9
DM
356 my $res = [];
357 foreach my $name (keys %{$fw_conf->{ipset}}) {
d72c631c
DM
358 my $data = {
359 name => $name,
360 digest => $digest,
361 count => scalar(@{$fw_conf->{ipset}->{$name}})
362 };
363 if (my $comment = $fw_conf->{ipset_comments}->{$name}) {
364 $data->{comment} = $comment;
365 }
366 push @$res, $data;
c85c87f9
DM
367 }
368
369 return $res;
370 }});
371}
372
373sub register_create {
374 my ($class) = @_;
375
376 $class->register_method({
377 name => 'create_ipset',
378 path => '',
379 method => 'POST',
380 description => "Create new IPSet",
381 protected => 1,
382 parameters => {
383 additionalProperties => 0,
384 properties => {
e74a87f5 385 name => get_standard_option('ipset-name'),
d72c631c
DM
386 comment => {
387 type => 'string',
388 optional => 1,
389 },
e74a87f5 390 rename => get_standard_option('ipset-name', {
d72c631c 391 description => "Rename an existing IPSet. You can set 'rename' to the same value as 'name' to update the 'comment' of an existing IPSet.",
bc374ca7 392 optional => 1,
e74a87f5 393 }),
d72c631c 394 digest => get_standard_option('pve-config-digest'),
c85c87f9
DM
395 }
396 },
397 returns => { type => 'null' },
398 code => sub {
399 my ($param) = @_;
400
401 my $fw_conf = $class->load_config();
402
d72c631c
DM
403 my $digest = $fw_conf->{digest};
404
405 PVE::Tools::assert_if_modified($digest, $param->{digest});
406
407 if (!$param->{rename}) {
408 foreach my $name (keys %{$fw_conf->{ipset}}) {
409 raise_param_exc({ name => "IPSet '$name' already exists" })
410 if $name eq $param->{name};
411 }
c85c87f9
DM
412 }
413
bc374ca7
DM
414 if ($param->{rename}) {
415 raise_param_exc({ name => "IPSet '$param->{rename}' does not exists" })
416 if !$fw_conf->{ipset}->{$param->{rename}};
417 my $data = delete $fw_conf->{ipset}->{$param->{rename}};
418 $fw_conf->{ipset}->{$param->{name}} = $data;
d72c631c
DM
419 if (my $comment = delete $fw_conf->{ipset_comments}->{$param->{rename}}) {
420 $fw_conf->{ipset_comments}->{$param->{name}} = $comment;
421 }
422 $fw_conf->{ipset_comments}->{$param->{name}} = $param->{comment} if defined($param->{comment});
bc374ca7
DM
423 } else {
424 $fw_conf->{ipset}->{$param->{name}} = [];
d72c631c 425 $fw_conf->{ipset_comments}->{$param->{name}} = $param->{comment} if defined($param->{comment});
bc374ca7
DM
426 }
427
c85c87f9
DM
428 $class->save_config($fw_conf);
429
430 return undef;
431 }});
432}
433
434sub register_delete {
435 my ($class) = @_;
436
437 $class->register_method({
438 name => 'delete_ipset',
439 path => '{name}',
440 method => 'DELETE',
441 description => "Delete IPSet",
442 protected => 1,
443 parameters => {
444 additionalProperties => 0,
445 properties => {
e74a87f5 446 name => get_standard_option('ipset-name'),
d72c631c
DM
447 digest => get_standard_option('pve-config-digest'),
448 },
c85c87f9
DM
449 },
450 returns => { type => 'null' },
451 code => sub {
452 my ($param) = @_;
453
454 my $fw_conf = $class->load_config();
455
d72c631c
DM
456 PVE::Tools::assert_if_modified($fw_conf->{digest}, $param->{digest});
457
c85c87f9
DM
458 return undef if !$fw_conf->{ipset}->{$param->{name}};
459
76aad6c5 460 die "IPSet '$param->{name}' is not empty\n"
c85c87f9
DM
461 if scalar(@{$fw_conf->{ipset}->{$param->{name}}});
462
463 delete $fw_conf->{ipset}->{$param->{name}};
464
465 $class->save_config($fw_conf);
466
467 return undef;
468 }});
469}
470
471sub register_handlers {
472 my ($class) = @_;
473
474 $class->register_index();
475 $class->register_create();
476 $class->register_delete();
477}
478
479package PVE::API2::Firewall::ClusterIPSetList;
480
481use strict;
482use warnings;
483use PVE::Firewall;
484
485use base qw(PVE::API2::Firewall::BaseIPSetList);
486
487sub load_config {
488 my ($class) = @_;
489
490 return PVE::Firewall::load_clusterfw_conf();
491}
492
493sub save_config {
494 my ($class, $fw_conf) = @_;
495
496 PVE::Firewall::save_clusterfw_conf($fw_conf);
497}
498
499__PACKAGE__->register_handlers();
500
501__PACKAGE__->register_method ({
502 subclass => "PVE::API2::Firewall::ClusterIPset",
503 path => '{name}',
504 # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/'
505 fragmentDelimiter => '',
506});
507
009ee3ac 5081;