fix #967: source: dest: limit length
[pve-firewall.git] / src / PVE / API2 / Firewall / Rules.pm
CommitLineData
86791289
DM
1package PVE::API2::Firewall::RulesBase;
2
3use strict;
4use warnings;
1c49d333 5
86791289 6use PVE::JSONSchema qw(get_standard_option);
0d22acb3 7use PVE::Exception qw(raise raise_param_exc);
86791289
DM
8
9use PVE::Firewall;
10
11use base qw(PVE::RESTHandler);
12
75a12a9d 13my $api_properties = {
86791289
DM
14 pos => {
15 description => "Rule position.",
16 type => 'integer',
17 minimum => 0,
18 },
19};
20
05496017
FG
21sub lock_config {
22 my ($class, $param, $code) = @_;
23
24 die "implement this in subclass";
25}
26
86791289
DM
27sub load_config {
28 my ($class, $param) = @_;
29
30 die "implement this in subclass";
31
a523e057 32 #return ($cluster_conf, $fw_conf, $rules);
86791289
DM
33}
34
35sub save_rules {
36 my ($class, $param, $fw_conf, $rules) = @_;
37
38 die "implement this in subclass";
39}
40
63c91681 41my $additional_param_hash = {};
86791289 42
b6b8e6ad
DM
43sub rule_env {
44 my ($class, $param) = @_;
75a12a9d 45
b6b8e6ad 46 die "implement this in subclass";
7ca36671
DM
47}
48
63c91681 49sub additional_parameters {
86791289
DM
50 my ($class, $new_value) = @_;
51
63c91681
DM
52 if (defined($new_value)) {
53 $additional_param_hash->{$class} = $new_value;
54 }
86791289 55
63c91681
DM
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;
86791289
DM
61}
62
63sub register_get_rules {
64 my ($class) = @_;
65
63c91681 66 my $properties = $class->additional_parameters();
86791289 67
7f733a5a
DM
68 my $rule_env = $class->rule_env();
69
86791289
DM
70 $class->register_method({
71 name => 'get_rules',
72 path => '',
73 method => 'GET',
74 description => "List rules.",
9f6845cf 75 permissions => PVE::Firewall::rules_audit_permissions($rule_env),
86791289
DM
76 parameters => {
77 additionalProperties => 0,
78 properties => $properties,
79 },
7f733a5a 80 proxyto => $rule_env eq 'host' ? 'node' : undef,
86791289
DM
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
a523e057 96 my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
86791289 97
5d38d64f 98 my ($list, $digest) = PVE::Firewall::copy_list_with_digest($rules);
86791289
DM
99
100 my $ind = 0;
5d38d64f
DM
101 foreach my $rule (@$list) {
102 $rule->{pos} = $ind++;
86791289
DM
103 }
104
5d38d64f 105 return $list;
86791289
DM
106 }});
107}
108
109sub register_get_rule {
110 my ($class) = @_;
111
63c91681 112 my $properties = $class->additional_parameters();
86791289
DM
113
114 $properties->{pos} = $api_properties->{pos};
75a12a9d 115
7f733a5a
DM
116 my $rule_env = $class->rule_env();
117
86791289
DM
118 $class->register_method({
119 name => 'get_rule',
120 path => '{pos}',
121 method => 'GET',
122 description => "Get single rule data.",
9f6845cf 123 permissions => PVE::Firewall::rules_audit_permissions($rule_env),
86791289
DM
124 parameters => {
125 additionalProperties => 0,
126 properties => $properties,
127 },
7f733a5a 128 proxyto => $rule_env eq 'host' ? 'node' : undef,
86791289
DM
129 returns => {
130 type => "object",
131 properties => {
c5b2e6d9
RV
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 },
3489f8a2
CE
151 log => PVE::Firewall::get_standard_option('pve-fw-loglevel', {
152 description => 'Log level for firewall rule',
153 }),
72194c7c
ML
154 'icmp-type' => {
155 type => 'string',
156 optional => 1,
157 },
c5b2e6d9
RV
158 iface => {
159 type => 'string',
160 optional => 1,
161 },
162 ipversion => {
163 type => 'integer',
164 optional => 1,
165 },
166 macro => {
f0e30c99 167 type => 'string',
c5b2e6d9
RV
168 optional => 1,
169 },
86791289
DM
170 pos => {
171 type => 'integer',
c5b2e6d9
RV
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 },
86791289
DM
188 },
189 },
190 code => sub {
191 my ($param) = @_;
192
a523e057 193 my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
86791289 194
5d38d64f 195 my ($list, $digest) = PVE::Firewall::copy_list_with_digest($rules);
75a12a9d 196
5d38d64f 197 die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$list);
75a12a9d 198
5d38d64f
DM
199 my $rule = $list->[$param->{pos}];
200 $rule->{pos} = $param->{pos};
201
202 return $rule;
86791289
DM
203 }});
204}
205
206sub register_create_rule {
207 my ($class) = @_;
208
63c91681 209 my $properties = $class->additional_parameters();
86791289
DM
210
211 my $create_rule_properties = PVE::Firewall::add_rule_properties($properties);
3655b01f
DM
212 $create_rule_properties->{action}->{optional} = 0;
213 $create_rule_properties->{type}->{optional} = 0;
75a12a9d 214
7f733a5a
DM
215 my $rule_env = $class->rule_env();
216
86791289
DM
217 $class->register_method({
218 name => 'create_rule',
219 path => '',
220 method => 'POST',
221 description => "Create new rule.",
222 protected => 1,
9f6845cf 223 permissions => PVE::Firewall::rules_modify_permissions($rule_env),
86791289
DM
224 parameters => {
225 additionalProperties => 0,
226 properties => $create_rule_properties,
227 },
7f733a5a 228 proxyto => $rule_env eq 'host' ? 'node' : undef,
86791289
DM
229 returns => { type => "null" },
230 code => sub {
231 my ($param) = @_;
232
a38849e6
FG
233 $class->lock_config($param, sub {
234 my ($param) = @_;
235
236 my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
86791289 237
a38849e6 238 my $rule = {};
86791289 239
a38849e6
FG
240 PVE::Firewall::copy_rule_data($rule, $param);
241 PVE::Firewall::verify_rule($rule, $cluster_conf, $fw_conf, $class->rule_env());
86791289 242
a38849e6 243 $rule->{enable} = 0 if !defined($param->{enable});
3655b01f 244
a38849e6 245 unshift @$rules, $rule;
86791289 246
a38849e6
FG
247 $class->save_rules($param, $fw_conf, $rules);
248 });
86791289
DM
249
250 return undef;
251 }});
252}
253
254sub register_update_rule {
255 my ($class) = @_;
256
63c91681 257 my $properties = $class->additional_parameters();
86791289
DM
258
259 $properties->{pos} = $api_properties->{pos};
75a12a9d 260
7f733a5a
DM
261 my $rule_env = $class->rule_env();
262
86791289
DM
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
5b7974df
DM
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
86791289
DM
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,
9f6845cf 284 permissions => PVE::Firewall::rules_modify_permissions($rule_env),
86791289
DM
285 parameters => {
286 additionalProperties => 0,
287 properties => $update_rule_properties,
288 },
7f733a5a 289 proxyto => $rule_env eq 'host' ? 'node' : undef,
86791289
DM
290 returns => { type => "null" },
291 code => sub {
292 my ($param) = @_;
293
a38849e6
FG
294 $class->lock_config($param, sub {
295 my ($param) = @_;
296
297 my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
86791289 298
a38849e6
FG
299 my (undef, $digest) = PVE::Firewall::copy_list_with_digest($rules);
300 PVE::Tools::assert_if_modified($digest, $param->{digest});
ddf1e07d 301
a38849e6 302 die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
75a12a9d 303
a38849e6 304 my $rule = $rules->[$param->{pos}];
86791289 305
a38849e6
FG
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];
86791289 315 }
a38849e6
FG
316 push @$newrules, $rule if $moveto >= scalar(@$rules);
317 $rules = $newrules;
318 } else {
319 PVE::Firewall::copy_rule_data($rule, $param);
75a12a9d 320
a38849e6 321 PVE::Firewall::delete_rule_properties($rule, $param->{'delete'}) if $param->{'delete'};
7ca36671 322
a38849e6
FG
323 PVE::Firewall::verify_rule($rule, $cluster_conf, $fw_conf, $class->rule_env());
324 }
86791289 325
a38849e6
FG
326 $class->save_rules($param, $fw_conf, $rules);
327 });
86791289
DM
328
329 return undef;
330 }});
331}
332
333sub register_delete_rule {
334 my ($class) = @_;
335
63c91681 336 my $properties = $class->additional_parameters();
86791289
DM
337
338 $properties->{pos} = $api_properties->{pos};
ddf1e07d
DM
339
340 $properties->{digest} = get_standard_option('pve-config-digest');
75a12a9d 341
7f733a5a
DM
342 my $rule_env = $class->rule_env();
343
86791289
DM
344 $class->register_method({
345 name => 'delete_rule',
346 path => '{pos}',
347 method => 'DELETE',
348 description => "Delete rule.",
349 protected => 1,
9f6845cf 350 permissions => PVE::Firewall::rules_modify_permissions($rule_env),
86791289
DM
351 parameters => {
352 additionalProperties => 0,
353 properties => $properties,
354 },
7f733a5a 355 proxyto => $rule_env eq 'host' ? 'node' : undef,
86791289
DM
356 returns => { type => "null" },
357 code => sub {
358 my ($param) = @_;
359
a38849e6
FG
360 $class->lock_config($param, sub {
361 my ($param) = @_;
86791289 362
a38849e6 363 my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
75a12a9d 364
a38849e6
FG
365 my (undef, $digest) = PVE::Firewall::copy_list_with_digest($rules);
366 PVE::Tools::assert_if_modified($digest, $param->{digest});
75a12a9d 367
a38849e6 368 die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
75a12a9d 369
a38849e6
FG
370 splice(@$rules, $param->{pos}, 1);
371
372 $class->save_rules($param, $fw_conf, $rules);
373 });
86791289
DM
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;
387d0ffc 393use PVE::JSONSchema qw(get_standard_option);
86791289
DM
394
395use base qw(PVE::API2::Firewall::RulesBase);
396
387d0ffc 397__PACKAGE__->additional_parameters({ group => get_standard_option('pve-security-group-name') });
86791289 398
1210ae94 399
b6b8e6ad
DM
400sub rule_env {
401 my ($class, $param) = @_;
75a12a9d 402
b6b8e6ad 403 return 'group';
7ca36671
DM
404}
405
05496017
FG
406sub lock_config {
407 my ($class, $param, $code) = @_;
408
409 PVE::Firewall::lock_clusterfw_conf(10, $code, $param);
410}
411
86791289
DM
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
a523e057 419 return (undef, $fw_conf, $rules);
86791289
DM
420}
421
422sub save_rules {
423 my ($class, $param, $fw_conf, $rules) = @_;
424
1210ae94
DM
425 if (!defined($rules)) {
426 delete $fw_conf->{groups}->{$param->{group}};
427 } else {
428 $fw_conf->{groups}->{$param->{group}} = $rules;
429 }
430
86791289
DM
431 PVE::Firewall::save_clusterfw_conf($fw_conf);
432}
433
1210ae94
DM
434__PACKAGE__->register_method({
435 name => 'delete_security_group',
436 path => '',
437 method => 'DELETE',
438 description => "Delete security group.",
439 protected => 1,
2ba4951d
DM
440 permissions => {
441 check => ['perm', '/', [ 'Sys.Modify' ]],
442 },
1210ae94
DM
443 parameters => {
444 additionalProperties => 0,
75a12a9d 445 properties => {
1210ae94
DM
446 group => get_standard_option('pve-security-group-name'),
447 },
448 },
449 returns => { type => 'null' },
450 code => sub {
451 my ($param) = @_;
75a12a9d 452
a38849e6
FG
453 __PACKAGE__->lock_config($param, sub {
454 my ($param) = @_;
455
456 my (undef, $cluster_conf, $rules) = __PACKAGE__->load_config($param);
1210ae94 457
a38849e6
FG
458 die "Security group '$param->{group}' is not empty\n"
459 if scalar(@$rules);
1210ae94 460
a38849e6
FG
461 __PACKAGE__->save_rules($param, $cluster_conf, undef);
462 });
1210ae94
DM
463
464 return undef;
465 }});
466
63c91681 467__PACKAGE__->register_handlers();
86791289
DM
468
469package PVE::API2::Firewall::ClusterRules;
470
471use strict;
472use warnings;
473
474use base qw(PVE::API2::Firewall::RulesBase);
475
b6b8e6ad
DM
476sub rule_env {
477 my ($class, $param) = @_;
75a12a9d 478
b6b8e6ad
DM
479 return 'cluster';
480}
481
05496017
FG
482sub lock_config {
483 my ($class, $param, $code) = @_;
484
485 PVE::Firewall::lock_clusterfw_conf(10, $code, $param);
486}
487
86791289
DM
488sub load_config {
489 my ($class, $param) = @_;
490
491 my $fw_conf = PVE::Firewall::load_clusterfw_conf();
492 my $rules = $fw_conf->{rules};
493
a523e057 494 return (undef, $fw_conf, $rules);
86791289
DM
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
63c91681
DM
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
b6b8e6ad
DM
516sub rule_env {
517 my ($class, $param) = @_;
75a12a9d 518
b6b8e6ad
DM
519 return 'host';
520}
521
05496017
FG
522sub lock_config {
523 my ($class, $param, $code) = @_;
524
525 PVE::Firewall::lock_hostfw_conf(10, $code, $param);
526}
527
63c91681
DM
528sub load_config {
529 my ($class, $param) = @_;
530
a523e057
DM
531 my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
532 my $fw_conf = PVE::Firewall::load_hostfw_conf($cluster_conf);
63c91681
DM
533 my $rules = $fw_conf->{rules};
534
a523e057 535 return ($cluster_conf, $fw_conf, $rules);
63c91681
DM
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();
86791289 546
464f933e
DM
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
75a12a9d 555__PACKAGE__->additional_parameters({
464f933e 556 node => get_standard_option('pve-node'),
75a12a9d 557 vmid => get_standard_option('pve-vmid'),
464f933e
DM
558});
559
b6b8e6ad
DM
560sub rule_env {
561 my ($class, $param) = @_;
75a12a9d 562
b6b8e6ad
DM
563 return 'vm';
564}
565
05496017
FG
566sub lock_config {
567 my ($class, $param, $code) = @_;
568
569 PVE::Firewall::lock_vmfw_conf($param->{vmid}, 10, $code, $param);
570}
571
b6b8e6ad
DM
572sub load_config {
573 my ($class, $param) = @_;
574
a523e057
DM
575 my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
576 my $fw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, 'vm', $param->{vmid});
b6b8e6ad
DM
577 my $rules = $fw_conf->{rules};
578
a523e057 579 return ($cluster_conf, $fw_conf, $rules);
b6b8e6ad
DM
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
75a12a9d 599__PACKAGE__->additional_parameters({
b6b8e6ad 600 node => get_standard_option('pve-node'),
75a12a9d 601 vmid => get_standard_option('pve-vmid'),
b6b8e6ad
DM
602});
603
604sub rule_env {
605 my ($class, $param) = @_;
75a12a9d 606
b6b8e6ad
DM
607 return 'ct';
608}
609
05496017
FG
610sub lock_config {
611 my ($class, $param, $code) = @_;
612
613 PVE::Firewall::lock_vmfw_conf($param->{vmid}, 10, $code, $param);
614}
615
464f933e
DM
616sub load_config {
617 my ($class, $param) = @_;
618
a523e057
DM
619 my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
620 my $fw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, 'ct', $param->{vmid});
464f933e
DM
621 my $rules = $fw_conf->{rules};
622
a523e057 623 return ($cluster_conf, $fw_conf, $rules);
464f933e
DM
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
86791289 6351;