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