better documentation
[pve-firewall.git] / pvefw
CommitLineData
b6360c3f
DM
1#!/usr/bin/perl -w
2
3use strict;
4use lib qw(.);
5use PVE::Firewall;
f789653a 6use File::Path;
dddd9413 7use IO::File;
80bfe1ff 8use Data::Dumper;
dddd9413 9
80bfe1ff
DM
10use PVE::SafeSyslog;
11use PVE::Cluster;
12use PVE::INotify;
13use PVE::RPCEnvironment;
14use PVE::QemuServer;
b6360c3f 15
80bfe1ff
DM
16use PVE::JSONSchema qw(get_standard_option);
17
18use PVE::CLIHandler;
19
20use base qw(PVE::CLIHandler);
21
22$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
23
24initlog ('pvefw');
25
26die "please run as root\n" if $> != 0;
27
28PVE::INotify::inotify_init();
29
30my $rpcenv = PVE::RPCEnvironment->init('cli');
31
32$rpcenv->init_request();
33$rpcenv->set_language($ENV{LANG});
34$rpcenv->set_user('root@pam');
b6360c3f 35
9aab3127 36
dddd9413
DM
37sub parse_fw_rules {
38 my ($filename, $fh) = @_;
39
40 my $section;
41
42 my $res = { in => [], out => [] };
43
9aab3127
DM
44 my $macros = PVE::Firewall::get_shorewall_macros();
45
dddd9413
DM
46 while (defined(my $line = <$fh>)) {
47 next if $line =~ m/^#/;
48 next if $line =~ m/^\s*$/;
49
50 if ($line =~ m/^\[(in|out)\]\s*$/i) {
51 $section = lc($1);
52 next;
53 }
54 next if !$section;
55
56 my ($action, $iface, $source, $dest, $proto, $dport, $sport) =
57 split(/\s+/, $line);
58
9a4644fa 59 if (!$action) {
dddd9413
DM
60 warn "skip incomplete line\n";
61 next;
62 }
63
9aab3127
DM
64 my $service;
65 if ($action =~ m/^(ACCEPT|DROP|REJECT)$/) {
66 # OK
67 } elsif ($action =~ m/^(\S+)\((ACCEPT|DROP|REJECT)\)$/) {
68 ($service, $action) = ($1, $2);
69 if (!$macros->{$service}) {
70 warn "unknown service '$service'\n";
71 next;
72 }
73 } else {
dddd9413 74 warn "unknown action '$action'\n";
9aab3127 75 next;
dddd9413
DM
76 }
77
9a4644fa 78 $iface = undef if $iface && $iface eq '-';
8cebfa6f 79 if ($iface && $iface !~ m/^(net0|net1|net2|net3|net4|net5)$/) {
dddd9413
DM
80 warn "unknown interface '$iface'\n";
81 next;
82 }
83
9a4644fa 84 $proto = undef if $proto && $proto eq '-';
dddd9413
DM
85 if ($proto && $proto !~ m/^(icmp|tcp|udp)$/) {
86 warn "unknown protokol '$proto'\n";
87 next;
88 }
89
9a4644fa 90 $source = undef if $source && $source eq '-';
dddd9413 91
8cebfa6f
DM
92# if ($source !~ m/^(XYZ)$/) {
93# warn "unknown source '$source'\n";
94# next;
95# }
96
9a4644fa 97 $dest = undef if $dest && $dest eq '-';
8cebfa6f
DM
98# if ($dest !~ m/^XYZ)$/) {
99# warn "unknown destination '$dest'\n";
100# next;
101# }
102
103 $dport = undef if $dport && $dport eq '-';
104 $sport = undef if $sport && $sport eq '-';
dddd9413
DM
105
106 my $rule = {
107 action => $action,
9aab3127 108 service => $service,
dddd9413
DM
109 iface => $iface,
110 source => $source,
111 dest => $dest,
112 proto => $proto,
113 dport => $dport,
114 sport => $sport,
115 };
116
117 push @{$res->{$section}}, $rule;
118 }
119
120 return $res;
121}
122
80bfe1ff
DM
123sub read_local_vm_config {
124
125 my $openvz = {};
126
127 my $qemu = {};
f789653a 128
80bfe1ff 129 my $list = PVE::QemuServer::config_list();
dddd9413 130
80bfe1ff 131 foreach my $vmid (keys %$list) {
8cebfa6f 132 # next if $vmid ne '100';
80bfe1ff
DM
133 my $cfspath = PVE::QemuServer::cfs_config_path($vmid);
134 if (my $conf = PVE::Cluster::cfs_read_file($cfspath)) {
135 $qemu->{$vmid} = $conf;
136 }
137 }
138
139 my $vmdata = { openvz => $openvz, qemu => $qemu };
140
141 return $vmdata;
142};
143
144sub read_vm_firewall_rules {
145 my ($vmdata) = @_;
80bfe1ff
DM
146 my $rules = {};
147 foreach my $vmid (keys %{$vmdata->{qemu}}, keys %{$vmdata->{openvz}}) {
9aab3127 148 my $filename = "/etc/pve/firewall/$vmid.fw";
80bfe1ff
DM
149 my $fh = IO::File->new($filename, O_RDONLY);
150 next if !$fh;
151
152 $rules->{$vmid} = parse_fw_rules($filename, $fh);
153 }
154
155 return $rules;
dddd9413
DM
156}
157
80bfe1ff
DM
158__PACKAGE__->register_method ({
159 name => 'compile',
160 path => 'compile',
161 method => 'POST',
162 description => "Compile firewall rules.",
163 parameters => {
164 additionalProperties => 0,
165 properties => {},
166 },
167 returns => { type => 'null' },
168
169 code => sub {
170 my ($param) = @_;
171
172 my $vmdata = read_local_vm_config();
9aab3127 173 my $rules = read_vm_firewall_rules($vmdata);
f789653a 174
80bfe1ff
DM
175 # print Dumper($vmdata);
176
177 my $swdir = '/etc/shorewall';
178 mkdir $swdir;
179
180 PVE::Firewall::compile($swdir, $vmdata, $rules);
181
182 PVE::Tools::run_command(['shorewall', 'compile']);
183
184 return undef;
185
186 }});
187
188__PACKAGE__->register_method ({
189 name => 'start',
190 path => 'start',
191 method => 'POST',
192 description => "Start firewall.",
193 parameters => {
194 additionalProperties => 0,
195 properties => {},
196 },
197 returns => { type => 'null' },
198
199 code => sub {
200 my ($param) = @_;
201
202 PVE::Tools::run_command(['shorewall', 'start']);
203
204 return undef;
205 }});
206
207__PACKAGE__->register_method ({
208 name => 'stop',
209 path => 'stop',
210 method => 'POST',
211 description => "Stop firewall.",
212 parameters => {
213 additionalProperties => 0,
214 properties => {},
215 },
216 returns => { type => 'null' },
217
218 code => sub {
219 my ($param) = @_;
220
221 PVE::Tools::run_command(['shorewall', 'stop']);
222
223 return undef;
224 }});
225
226__PACKAGE__->register_method ({
227 name => 'clear',
228 path => 'clear',
229 method => 'POST',
230 description => "Clear will remove all rules installed by this script. The host is then unprotected.",
231 parameters => {
232 additionalProperties => 0,
233 properties => {},
234 },
235 returns => { type => 'null' },
236
237 code => sub {
238 my ($param) = @_;
239
240 PVE::Tools::run_command(['shorewall', 'clear']);
241
242 return undef;
243 }});
244
245my $nodename = PVE::INotify::nodename();
246
247my $cmddef = {
248 compile => [ __PACKAGE__, 'compile', []],
249 start => [ __PACKAGE__, 'start', []],
250 stop => [ __PACKAGE__, 'stop', []],
251 clear => [ __PACKAGE__, 'clear', []],
252};
253
254my $cmd = shift;
255
256PVE::CLIHandler::handle_cmd($cmddef, "pvefw", $cmd, \@ARGV, undef, $0);
b6360c3f
DM
257
258exit(0);
80bfe1ff 259