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