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