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