]> git.proxmox.com Git - pve-firewall.git/blob - src/PVE/API2/Firewall/Rules.pm
define standard option for security group names
[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
7 use PVE::Firewall;
8
9 use base qw(PVE::RESTHandler);
10
11 my $api_properties = {
12 pos => {
13 description => "Rule position.",
14 type => 'integer',
15 minimum => 0,
16 },
17 };
18
19 sub load_config {
20 my ($class, $param) = @_;
21
22 die "implement this in subclass";
23
24 #return ($fw_conf, $rules);
25 }
26
27 sub save_rules {
28 my ($class, $param, $fw_conf, $rules) = @_;
29
30 die "implement this in subclass";
31 }
32
33 my $additional_param_hash = {};
34
35 sub allow_groups {
36 return 1;
37 }
38
39 sub additional_parameters {
40 my ($class, $new_value) = @_;
41
42 if (defined($new_value)) {
43 $additional_param_hash->{$class} = $new_value;
44 }
45
46 # return a copy
47 my $copy = {};
48 my $org = $additional_param_hash->{$class} || {};
49 foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
50 return $copy;
51 }
52
53 sub register_get_rules {
54 my ($class) = @_;
55
56 my $properties = $class->additional_parameters();
57
58 $class->register_method({
59 name => 'get_rules',
60 path => '',
61 method => 'GET',
62 description => "List rules.",
63 parameters => {
64 additionalProperties => 0,
65 properties => $properties,
66 },
67 returns => {
68 type => 'array',
69 items => {
70 type => "object",
71 properties => {
72 pos => {
73 type => 'integer',
74 }
75 },
76 },
77 links => [ { rel => 'child', href => "{pos}" } ],
78 },
79 code => sub {
80 my ($param) = @_;
81
82 my ($fw_conf, $rules) = $class->load_config($param);
83
84 my $digest = $fw_conf->{digest};
85
86 my $res = [];
87
88 my $ind = 0;
89 foreach my $rule (@$rules) {
90 push @$res, PVE::Firewall::cleanup_fw_rule($rule, $digest, $ind++);
91 }
92
93 return $res;
94 }});
95 }
96
97 sub register_get_rule {
98 my ($class) = @_;
99
100 my $properties = $class->additional_parameters();
101
102 $properties->{pos} = $api_properties->{pos};
103
104 $class->register_method({
105 name => 'get_rule',
106 path => '{pos}',
107 method => 'GET',
108 description => "Get single rule data.",
109 parameters => {
110 additionalProperties => 0,
111 properties => $properties,
112 },
113 returns => {
114 type => "object",
115 properties => {
116 pos => {
117 type => 'integer',
118 }
119 },
120 },
121 code => sub {
122 my ($param) = @_;
123
124 my ($fw_conf, $rules) = $class->load_config($param);
125
126 my $digest = $fw_conf->{digest};
127 # fixme: check digest
128
129 die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
130
131 my $rule = $rules->[$param->{pos}];
132
133 return PVE::Firewall::cleanup_fw_rule($rule, $digest, $param->{pos});
134 }});
135 }
136
137 sub register_create_rule {
138 my ($class) = @_;
139
140 my $properties = $class->additional_parameters();
141
142 my $create_rule_properties = PVE::Firewall::add_rule_properties($properties);
143 $create_rule_properties->{action}->{optional} = 0;
144 $create_rule_properties->{type}->{optional} = 0;
145
146 $class->register_method({
147 name => 'create_rule',
148 path => '',
149 method => 'POST',
150 description => "Create new rule.",
151 protected => 1,
152 parameters => {
153 additionalProperties => 0,
154 properties => $create_rule_properties,
155 },
156 returns => { type => "null" },
157 code => sub {
158 my ($param) = @_;
159
160 my ($fw_conf, $rules) = $class->load_config($param);
161
162 my $digest = $fw_conf->{digest};
163
164 my $rule = {};
165
166 PVE::Firewall::copy_rule_data($rule, $param);
167 PVE::Firewall::verify_rule($rule, $class->allow_groups());
168
169 $rule->{enable} = 0 if !defined($param->{enable});
170
171 unshift @$rules, $rule;
172
173 $class->save_rules($param, $fw_conf, $rules);
174
175 return undef;
176 }});
177 }
178
179 sub register_update_rule {
180 my ($class) = @_;
181
182 my $properties = $class->additional_parameters();
183
184 $properties->{pos} = $api_properties->{pos};
185
186 $properties->{moveto} = {
187 description => "Move rule to new position <moveto>. Other arguments are ignored.",
188 type => 'integer',
189 minimum => 0,
190 optional => 1,
191 };
192
193 $properties->{delete} = {
194 type => 'string', format => 'pve-configid-list',
195 description => "A list of settings you want to delete.",
196 optional => 1,
197 };
198
199 my $update_rule_properties = PVE::Firewall::add_rule_properties($properties);
200
201 $class->register_method({
202 name => 'update_rule',
203 path => '{pos}',
204 method => 'PUT',
205 description => "Modify rule data.",
206 protected => 1,
207 parameters => {
208 additionalProperties => 0,
209 properties => $update_rule_properties,
210 },
211 returns => { type => "null" },
212 code => sub {
213 my ($param) = @_;
214
215 my ($fw_conf, $rules) = $class->load_config($param);
216
217 my $digest = $fw_conf->{digest};
218 # fixme: check digest
219
220 die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
221
222 my $rule = $rules->[$param->{pos}];
223
224 my $moveto = $param->{moveto};
225 if (defined($moveto) && $moveto != $param->{pos}) {
226 my $newrules = [];
227 for (my $i = 0; $i < scalar(@$rules); $i++) {
228 next if $i == $param->{pos};
229 if ($i == $moveto) {
230 push @$newrules, $rule;
231 }
232 push @$newrules, $rules->[$i];
233 }
234 push @$newrules, $rule if $moveto >= scalar(@$rules);
235 $rules = $newrules;
236 } else {
237 raise_param_exc({ type => "property is missing"})
238 if !defined($param->{type});
239 raise_param_exc({ action => "property is missing"})
240 if !defined($param->{action});
241
242 PVE::Firewall::copy_rule_data($rule, $param);
243
244 PVE::Firewall::delete_rule_properties($rule, $param->{'delete'}) if $param->{'delete'};
245
246 PVE::Firewall::verify_rule($rule, $class->allow_groups());
247 }
248
249 $class->save_rules($param, $fw_conf, $rules);
250
251 return undef;
252 }});
253 }
254
255 sub register_delete_rule {
256 my ($class) = @_;
257
258 my $properties = $class->additional_parameters();
259
260 $properties->{pos} = $api_properties->{pos};
261
262 $class->register_method({
263 name => 'delete_rule',
264 path => '{pos}',
265 method => 'DELETE',
266 description => "Delete rule.",
267 protected => 1,
268 parameters => {
269 additionalProperties => 0,
270 properties => $properties,
271 },
272 returns => { type => "null" },
273 code => sub {
274 my ($param) = @_;
275
276 my ($fw_conf, $rules) = $class->load_config($param);
277
278 my $digest = $fw_conf->{digest};
279 # fixme: check digest
280
281 die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
282
283 splice(@$rules, $param->{pos}, 1);
284
285 $class->save_rules($param, $fw_conf, $rules);
286
287 return undef;
288 }});
289 }
290
291 sub register_handlers {
292 my ($class) = @_;
293
294 $class->register_get_rules();
295 $class->register_get_rule();
296 $class->register_create_rule();
297 $class->register_update_rule();
298 $class->register_delete_rule();
299 }
300
301 package PVE::API2::Firewall::GroupRules;
302
303 use strict;
304 use warnings;
305 use PVE::JSONSchema qw(get_standard_option);
306
307 use base qw(PVE::API2::Firewall::RulesBase);
308
309 __PACKAGE__->additional_parameters({ group => get_standard_option('pve-security-group-name') });
310
311 sub allow_groups {
312 return 0;
313 }
314
315 sub load_config {
316 my ($class, $param) = @_;
317
318 my $fw_conf = PVE::Firewall::load_clusterfw_conf();
319 my $rules = $fw_conf->{groups}->{$param->{group}};
320 die "no such security group '$param->{group}'\n" if !defined($rules);
321
322 return ($fw_conf, $rules);
323 }
324
325 sub save_rules {
326 my ($class, $param, $fw_conf, $rules) = @_;
327
328 $fw_conf->{groups}->{$param->{group}} = $rules;
329 PVE::Firewall::save_clusterfw_conf($fw_conf);
330 }
331
332 __PACKAGE__->register_handlers();
333
334 package PVE::API2::Firewall::ClusterRules;
335
336 use strict;
337 use warnings;
338
339 use base qw(PVE::API2::Firewall::RulesBase);
340
341 sub load_config {
342 my ($class, $param) = @_;
343
344 my $fw_conf = PVE::Firewall::load_clusterfw_conf();
345 my $rules = $fw_conf->{rules};
346
347 return ($fw_conf, $rules);
348 }
349
350 sub save_rules {
351 my ($class, $param, $fw_conf, $rules) = @_;
352
353 $fw_conf->{rules} = $rules;
354 PVE::Firewall::save_clusterfw_conf($fw_conf);
355 }
356
357 __PACKAGE__->register_handlers();
358
359 package PVE::API2::Firewall::HostRules;
360
361 use strict;
362 use warnings;
363 use PVE::JSONSchema qw(get_standard_option);
364
365 use base qw(PVE::API2::Firewall::RulesBase);
366
367 __PACKAGE__->additional_parameters({ node => get_standard_option('pve-node')});
368
369 sub load_config {
370 my ($class, $param) = @_;
371
372 my $fw_conf = PVE::Firewall::load_hostfw_conf();
373 my $rules = $fw_conf->{rules};
374
375 return ($fw_conf, $rules);
376 }
377
378 sub save_rules {
379 my ($class, $param, $fw_conf, $rules) = @_;
380
381 $fw_conf->{rules} = $rules;
382 PVE::Firewall::save_hostfw_conf($fw_conf);
383 }
384
385 __PACKAGE__->register_handlers();
386
387 package PVE::API2::Firewall::VMRules;
388
389 use strict;
390 use warnings;
391 use PVE::JSONSchema qw(get_standard_option);
392
393 use base qw(PVE::API2::Firewall::RulesBase);
394
395 __PACKAGE__->additional_parameters({
396 node => get_standard_option('pve-node'),
397 vmid => get_standard_option('pve-vmid'),
398 });
399
400 sub load_config {
401 my ($class, $param) = @_;
402
403 my $fw_conf = PVE::Firewall::load_vmfw_conf($param->{vmid});
404 my $rules = $fw_conf->{rules};
405
406 return ($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_vmfw_conf($param->{vmid}, $fw_conf);
414 }
415
416 __PACKAGE__->register_handlers();
417
418 1;