]> git.proxmox.com Git - pve-firewall.git/blob - src/PVE/API2/Firewall/VM.pm
added the 'ipfilter' option
[pve-firewall.git] / src / PVE / API2 / Firewall / VM.pm
1 package PVE::API2::Firewall::VMBase;
2
3 use strict;
4 use warnings;
5 use PVE::JSONSchema qw(get_standard_option);
6 use PVE::Cluster;
7 use PVE::Firewall;
8 use PVE::API2::Firewall::Rules;
9 use PVE::API2::Firewall::Aliases;
10
11 use Data::Dumper; # fixme: remove
12
13 use base qw(PVE::RESTHandler);
14
15 my $option_properties = {
16 enable => {
17 description => "Enable host firewall rules.",
18 type => 'boolean',
19 optional => 1,
20 },
21 macfilter => {
22 description => "Enable/disable MAC address filter.",
23 type => 'boolean',
24 optional => 1,
25 },
26 dhcp => {
27 description => "Enable DHCP.",
28 type => 'boolean',
29 optional => 1,
30 },
31 ndp => {
32 description => "Enable NDP.",
33 type => 'boolean',
34 optional => 1,
35 },
36 radv => {
37 description => "Allow sending Router Advertisement.",
38 type => 'boolean',
39 optional => 1,
40 },
41 ipfilter => {
42 description => "Enable default IP filters. " .
43 "This is equivalent to adding an empty ipfilter-net<id> ipset " .
44 "for every interface. Such ipsets implicitly contain sane default " .
45 "restrictions such as restricting IPv6 link local addresses to " .
46 "the one derived from the interface's MAC address.",
47 type => 'boolean',
48 optional => 1,
49 },
50 policy_in => {
51 description => "Input policy.",
52 type => 'string',
53 optional => 1,
54 enum => ['ACCEPT', 'REJECT', 'DROP'],
55 },
56 policy_out => {
57 description => "Output policy.",
58 type => 'string',
59 optional => 1,
60 enum => ['ACCEPT', 'REJECT', 'DROP'],
61 },
62 log_level_in => get_standard_option('pve-fw-loglevel', {
63 description => "Log level for incoming traffic." }),
64 log_level_out => get_standard_option('pve-fw-loglevel', {
65 description => "Log level for outgoing traffic." }),
66
67 };
68
69 my $add_option_properties = sub {
70 my ($properties) = @_;
71
72 foreach my $k (keys %$option_properties) {
73 $properties->{$k} = $option_properties->{$k};
74 }
75
76 return $properties;
77 };
78
79 sub register_handlers {
80 my ($class, $rule_env) = @_;
81
82 $class->register_method({
83 name => 'index',
84 path => '',
85 method => 'GET',
86 permissions => { user => 'all' },
87 description => "Directory index.",
88 parameters => {
89 additionalProperties => 0,
90 properties => {
91 node => get_standard_option('pve-node'),
92 vmid => get_standard_option('pve-vmid'),
93 },
94 },
95 returns => {
96 type => 'array',
97 items => {
98 type => "object",
99 properties => {},
100 },
101 links => [ { rel => 'child', href => "{name}" } ],
102 },
103 code => sub {
104 my ($param) = @_;
105
106 my $result = [
107 { name => 'rules' },
108 { name => 'aliases' },
109 { name => 'ipset' },
110 { name => 'refs' },
111 { name => 'options' },
112 ];
113
114 return $result;
115 }});
116
117
118 $class->register_method({
119 name => 'get_options',
120 path => 'options',
121 method => 'GET',
122 description => "Get VM firewall options.",
123 proxyto => 'node',
124 permissions => {
125 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
126 },
127 parameters => {
128 additionalProperties => 0,
129 properties => {
130 node => get_standard_option('pve-node'),
131 vmid => get_standard_option('pve-vmid'),
132 },
133 },
134 returns => {
135 type => "object",
136 #additionalProperties => 1,
137 properties => $option_properties,
138 },
139 code => sub {
140 my ($param) = @_;
141
142 my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
143 my $vmfw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, $rule_env, $param->{vmid});
144
145 return PVE::Firewall::copy_opject_with_digest($vmfw_conf->{options});
146 }});
147
148 $class->register_method({
149 name => 'set_options',
150 path => 'options',
151 method => 'PUT',
152 description => "Set Firewall options.",
153 protected => 1,
154 proxyto => 'node',
155 permissions => {
156 check => ['perm', '/vms/{vmid}', [ 'VM.Config.Network' ]],
157 },
158 parameters => {
159 additionalProperties => 0,
160 properties => &$add_option_properties({
161 node => get_standard_option('pve-node'),
162 vmid => get_standard_option('pve-vmid'),
163 delete => {
164 type => 'string', format => 'pve-configid-list',
165 description => "A list of settings you want to delete.",
166 optional => 1,
167 },
168 digest => get_standard_option('pve-config-digest'),
169 }),
170 },
171 returns => { type => "null" },
172 code => sub {
173 my ($param) = @_;
174
175
176 my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
177 my $vmfw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, $rule_env, $param->{vmid});
178
179 my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($vmfw_conf->{options});
180 PVE::Tools::assert_if_modified($digest, $param->{digest});
181
182 if ($param->{delete}) {
183 foreach my $opt (PVE::Tools::split_list($param->{delete})) {
184 raise_param_exc({ delete => "no such option '$opt'" })
185 if !$option_properties->{$opt};
186 delete $vmfw_conf->{options}->{$opt};
187 }
188 }
189
190 if (defined($param->{enable})) {
191 $param->{enable} = $param->{enable} ? 1 : 0;
192 }
193
194 foreach my $k (keys %$option_properties) {
195 next if !defined($param->{$k});
196 $vmfw_conf->{options}->{$k} = $param->{$k};
197 }
198
199 PVE::Firewall::save_vmfw_conf($param->{vmid}, $vmfw_conf);
200
201 return undef;
202 }});
203
204 $class->register_method({
205 name => 'log',
206 path => 'log',
207 method => 'GET',
208 description => "Read firewall log",
209 proxyto => 'node',
210 permissions => {
211 check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
212 },
213 protected => 1,
214 parameters => {
215 additionalProperties => 0,
216 properties => {
217 node => get_standard_option('pve-node'),
218 vmid => get_standard_option('pve-vmid'),
219 start => {
220 type => 'integer',
221 minimum => 0,
222 optional => 1,
223 },
224 limit => {
225 type => 'integer',
226 minimum => 0,
227 optional => 1,
228 },
229 },
230 },
231 returns => {
232 type => 'array',
233 items => {
234 type => "object",
235 properties => {
236 n => {
237 description=> "Line number",
238 type=> 'integer',
239 },
240 t => {
241 description=> "Line text",
242 type => 'string',
243 }
244 }
245 }
246 },
247 code => sub {
248 my ($param) = @_;
249
250 my $rpcenv = PVE::RPCEnvironment::get();
251 my $user = $rpcenv->get_user();
252 my $vmid = $param->{vmid};
253
254 my ($count, $lines) = PVE::Tools::dump_logfile("/var/log/pve-firewall.log",
255 $param->{start}, $param->{limit},
256 "^$vmid ");
257
258 $rpcenv->set_result_attrib('total', $count);
259
260 return $lines;
261 }});
262
263
264 $class->register_method({
265 name => 'refs',
266 path => 'refs',
267 method => 'GET',
268 description => "Lists possible IPSet/Alias reference which are allowed in source/dest properties.",
269 permissions => {
270 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
271 },
272 parameters => {
273 additionalProperties => 0,
274 properties => {
275 node => get_standard_option('pve-node'),
276 vmid => get_standard_option('pve-vmid'),
277 type => {
278 description => "Only list references of specified type.",
279 type => 'string',
280 enum => ['alias', 'ipset'],
281 optional => 1,
282 },
283 },
284 },
285 returns => {
286 type => 'array',
287 items => {
288 type => "object",
289 properties => {
290 type => {
291 type => 'string',
292 enum => ['alias', 'ipset'],
293 },
294 name => {
295 type => 'string',
296 },
297 comment => {
298 type => 'string',
299 optional => 1,
300 },
301 },
302 },
303 },
304 code => sub {
305 my ($param) = @_;
306
307 my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
308 my $fw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, $rule_env, $param->{vmid});
309
310 my $ipsets = {};
311 my $aliases = {};
312
313 foreach my $conf (($cluster_conf, $fw_conf)) {
314 next if !$conf;
315 if (!$param->{type} || $param->{type} eq 'ipset') {
316 foreach my $name (keys %{$conf->{ipset}}) {
317 my $data = {
318 type => 'ipset',
319 name => $name,
320 ref => "+$name",
321 };
322 if (my $comment = $conf->{ipset_comments}->{$name}) {
323 $data->{comment} = $comment;
324 }
325 $ipsets->{$name} = $data;
326 }
327 }
328
329 if (!$param->{type} || $param->{type} eq 'alias') {
330 foreach my $name (keys %{$conf->{aliases}}) {
331 my $e = $conf->{aliases}->{$name};
332 my $data = {
333 type => 'alias',
334 name => $name,
335 ref => $name,
336 };
337 $data->{comment} = $e->{comment} if $e->{comment};
338 $aliases->{$name} = $data;
339 }
340 }
341 }
342
343 my $res = [];
344 foreach my $e (values %$ipsets) { push @$res, $e; };
345 foreach my $e (values %$aliases) { push @$res, $e; };
346
347 return $res;
348 }});
349 }
350
351 package PVE::API2::Firewall::VM;
352
353 use strict;
354 use warnings;
355
356 use base qw(PVE::API2::Firewall::VMBase);
357
358 __PACKAGE__->register_method ({
359 subclass => "PVE::API2::Firewall::VMRules",
360 path => 'rules',
361 });
362
363 __PACKAGE__->register_method ({
364 subclass => "PVE::API2::Firewall::VMAliases",
365 path => 'aliases',
366 });
367
368 __PACKAGE__->register_method ({
369 subclass => "PVE::API2::Firewall::VMIPSetList",
370 path => 'ipset',
371 });
372
373 __PACKAGE__->register_handlers('vm');
374
375 package PVE::API2::Firewall::CT;
376
377 use strict;
378 use warnings;
379
380 use base qw(PVE::API2::Firewall::VMBase);
381
382 __PACKAGE__->register_method ({
383 subclass => "PVE::API2::Firewall::CTRules",
384 path => 'rules',
385 });
386
387 __PACKAGE__->register_method ({
388 subclass => "PVE::API2::Firewall::CTAliases",
389 path => 'aliases',
390 });
391
392 __PACKAGE__->register_method ({
393 subclass => "PVE::API2::Firewall::CTIPSetList",
394 path => 'ipset',
395 });
396
397 __PACKAGE__->register_handlers('vm');
398
399 1;