]> git.proxmox.com Git - pve-firewall.git/blob - src/PVE/API2/Firewall/Aliases.pm
fix #5335: stable sorting in cluster.fw
[pve-firewall.git] / src / PVE / API2 / Firewall / Aliases.pm
1 package PVE::API2::Firewall::AliasesBase;
2
3 use strict;
4 use warnings;
5 use PVE::Exception qw(raise raise_param_exc);
6 use PVE::JSONSchema qw(get_standard_option);
7
8 use PVE::Firewall;
9
10 use base qw(PVE::RESTHandler);
11
12 my $api_properties = {
13 cidr => {
14 description => "Network/IP specification in CIDR format.",
15 type => 'string', format => 'IPorCIDR',
16 },
17 name => get_standard_option('pve-fw-alias'),
18 rename => get_standard_option('pve-fw-alias', {
19 description => "Rename an existing alias.",
20 optional => 1,
21 }),
22 comment => {
23 type => 'string',
24 optional => 1,
25 },
26 };
27
28 sub lock_config {
29 my ($class, $param, $code) = @_;
30
31 die "implement this in subclass";
32 }
33
34 sub load_config {
35 my ($class, $param) = @_;
36
37 die "implement this in subclass";
38
39 #return ($fw_conf, $rules);
40 }
41
42 sub save_aliases {
43 my ($class, $param, $fw_conf, $aliases) = @_;
44
45 die "implement this in subclass";
46 }
47
48 sub rule_env {
49 my ($class, $param) = @_;
50
51 die "implement this in subclass";
52 }
53
54 my $additional_param_hash = {};
55
56 sub additional_parameters {
57 my ($class, $new_value) = @_;
58
59 if (defined($new_value)) {
60 $additional_param_hash->{$class} = $new_value;
61 }
62
63 # return a copy
64 my $copy = {};
65 my $org = $additional_param_hash->{$class} || {};
66 foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
67 return $copy;
68 }
69
70 my $aliases_to_list = sub {
71 my ($aliases) = @_;
72
73 my $list = [];
74 foreach my $k (sort keys %$aliases) {
75 push @$list, $aliases->{$k};
76 }
77 return $list;
78 };
79
80 sub register_get_aliases {
81 my ($class) = @_;
82
83 my $properties = $class->additional_parameters();
84
85 $class->register_method({
86 name => 'get_aliases',
87 path => '',
88 method => 'GET',
89 description => "List aliases",
90 permissions => PVE::Firewall::rules_audit_permissions($class->rule_env()),
91 parameters => {
92 additionalProperties => 0,
93 properties => $properties,
94 },
95 returns => {
96 type => 'array',
97 items => {
98 type => "object",
99 properties => {
100 name => { type => 'string' },
101 cidr => { type => 'string' },
102 comment => {
103 type => 'string',
104 optional => 1,
105 },
106 digest => get_standard_option('pve-config-digest', { optional => 0} ),
107 },
108 },
109 links => [ { rel => 'child', href => "{name}" } ],
110 },
111 code => sub {
112 my ($param) = @_;
113
114 my ($fw_conf, $aliases) = $class->load_config($param);
115
116 my $list = &$aliases_to_list($aliases);
117
118 return PVE::Firewall::copy_list_with_digest($list);
119 }});
120 }
121
122 sub register_create_alias {
123 my ($class) = @_;
124
125 my $properties = $class->additional_parameters();
126
127 $properties->{name} = $api_properties->{name};
128 $properties->{cidr} = $api_properties->{cidr};
129 $properties->{comment} = $api_properties->{comment};
130
131 $class->register_method({
132 name => 'create_alias',
133 path => '',
134 method => 'POST',
135 description => "Create IP or Network Alias.",
136 permissions => PVE::Firewall::rules_modify_permissions($class->rule_env()),
137 protected => 1,
138 parameters => {
139 additionalProperties => 0,
140 properties => $properties,
141 },
142 returns => { type => "null" },
143 code => sub {
144 my ($param) = @_;
145
146 $class->lock_config($param, sub {
147 my ($param) = @_;
148
149 my ($fw_conf, $aliases) = $class->load_config($param);
150
151 my $name = lc($param->{name});
152
153 raise_param_exc({ name => "alias '$param->{name}' already exists" })
154 if defined($aliases->{$name});
155
156 my $data = { name => $param->{name}, cidr => $param->{cidr} };
157 $data->{comment} = $param->{comment} if $param->{comment};
158
159 $aliases->{$name} = $data;
160
161 $class->save_aliases($param, $fw_conf, $aliases);
162 });
163
164 return undef;
165 }});
166 }
167
168 sub register_read_alias {
169 my ($class) = @_;
170
171 my $properties = $class->additional_parameters();
172
173 $properties->{name} = $api_properties->{name};
174
175 $class->register_method({
176 name => 'read_alias',
177 path => '{name}',
178 method => 'GET',
179 description => "Read alias.",
180 permissions => PVE::Firewall::rules_audit_permissions($class->rule_env()),
181 parameters => {
182 additionalProperties => 0,
183 properties => $properties,
184 },
185 returns => { type => "object" },
186 code => sub {
187 my ($param) = @_;
188
189 my ($fw_conf, $aliases) = $class->load_config($param);
190
191 my $name = lc($param->{name});
192
193 raise_param_exc({ name => "no such alias" })
194 if !defined($aliases->{$name});
195
196 return $aliases->{$name};
197 }});
198 }
199
200 sub register_update_alias {
201 my ($class) = @_;
202
203 my $properties = $class->additional_parameters();
204
205 $properties->{name} = $api_properties->{name};
206 $properties->{rename} = $api_properties->{rename};
207 $properties->{cidr} = $api_properties->{cidr};
208 $properties->{comment} = $api_properties->{comment};
209 $properties->{digest} = get_standard_option('pve-config-digest');
210
211 $class->register_method({
212 name => 'update_alias',
213 path => '{name}',
214 method => 'PUT',
215 description => "Update IP or Network alias.",
216 permissions => PVE::Firewall::rules_modify_permissions($class->rule_env()),
217 protected => 1,
218 parameters => {
219 additionalProperties => 0,
220 properties => $properties,
221 },
222 returns => { type => "null" },
223 code => sub {
224 my ($param) = @_;
225
226 $class->lock_config($param, sub {
227 my ($param) = @_;
228
229 my ($fw_conf, $aliases) = $class->load_config($param);
230
231 my $list = &$aliases_to_list($aliases);
232
233 my (undef, $digest) = PVE::Firewall::copy_list_with_digest($list);
234
235 PVE::Tools::assert_if_modified($digest, $param->{digest});
236
237 my $name = lc($param->{name});
238
239 raise_param_exc({ name => "no such alias" }) if !$aliases->{$name};
240
241 my $data = { name => $param->{name}, cidr => $param->{cidr} };
242 $data->{comment} = $param->{comment} if $param->{comment};
243
244 $aliases->{$name} = $data;
245
246 my $rename = $param->{rename};
247 $rename = lc($rename) if $rename;
248
249 if ($rename && ($name ne $rename)) {
250 raise_param_exc({ name => "alias '$param->{rename}' already exists" })
251 if defined($aliases->{$rename});
252 $aliases->{$name}->{name} = $param->{rename};
253 $aliases->{$rename} = $aliases->{$name};
254 delete $aliases->{$name};
255 }
256
257 $class->save_aliases($param, $fw_conf, $aliases);
258 });
259
260 return undef;
261 }});
262 }
263
264 sub register_delete_alias {
265 my ($class) = @_;
266
267 my $properties = $class->additional_parameters();
268
269 $properties->{name} = $api_properties->{name};
270 $properties->{digest} = get_standard_option('pve-config-digest');
271
272 $class->register_method({
273 name => 'remove_alias',
274 path => '{name}',
275 method => 'DELETE',
276 description => "Remove IP or Network alias.",
277 permissions => PVE::Firewall::rules_modify_permissions($class->rule_env()),
278 protected => 1,
279 parameters => {
280 additionalProperties => 0,
281 properties => $properties,
282 },
283 returns => { type => "null" },
284 code => sub {
285 my ($param) = @_;
286
287 $class->lock_config($param, sub {
288 my ($param) = @_;
289
290 my ($fw_conf, $aliases) = $class->load_config($param);
291
292 my $list = &$aliases_to_list($aliases);
293 my (undef, $digest) = PVE::Firewall::copy_list_with_digest($list);
294 PVE::Tools::assert_if_modified($digest, $param->{digest});
295
296 my $name = lc($param->{name});
297 delete $aliases->{$name};
298
299 $class->save_aliases($param, $fw_conf, $aliases);
300 });
301
302 return undef;
303 }});
304 }
305
306 sub register_handlers {
307 my ($class) = @_;
308
309 $class->register_get_aliases();
310 $class->register_create_alias();
311 $class->register_read_alias();
312 $class->register_update_alias();
313 $class->register_delete_alias();
314 }
315
316 package PVE::API2::Firewall::ClusterAliases;
317
318 use strict;
319 use warnings;
320
321 use base qw(PVE::API2::Firewall::AliasesBase);
322
323 sub rule_env {
324 my ($class, $param) = @_;
325
326 return 'cluster';
327 }
328
329 sub lock_config {
330 my ($class, $param, $code) = @_;
331
332 PVE::Firewall::lock_clusterfw_conf(10, $code, $param);
333 }
334
335 sub load_config {
336 my ($class, $param) = @_;
337
338 my $fw_conf = PVE::Firewall::load_clusterfw_conf();
339 my $aliases = $fw_conf->{aliases};
340
341 return ($fw_conf, $aliases);
342 }
343
344 sub save_aliases {
345 my ($class, $param, $fw_conf, $aliases) = @_;
346
347 $fw_conf->{aliases} = $aliases;
348 PVE::Firewall::save_clusterfw_conf($fw_conf);
349 }
350
351 __PACKAGE__->register_handlers();
352
353 package PVE::API2::Firewall::VMAliases;
354
355 use strict;
356 use warnings;
357 use PVE::JSONSchema qw(get_standard_option);
358
359 use base qw(PVE::API2::Firewall::AliasesBase);
360
361 sub rule_env {
362 my ($class, $param) = @_;
363
364 return 'vm';
365 }
366
367 __PACKAGE__->additional_parameters({
368 node => get_standard_option('pve-node'),
369 vmid => get_standard_option('pve-vmid'),
370 });
371
372 sub lock_config {
373 my ($class, $param, $code) = @_;
374
375 PVE::Firewall::lock_vmfw_conf($param->{vmid}, 10, $code, $param);
376 }
377
378 sub load_config {
379 my ($class, $param) = @_;
380
381 my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
382 my $fw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, 'vm', $param->{vmid});
383 my $aliases = $fw_conf->{aliases};
384
385 return ($fw_conf, $aliases);
386 }
387
388 sub save_aliases {
389 my ($class, $param, $fw_conf, $aliases) = @_;
390
391 $fw_conf->{aliases} = $aliases;
392 PVE::Firewall::save_vmfw_conf($param->{vmid}, $fw_conf);
393 }
394
395 __PACKAGE__->register_handlers();
396
397 package PVE::API2::Firewall::CTAliases;
398
399 use strict;
400 use warnings;
401 use PVE::JSONSchema qw(get_standard_option);
402
403 use base qw(PVE::API2::Firewall::AliasesBase);
404
405 sub rule_env {
406 my ($class, $param) = @_;
407
408 return 'ct';
409 }
410
411 __PACKAGE__->additional_parameters({
412 node => get_standard_option('pve-node'),
413 vmid => get_standard_option('pve-vmid'),
414 });
415
416 sub lock_config {
417 my ($class, $param, $code) = @_;
418
419 PVE::Firewall::lock_vmfw_conf($param->{vmid}, 10, $code, $param);
420 }
421
422 sub load_config {
423 my ($class, $param) = @_;
424
425 my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
426 my $fw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, 'ct', $param->{vmid});
427 my $aliases = $fw_conf->{aliases};
428
429 return ($fw_conf, $aliases);
430 }
431
432 sub save_aliases {
433 my ($class, $param, $fw_conf, $aliases) = @_;
434
435 $fw_conf->{aliases} = $aliases;
436 PVE::Firewall::save_vmfw_conf($param->{vmid}, $fw_conf);
437 }
438
439 __PACKAGE__->register_handlers();
440
441 1;