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