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