]> git.proxmox.com Git - pve-firewall.git/blob - pvefw
978ffb15cdac07e95364d9c52bd137da2cd682f8
[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
37 sub parse_fw_rules {
38 my ($filename, $fh) = @_;
39
40 my $section;
41
42 my $res = { in => [], out => [] };
43
44 my $macros = PVE::Firewall::get_shorewall_macros();
45
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
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 {
74 warn "unknown action '$action'\n";
75 next;
76 }
77
78 $iface = undef if $iface eq '-';
79 if ($iface && $iface !~ m/^(net0|net1|net2|net3|net4|net5)$/) {
80 warn "unknown interface '$iface'\n";
81 next;
82 }
83
84 $proto = undef if $proto eq '-';
85 if ($proto && $proto !~ m/^(icmp|tcp|udp)$/) {
86 warn "unknown protokol '$proto'\n";
87 next;
88 }
89
90 $source = undef if $source eq '-';
91
92 # if ($source !~ m/^(XYZ)$/) {
93 # warn "unknown source '$source'\n";
94 # next;
95 # }
96
97 $dest = undef if $dest eq '-';
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 '-';
105
106 my $rule = {
107 action => $action,
108 service => $service,
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
123 sub read_local_vm_config {
124
125 my $openvz = {};
126
127 my $qemu = {};
128
129 my $list = PVE::QemuServer::config_list();
130
131 foreach my $vmid (keys %$list) {
132 # next if $vmid ne '100';
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
144 sub read_vm_firewall_rules {
145 my ($vmdata) = @_;
146 my $rules = {};
147 foreach my $vmid (keys %{$vmdata->{qemu}}, keys %{$vmdata->{openvz}}) {
148 my $filename = "/etc/pve/firewall/$vmid.fw";
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;
156 }
157
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();
173 my $rules = read_vm_firewall_rules($vmdata);
174
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
245 my $nodename = PVE::INotify::nodename();
246
247 my $cmddef = {
248 compile => [ __PACKAGE__, 'compile', []],
249 start => [ __PACKAGE__, 'start', []],
250 stop => [ __PACKAGE__, 'stop', []],
251 clear => [ __PACKAGE__, 'clear', []],
252 };
253
254 my $cmd = shift;
255
256 PVE::CLIHandler::handle_cmd($cmddef, "pvefw", $cmd, \@ARGV, undef, $0);
257
258 exit(0);
259