]> git.proxmox.com Git - pve-firewall.git/blame_incremental - src/PVE/API2/Firewall/Rules.pm
introduce new icmp-type parameter
[pve-firewall.git] / src / PVE / API2 / Firewall / Rules.pm
... / ...
CommitLineData
1package PVE::API2::Firewall::RulesBase;
2
3use strict;
4use warnings;
5
6use PVE::JSONSchema qw(get_standard_option);
7use PVE::Exception qw(raise raise_param_exc);
8
9use PVE::Firewall;
10
11use base qw(PVE::RESTHandler);
12
13my $api_properties = {
14 pos => {
15 description => "Rule position.",
16 type => 'integer',
17 minimum => 0,
18 },
19};
20
21sub lock_config {
22 my ($class, $param, $code) = @_;
23
24 die "implement this in subclass";
25}
26
27sub load_config {
28 my ($class, $param) = @_;
29
30 die "implement this in subclass";
31
32 #return ($cluster_conf, $fw_conf, $rules);
33}
34
35sub save_rules {
36 my ($class, $param, $fw_conf, $rules) = @_;
37
38 die "implement this in subclass";
39}
40
41my $additional_param_hash = {};
42
43sub rule_env {
44 my ($class, $param) = @_;
45
46 die "implement this in subclass";
47}
48
49sub additional_parameters {
50 my ($class, $new_value) = @_;
51
52 if (defined($new_value)) {
53 $additional_param_hash->{$class} = $new_value;
54 }
55
56 # return a copy
57 my $copy = {};
58 my $org = $additional_param_hash->{$class} || {};
59 foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
60 return $copy;
61}
62
63sub register_get_rules {
64 my ($class) = @_;
65
66 my $properties = $class->additional_parameters();
67
68 my $rule_env = $class->rule_env();
69
70 $class->register_method({
71 name => 'get_rules',
72 path => '',
73 method => 'GET',
74 description => "List rules.",
75 permissions => PVE::Firewall::rules_audit_permissions($rule_env),
76 parameters => {
77 additionalProperties => 0,
78 properties => $properties,
79 },
80 proxyto => $rule_env eq 'host' ? 'node' : undef,
81 returns => {
82 type => 'array',
83 items => {
84 type => "object",
85 properties => {
86 pos => {
87 type => 'integer',
88 }
89 },
90 },
91 links => [ { rel => 'child', href => "{pos}" } ],
92 },
93 code => sub {
94 my ($param) = @_;
95
96 my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
97
98 my ($list, $digest) = PVE::Firewall::copy_list_with_digest($rules);
99
100 my $ind = 0;
101 foreach my $rule (@$list) {
102 $rule->{pos} = $ind++;
103 }
104
105 return $list;
106 }});
107}
108
109sub register_get_rule {
110 my ($class) = @_;
111
112 my $properties = $class->additional_parameters();
113
114 $properties->{pos} = $api_properties->{pos};
115
116 my $rule_env = $class->rule_env();
117
118 $class->register_method({
119 name => 'get_rule',
120 path => '{pos}',
121 method => 'GET',
122 description => "Get single rule data.",
123 permissions => PVE::Firewall::rules_audit_permissions($rule_env),
124 parameters => {
125 additionalProperties => 0,
126 properties => $properties,
127 },
128 proxyto => $rule_env eq 'host' ? 'node' : undef,
129 returns => {
130 type => "object",
131 properties => {
132 action => {
133 type => 'string',
134 },
135 comment => {
136 type => 'string',
137 optional => 1,
138 },
139 dest => {
140 type => 'string',
141 optional => 1,
142 },
143 dport => {
144 type => 'string',
145 optional => 1,
146 },
147 enable => {
148 type => 'integer',
149 optional => 1,
150 },
151 log => PVE::Firewall::get_standard_option('pve-fw-loglevel', {
152 description => 'Log level for firewall rule',
153 }),
154 'icmp-type' => {
155 type => 'string',
156 optional => 1,
157 },
158 iface => {
159 type => 'string',
160 optional => 1,
161 },
162 ipversion => {
163 type => 'integer',
164 optional => 1,
165 },
166 macro => {
167 type => 'string',
168 optional => 1,
169 },
170 pos => {
171 type => 'integer',
172 },
173 proto => {
174 type => 'string',
175 optional => 1,
176 },
177 source => {
178 type => 'string',
179 optional => 1,
180 },
181 sport => {
182 type => 'string',
183 optional => 1,
184 },
185 type => {
186 type => 'string',
187 },
188 },
189 },
190 code => sub {
191 my ($param) = @_;
192
193 my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
194
195 my ($list, $digest) = PVE::Firewall::copy_list_with_digest($rules);
196
197 die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$list);
198
199 my $rule = $list->[$param->{pos}];
200 $rule->{pos} = $param->{pos};
201
202 return $rule;
203 }});
204}
205
206sub register_create_rule {
207 my ($class) = @_;
208
209 my $properties = $class->additional_parameters();
210
211 my $create_rule_properties = PVE::Firewall::add_rule_properties($properties);
212 $create_rule_properties->{action}->{optional} = 0;
213 $create_rule_properties->{type}->{optional} = 0;
214
215 my $rule_env = $class->rule_env();
216
217 $class->register_method({
218 name => 'create_rule',
219 path => '',
220 method => 'POST',
221 description => "Create new rule.",
222 protected => 1,
223 permissions => PVE::Firewall::rules_modify_permissions($rule_env),
224 parameters => {
225 additionalProperties => 0,
226 properties => $create_rule_properties,
227 },
228 proxyto => $rule_env eq 'host' ? 'node' : undef,
229 returns => { type => "null" },
230 code => sub {
231 my ($param) = @_;
232
233 $class->lock_config($param, sub {
234 my ($param) = @_;
235
236 my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
237
238 my $rule = {};
239
240 PVE::Firewall::copy_rule_data($rule, $param);
241 PVE::Firewall::verify_rule($rule, $cluster_conf, $fw_conf, $class->rule_env());
242
243 $rule->{enable} = 0 if !defined($param->{enable});
244
245 unshift @$rules, $rule;
246
247 $class->save_rules($param, $fw_conf, $rules);
248 });
249
250 return undef;
251 }});
252}
253
254sub register_update_rule {
255 my ($class) = @_;
256
257 my $properties = $class->additional_parameters();
258
259 $properties->{pos} = $api_properties->{pos};
260
261 my $rule_env = $class->rule_env();
262
263 $properties->{moveto} = {
264 description => "Move rule to new position <moveto>. Other arguments are ignored.",
265 type => 'integer',
266 minimum => 0,
267 optional => 1,
268 };
269
270 $properties->{delete} = {
271 type => 'string', format => 'pve-configid-list',
272 description => "A list of settings you want to delete.",
273 optional => 1,
274 };
275
276 my $update_rule_properties = PVE::Firewall::add_rule_properties($properties);
277
278 $class->register_method({
279 name => 'update_rule',
280 path => '{pos}',
281 method => 'PUT',
282 description => "Modify rule data.",
283 protected => 1,
284 permissions => PVE::Firewall::rules_modify_permissions($rule_env),
285 parameters => {
286 additionalProperties => 0,
287 properties => $update_rule_properties,
288 },
289 proxyto => $rule_env eq 'host' ? 'node' : undef,
290 returns => { type => "null" },
291 code => sub {
292 my ($param) = @_;
293
294 $class->lock_config($param, sub {
295 my ($param) = @_;
296
297 my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
298
299 my (undef, $digest) = PVE::Firewall::copy_list_with_digest($rules);
300 PVE::Tools::assert_if_modified($digest, $param->{digest});
301
302 die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
303
304 my $rule = $rules->[$param->{pos}];
305
306 my $moveto = $param->{moveto};
307 if (defined($moveto) && $moveto != $param->{pos}) {
308 my $newrules = [];
309 for (my $i = 0; $i < scalar(@$rules); $i++) {
310 next if $i == $param->{pos};
311 if ($i == $moveto) {
312 push @$newrules, $rule;
313 }
314 push @$newrules, $rules->[$i];
315 }
316 push @$newrules, $rule if $moveto >= scalar(@$rules);
317 $rules = $newrules;
318 } else {
319 PVE::Firewall::copy_rule_data($rule, $param);
320
321 PVE::Firewall::delete_rule_properties($rule, $param->{'delete'}) if $param->{'delete'};
322
323 PVE::Firewall::verify_rule($rule, $cluster_conf, $fw_conf, $class->rule_env());
324 }
325
326 $class->save_rules($param, $fw_conf, $rules);
327 });
328
329 return undef;
330 }});
331}
332
333sub register_delete_rule {
334 my ($class) = @_;
335
336 my $properties = $class->additional_parameters();
337
338 $properties->{pos} = $api_properties->{pos};
339
340 $properties->{digest} = get_standard_option('pve-config-digest');
341
342 my $rule_env = $class->rule_env();
343
344 $class->register_method({
345 name => 'delete_rule',
346 path => '{pos}',
347 method => 'DELETE',
348 description => "Delete rule.",
349 protected => 1,
350 permissions => PVE::Firewall::rules_modify_permissions($rule_env),
351 parameters => {
352 additionalProperties => 0,
353 properties => $properties,
354 },
355 proxyto => $rule_env eq 'host' ? 'node' : undef,
356 returns => { type => "null" },
357 code => sub {
358 my ($param) = @_;
359
360 $class->lock_config($param, sub {
361 my ($param) = @_;
362
363 my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
364
365 my (undef, $digest) = PVE::Firewall::copy_list_with_digest($rules);
366 PVE::Tools::assert_if_modified($digest, $param->{digest});
367
368 die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
369
370 splice(@$rules, $param->{pos}, 1);
371
372 $class->save_rules($param, $fw_conf, $rules);
373 });
374
375 return undef;
376 }});
377}
378
379sub register_handlers {
380 my ($class) = @_;
381
382 $class->register_get_rules();
383 $class->register_get_rule();
384 $class->register_create_rule();
385 $class->register_update_rule();
386 $class->register_delete_rule();
387}
388
389package PVE::API2::Firewall::GroupRules;
390
391use strict;
392use warnings;
393use PVE::JSONSchema qw(get_standard_option);
394
395use base qw(PVE::API2::Firewall::RulesBase);
396
397__PACKAGE__->additional_parameters({ group => get_standard_option('pve-security-group-name') });
398
399
400sub rule_env {
401 my ($class, $param) = @_;
402
403 return 'group';
404}
405
406sub lock_config {
407 my ($class, $param, $code) = @_;
408
409 PVE::Firewall::lock_clusterfw_conf(10, $code, $param);
410}
411
412sub load_config {
413 my ($class, $param) = @_;
414
415 my $fw_conf = PVE::Firewall::load_clusterfw_conf();
416 my $rules = $fw_conf->{groups}->{$param->{group}};
417 die "no such security group '$param->{group}'\n" if !defined($rules);
418
419 return (undef, $fw_conf, $rules);
420}
421
422sub save_rules {
423 my ($class, $param, $fw_conf, $rules) = @_;
424
425 if (!defined($rules)) {
426 delete $fw_conf->{groups}->{$param->{group}};
427 } else {
428 $fw_conf->{groups}->{$param->{group}} = $rules;
429 }
430
431 PVE::Firewall::save_clusterfw_conf($fw_conf);
432}
433
434__PACKAGE__->register_method({
435 name => 'delete_security_group',
436 path => '',
437 method => 'DELETE',
438 description => "Delete security group.",
439 protected => 1,
440 permissions => {
441 check => ['perm', '/', [ 'Sys.Modify' ]],
442 },
443 parameters => {
444 additionalProperties => 0,
445 properties => {
446 group => get_standard_option('pve-security-group-name'),
447 },
448 },
449 returns => { type => 'null' },
450 code => sub {
451 my ($param) = @_;
452
453 __PACKAGE__->lock_config($param, sub {
454 my ($param) = @_;
455
456 my (undef, $cluster_conf, $rules) = __PACKAGE__->load_config($param);
457
458 die "Security group '$param->{group}' is not empty\n"
459 if scalar(@$rules);
460
461 __PACKAGE__->save_rules($param, $cluster_conf, undef);
462 });
463
464 return undef;
465 }});
466
467__PACKAGE__->register_handlers();
468
469package PVE::API2::Firewall::ClusterRules;
470
471use strict;
472use warnings;
473
474use base qw(PVE::API2::Firewall::RulesBase);
475
476sub rule_env {
477 my ($class, $param) = @_;
478
479 return 'cluster';
480}
481
482sub lock_config {
483 my ($class, $param, $code) = @_;
484
485 PVE::Firewall::lock_clusterfw_conf(10, $code, $param);
486}
487
488sub load_config {
489 my ($class, $param) = @_;
490
491 my $fw_conf = PVE::Firewall::load_clusterfw_conf();
492 my $rules = $fw_conf->{rules};
493
494 return (undef, $fw_conf, $rules);
495}
496
497sub save_rules {
498 my ($class, $param, $fw_conf, $rules) = @_;
499
500 $fw_conf->{rules} = $rules;
501 PVE::Firewall::save_clusterfw_conf($fw_conf);
502}
503
504__PACKAGE__->register_handlers();
505
506package PVE::API2::Firewall::HostRules;
507
508use strict;
509use warnings;
510use PVE::JSONSchema qw(get_standard_option);
511
512use base qw(PVE::API2::Firewall::RulesBase);
513
514__PACKAGE__->additional_parameters({ node => get_standard_option('pve-node')});
515
516sub rule_env {
517 my ($class, $param) = @_;
518
519 return 'host';
520}
521
522sub lock_config {
523 my ($class, $param, $code) = @_;
524
525 PVE::Firewall::lock_hostfw_conf(10, $code, $param);
526}
527
528sub load_config {
529 my ($class, $param) = @_;
530
531 my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
532 my $fw_conf = PVE::Firewall::load_hostfw_conf($cluster_conf);
533 my $rules = $fw_conf->{rules};
534
535 return ($cluster_conf, $fw_conf, $rules);
536}
537
538sub save_rules {
539 my ($class, $param, $fw_conf, $rules) = @_;
540
541 $fw_conf->{rules} = $rules;
542 PVE::Firewall::save_hostfw_conf($fw_conf);
543}
544
545__PACKAGE__->register_handlers();
546
547package PVE::API2::Firewall::VMRules;
548
549use strict;
550use warnings;
551use PVE::JSONSchema qw(get_standard_option);
552
553use base qw(PVE::API2::Firewall::RulesBase);
554
555__PACKAGE__->additional_parameters({
556 node => get_standard_option('pve-node'),
557 vmid => get_standard_option('pve-vmid'),
558});
559
560sub rule_env {
561 my ($class, $param) = @_;
562
563 return 'vm';
564}
565
566sub lock_config {
567 my ($class, $param, $code) = @_;
568
569 PVE::Firewall::lock_vmfw_conf($param->{vmid}, 10, $code, $param);
570}
571
572sub load_config {
573 my ($class, $param) = @_;
574
575 my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
576 my $fw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, 'vm', $param->{vmid});
577 my $rules = $fw_conf->{rules};
578
579 return ($cluster_conf, $fw_conf, $rules);
580}
581
582sub save_rules {
583 my ($class, $param, $fw_conf, $rules) = @_;
584
585 $fw_conf->{rules} = $rules;
586 PVE::Firewall::save_vmfw_conf($param->{vmid}, $fw_conf);
587}
588
589__PACKAGE__->register_handlers();
590
591package PVE::API2::Firewall::CTRules;
592
593use strict;
594use warnings;
595use PVE::JSONSchema qw(get_standard_option);
596
597use base qw(PVE::API2::Firewall::RulesBase);
598
599__PACKAGE__->additional_parameters({
600 node => get_standard_option('pve-node'),
601 vmid => get_standard_option('pve-vmid'),
602});
603
604sub rule_env {
605 my ($class, $param) = @_;
606
607 return 'ct';
608}
609
610sub lock_config {
611 my ($class, $param, $code) = @_;
612
613 PVE::Firewall::lock_vmfw_conf($param->{vmid}, 10, $code, $param);
614}
615
616sub load_config {
617 my ($class, $param) = @_;
618
619 my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
620 my $fw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, 'ct', $param->{vmid});
621 my $rules = $fw_conf->{rules};
622
623 return ($cluster_conf, $fw_conf, $rules);
624}
625
626sub save_rules {
627 my ($class, $param, $fw_conf, $rules) = @_;
628
629 $fw_conf->{rules} = $rules;
630 PVE::Firewall::save_vmfw_conf($param->{vmid}, $fw_conf);
631}
632
633__PACKAGE__->register_handlers();
634
6351;