read in shorewall macros
[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
59 if (!($action && $iface && $source && $dest)) {
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
78 if ($iface !~ m/^(all|net0|net1|net2|net3|net4|net5)$/) {
79 warn "unknown interface '$iface'\n";
80 next;
81 }
82
83 if ($proto && $proto !~ m/^(icmp|tcp|udp)$/) {
84 warn "unknown protokol '$proto'\n";
85 next;
86 }
87
88 if ($source !~ m/^(any)$/) {
89 warn "unknown source '$source'\n";
90 next;
91 }
92
93 if ($dest !~ m/^(any)$/) {
94 warn "unknown destination '$dest'\n";
95 next;
96 }
97
98 my $rule = {
99 action => $action,
9aab3127 100 service => $service,
dddd9413
DM
101 iface => $iface,
102 source => $source,
103 dest => $dest,
104 proto => $proto,
105 dport => $dport,
106 sport => $sport,
107 };
108
109 push @{$res->{$section}}, $rule;
110 }
111
112 return $res;
113}
114
80bfe1ff
DM
115sub read_local_vm_config {
116
117 my $openvz = {};
118
119 my $qemu = {};
f789653a 120
80bfe1ff 121 my $list = PVE::QemuServer::config_list();
dddd9413 122
80bfe1ff
DM
123 foreach my $vmid (keys %$list) {
124 my $cfspath = PVE::QemuServer::cfs_config_path($vmid);
125 if (my $conf = PVE::Cluster::cfs_read_file($cfspath)) {
126 $qemu->{$vmid} = $conf;
127 }
128 }
129
130 my $vmdata = { openvz => $openvz, qemu => $qemu };
131
132 return $vmdata;
133};
134
135sub read_vm_firewall_rules {
136 my ($vmdata) = @_;
80bfe1ff
DM
137 my $rules = {};
138 foreach my $vmid (keys %{$vmdata->{qemu}}, keys %{$vmdata->{openvz}}) {
9aab3127 139 my $filename = "/etc/pve/firewall/$vmid.fw";
80bfe1ff
DM
140 my $fh = IO::File->new($filename, O_RDONLY);
141 next if !$fh;
142
143 $rules->{$vmid} = parse_fw_rules($filename, $fh);
144 }
145
146 return $rules;
dddd9413
DM
147}
148
80bfe1ff
DM
149__PACKAGE__->register_method ({
150 name => 'compile',
151 path => 'compile',
152 method => 'POST',
153 description => "Compile firewall rules.",
154 parameters => {
155 additionalProperties => 0,
156 properties => {},
157 },
158 returns => { type => 'null' },
159
160 code => sub {
161 my ($param) = @_;
162
163 my $vmdata = read_local_vm_config();
9aab3127 164 my $rules = read_vm_firewall_rules($vmdata);
f789653a 165
80bfe1ff
DM
166 # print Dumper($vmdata);
167
168 my $swdir = '/etc/shorewall';
169 mkdir $swdir;
170
171 PVE::Firewall::compile($swdir, $vmdata, $rules);
172
173 PVE::Tools::run_command(['shorewall', 'compile']);
174
175 return undef;
176
177 }});
178
179__PACKAGE__->register_method ({
180 name => 'start',
181 path => 'start',
182 method => 'POST',
183 description => "Start firewall.",
184 parameters => {
185 additionalProperties => 0,
186 properties => {},
187 },
188 returns => { type => 'null' },
189
190 code => sub {
191 my ($param) = @_;
192
193 PVE::Tools::run_command(['shorewall', 'start']);
194
195 return undef;
196 }});
197
198__PACKAGE__->register_method ({
199 name => 'stop',
200 path => 'stop',
201 method => 'POST',
202 description => "Stop firewall.",
203 parameters => {
204 additionalProperties => 0,
205 properties => {},
206 },
207 returns => { type => 'null' },
208
209 code => sub {
210 my ($param) = @_;
211
212 PVE::Tools::run_command(['shorewall', 'stop']);
213
214 return undef;
215 }});
216
217__PACKAGE__->register_method ({
218 name => 'clear',
219 path => 'clear',
220 method => 'POST',
221 description => "Clear will remove all rules installed by this script. The host is then unprotected.",
222 parameters => {
223 additionalProperties => 0,
224 properties => {},
225 },
226 returns => { type => 'null' },
227
228 code => sub {
229 my ($param) = @_;
230
231 PVE::Tools::run_command(['shorewall', 'clear']);
232
233 return undef;
234 }});
235
236my $nodename = PVE::INotify::nodename();
237
238my $cmddef = {
239 compile => [ __PACKAGE__, 'compile', []],
240 start => [ __PACKAGE__, 'start', []],
241 stop => [ __PACKAGE__, 'stop', []],
242 clear => [ __PACKAGE__, 'clear', []],
243};
244
245my $cmd = shift;
246
247PVE::CLIHandler::handle_cmd($cmddef, "pvefw", $cmd, \@ARGV, undef, $0);
b6360c3f
DM
248
249exit(0);
80bfe1ff 250