2 # Defines firewall functions for IPtables
4 @actions = ( 'accept', 'drop', 'reject', 'ignore' );
5 $save_file = "$module_config_directory/iptables.save";
6 $prerules = "$module_config_directory/prerules";
7 $postrules = "$module_config_directory/postrules";
8 $prenat = "$module_config_directory/prenat";
9 $postnat = "$module_config_directory/postnat";
10 $premangle = "$module_config_directory/premangle";
11 $postmangle = "$module_config_directory/postmangle";
17 # Turns the firewall configuration into an IPtables save file, and then
21 &deactivate_all_interfaces(); # will add those needed later
23 local @dayname = ( "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" );
26 open(SAVE, ">$save_file");
27 print SAVE "*filter\n";
28 print SAVE ":INPUT ACCEPT [0:0]\n";
29 print SAVE ":OUTPUT ACCEPT [0:0]\n";
30 print SAVE ":FORWARD ACCEPT [0:0]\n";
31 print SAVE ":SYN-FLOOD -\n";
33 # Disable bandwith monitor
34 # Have a lots of issues.
38 #if ($config{'bandwidth'}) {
39 # # Add rules for bandwidth logging
40 # print SAVE "-A INPUT -i $config{'bandwidth'} -j LOG --log-prefix BANDWIDTH_IN: --log-level debug\n";
41 # print SAVE "-A FORWARD -i $config{'bandwidth'} -j LOG --log-prefix BANDWIDTH_IN: --log-level debug\n";
42 # print SAVE "-A FORWARD -o $config{'bandwidth'} -j LOG --log-prefix BANDWIDTH_OUT: --log-level debug\n";
43 # print SAVE "-A OUTPUT -o $config{'bandwidth'} -j LOG --log-prefix BANDWIDTH_OUT: --log-level debug\n";
46 # Add rules for spoofing
47 local ($spoofiface, @nets) = &get_spoof();
51 print SAVE "-A INPUT -i $spoofiface -s $n -j DROP\n";
55 # Always allow established connections
56 print SAVE "-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT\n";
57 print SAVE "-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT\n";
59 # Always allow localhost
60 print SAVE "-A INPUT -i lo -j ACCEPT\n";
61 print SAVE "-A OUTPUT -o lo -j ACCEPT\n";
63 if ($config{'frags'}) {
65 print SAVE "-A INPUT -p ip -f -j DROP\n";
66 print SAVE "-A OUTPUT -p ip -f -j DROP\n";
67 print SAVE "-A FORWARD -p ip -f -j DROP\n";
70 # Add syn flood and spoofing rules
71 local ($flood, $spoof, $fin) = &get_syn();
73 # Limit number of syns / second
74 print SAVE "-A SYN-FLOOD -m limit --limit 1/s --limit-burst 4 -j RETURN\n";
75 print SAVE "-A SYN-FLOOD -j DROP\n";
76 print SAVE "-A INPUT -p tcp -m tcp --syn -j SYN-FLOOD\n";
79 # Drop TCP connection starts without SYN set
80 print SAVE "-A INPUT -p tcp -m tcp ! --syn -m state --state NEW -j DROP\n";
83 # Drop TCP packets with both SYN and FIN
84 print SAVE "-A INPUT -p tcp -m tcp --tcp-flags SYN,FIN SYN,FIN -j DROP\n";
88 open(STATICS, $prerules);
96 local @rules = &list_rules();
97 local %services = map { $_->{'name'}, $_ } &list_services();
98 local %times = map { $_->{'name'}, $_ } &list_times();
99 local @groups = &list_groups();
100 foreach $r (@rules) {
101 next if (!$r->{'enabled'});
102 next if ($r->{'sep'});
104 # Work out all source and destination hosts?
105 local @sources = &expand_hosts($r->{'source'}, \@groups);
106 local @dests = &expand_hosts($r->{'dest'}, \@groups);
110 if ($r->{'time'} ne "*") {
111 local $time = $times{$r->{'time'}};
112 $timearg .= "-m time";
113 if ($time->{'hours'} ne "*") {
114 local ($from, $to) = split(/\-/, $time->{'hours'});
115 $timearg .= " --timestart $from --timestop $to";
117 if ($time->{'days'} ne "*") {
118 $timearg .= " --days ".
119 join(",", map { $dayname[$_] }
120 split(/,/, $time->{'days'}));
124 # Need to output a rule for every possible combination
125 local ($source, $dest);
126 local $aarg = "-j ".uc($r->{'action'});
127 local $n = $r->{'num'};
128 local $logpfx = "--log-prefix RULE_${n}:".uc($r->{'action'}).":";
129 foreach $source (@sources) {
130 $source =~ s/^!(\S.*)$/! $1/;
131 local $sarg = $source eq '*' ? "" :
132 $source =~ /^%(.*)$/ ? "-o $1" :
134 local $me = &my_address_in($source);
136 foreach $dest (@dests) {
137 $dest =~ s/^!(\S.*)$/! $1/;
138 local $darg = $dest eq '*' && !$config{'fw_any'} &&
139 $r->{'action'} eq 'accept' ? "! -d $me" :
141 $dest =~ /^%(.*)$/ ? "-i $1" :
144 if ($r->{'service'} ne '*') {
145 # Output one rule for each real service
146 local ($protos, $ports) =
147 &combine_services($r->{'service'},
149 for($i=0; $i<@$protos; $i++) {
150 local $pr = lc($protos->[$i]);
151 local $pt = $ports->[$i];
152 local $marg = $pr eq 'tcp' ||
153 $pr eq 'udp' || $pr eq 'icmp' ? "-m $pr" : "";
156 # handle old GRE protocols
168 # No need for port number
170 elsif ($pt =~ /^(\d+)$/ || $pt eq '*') {
172 $parg = "--icmp-type $pt" if ($pt ne '*');
175 $parg = "--destination-port $pt";
178 elsif ($pt =~ /^(\d+)\-(\d+)$/) {
179 $parg = "--dport $1:$2";
183 join(",", split(/\s+/, $pt));
184 $marg .= " -m multiport";
187 if ($source !~ /^%(.*)$/) {
188 #if ($dest !~ /^%(.*)$/) {
189 print SAVE "-A INPUT $marg $prarg $timearg $sarg $darg $parg -j LOG $logpfx\n";
191 print SAVE "-A FORWARD $marg $prarg $timearg $sarg $darg $parg -j LOG $logpfx\n";
193 if ($source !~ /^%(.*)$/) {
194 #if ($dest !~ /^%(.*)$/) {
195 print SAVE "-A INPUT $marg $prarg $timearg $sarg $darg $parg $aarg\n";
197 print SAVE "-A FORWARD $marg $prarg $timearg $sarg $darg $parg $aarg\n";
201 # Single service-independent rule
203 if ($source !~ /^%(.*)$/) {
204 #if ($dest !~ /^%(.*)$/) {
205 print SAVE "-A INPUT $timearg $sarg $darg -j LOG $logpfx\n";
207 print SAVE "-A FORWARD $timearg $sarg $darg -j LOG $logpfx\n";
209 if ($source !~ /^%(.*)$/) {
210 #if ($dest !~ /^%(.*)$/) {
211 print SAVE "-A INPUT $timearg $sarg $darg $aarg\n";
213 print SAVE "-A FORWARD $timearg $sarg $darg $aarg\n";
219 open(STATICS, $postrules);
226 print SAVE "COMMIT\n";
229 print SAVE ":PREROUTING ACCEPT [0:0]\n";
230 print SAVE ":POSTROUTING ACCEPT [0:0]\n";
231 print SAVE ":OUTPUT ACCEPT [0:0]\n";
235 local ($natiface, @nets) = &get_nat();
239 @maps = grep { ref($_) } @nets;
240 @nets = grep { !ref($_) } @nets;
242 # Add rules for NAT exclusions
244 foreach $e (grep { $_ =~ /^\!/ } @nets) {
247 local @dests = &expand_hosts("\@$my_e", \@groups);
250 foreach $dest (@dests) {
251 $dest =~ s/^!(\S.*)$/! $1/;
252 #print SAVE "-A POSTROUTING -o $natiface -d $dest -j RETURN\n";
253 #print SAVE "-A PREROUTING -i $natiface -d $dest -j RETURN\n";
254 print SAVE "-A POSTROUTING -d $dest -j RETURN\n";
255 print SAVE "-A PREROUTING -d $dest -j RETURN\n";
260 # Load PREnat After Return
261 open(STATICS, $prenat);
268 # Add rules for static NAT
270 local ($intf_i,$intf_o,$option_i,$option_o);
272 # local @dests = &expand_hosts("\@$my_e", \@groups);
273 local (@tmp,$internal,$external);
277 @tmp = &expand_hosts("\@$m->[1]", \@groups);
279 #@tmp = &expand_hosts("\@$m->[0]", \@groups);
282 $intf_i= " -i $m->[2] ";
283 $intf_o= " -o $m->[2] ";
288 if (&check_netaddress($external)) {
289 $option_i=" -j NETMAP ";
290 $option_o=" -j NETMAP ";
291 } elsif (&check_netaddress($internal)) {
292 $option_o=" -j SNAT ";
294 &activate_interface($m->[2], $external);
297 $option_i=" -j DNAT ";
298 $option_o=" -j SNAT ";
300 &activate_interface($m->[2], $external);
303 (! &check_netaddress($internal) ) && print SAVE "-A PREROUTING $intf_i -d $external $option_i --to $internal\n";
304 print SAVE "-A POSTROUTING $intf_o -s $internal $option_o --to $external\n";
308 open(STATICS, $postnat);
314 # Add rules for dynamic NAT
317 foreach $n (grep { $_ !~ /^\!/ } @nets) {
318 local @sources = &expand_hosts("\@$n", \@groups);
320 foreach $source (@sources) {
321 $source =~ s/^!(\S.*)$/! $1/;
322 print SAVE "-A POSTROUTING -o $natiface -s $source -j MASQUERADE\n";
328 local @forwards = &get_pat();
330 foreach $f (@forwards) {
331 next if (!$f->{'iface'});
332 local ($protos, $ports) = &combine_services($f->{'service'},
335 for($i=0; $i<@$protos; $i++) {
336 local $pr = lc($protos->[$i]);
337 local $pt = $ports->[$i];
338 next if ($pr ne 'tcp' && $pr ne 'udp');
339 print SAVE "-A PREROUTING -m $pr -p $pr --dport $pt -i $f->{'iface'} -j DNAT --to-destination $f->{'host'}:$pt\n";
343 print SAVE "COMMIT\n";
345 print SAVE "*mangle\n";
346 print SAVE ":PREROUTING ACCEPT [0:0]\n";
347 print SAVE ":OUTPUT ACCEPT [0:0]\n";
349 open(STATICS, $premangle);
357 open(STATICS, $postmangle);
362 print SAVE "COMMIT\n";
365 # Apply the save file
366 local $out = `iptables-restore <$save_file 2>&1`;
368 return "iptables-restore output : <pre>$out</pre>";
374 # Cancel all firewall rules and return to the default settings (allow all)
377 &deactivate_all_interfaces();
379 foreach $table ([ "filter", "INPUT", "OUTPUT", "FORWARD" ],
380 [ "nat", "PREROUTING", "POSTROUTING", "OUTPUT" ],
381 [ "mangle", "PREROUTING", "OUTPUT" ]) {
382 local ($name, @chains) = @$table;
384 foreach $cmd ((map { "iptables -t $name -P $_ ACCEPT" } @chains),
385 "iptables -t $name -F",
386 "iptables -t $name -X",
387 "iptables -t $name -Z") {
388 local $out = `$cmd 2>&1`;
390 return "$cmd output : $out";
398 # Enable routing under Linux
401 system("sysctl -w net.ipv4.ip_forward=1 >/dev/null 2>&1");
405 # Disable routing under Linux
408 system("sysctl -w net.ipv4.ip_forward=0 >/dev/null 2>&1");
413 return "/var/log/messages";
418 return -r "/var/log/secure" ? "/var/log/secure" :
419 -r "/var/log/security" ? "/var/log/security" :
420 -r "/var/log/authlog" ? "/var/log/authlog" :
426 return $_[0] =~ /IN=.*OUT=/;
430 @time_now = localtime($time_now);
431 %mmap = ( 'jan' => 0, 'feb' => 1, 'mar' => 2, 'apr' => 3,
432 'may' => 4, 'jun' => 5, 'jul' => 6, 'aug' => 7,
433 'sep' => 8, 'oct' => 9, 'nov' =>10, 'dec' =>11 );
435 # parse_log_line(line)
436 # Parses a line into a log info structure, or returns undef
439 if (&is_log_line($_[0])) {
441 if ($_[0] =~ /RULE_(\d+):([^\s:]+)/) {
442 $info->{'rule'} = $1;
443 $info->{'action'} = lc($2);
445 if ($_[0] =~ /^(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)/) {
446 local $tm = timelocal($5, $4, $3, $2, $mmap{lc($1)}, $time_now[5]);
447 if ($tm > $time_now + 24*60*60) {
448 # Was really last year
449 $tm = timelocal($5, $4, $3, $2, $mmap{lc($1)}, $time_now[5]-1);
451 $info->{'time'} = $tm;
453 $info->{'src_iface'} = $1 if ($_[0] =~ /OUT=(\S*)/);
454 $info->{'dst_iface'} = $1 if ($_[0] =~ /IN=(\S*)/);
455 $info->{'src'} = $1 if ($_[0] =~ /SRC=(\S*)/);
456 $info->{'dst'} = $1 if ($_[0] =~ /DST=(\S*)/);
457 $info->{'size'} = $1 if ($_[0] =~ /LEN=(\S*)/);
458 $info->{'proto'} = $1 if ($_[0] =~ /PROTO=(\S*)/);
459 $info->{'src_port'} = $1 if ($_[0] =~ /SPT=(\S*)/);
460 $info->{'dst_port'} = $1 if ($_[0] =~ /DPT=(\S*)/);
461 $info->{'dst_port'} = $1 if ($_[0] =~ /TYPE=(\S*)/ &&
462 $info->{'proto'} eq 'ICMP');
472 return $_[0]->{'action'} eq 'accept';
477 return $_[0]->{'action'} eq 'drop';
490 sub supports_bandwidth
492 return &foreign_check("bandwidth");