X-Git-Url: http://git.ipfire.org/?p=people%2Fteissler%2Fipfire-2.x.git;a=blobdiff_plain;f=config%2Ffirewall%2Frules.pl;h=a0bc32c9665da8e34d5af3c37ef82b50c3d02ca8;hp=4bb40a4f9c7b7f65a2b460bdea1d50117fe186ad;hb=025741919a54ceb2ce96961e74f3afd1ad10706b;hpb=179deb37d02efbb6c180568ef361a7caf3ede70e diff --git a/config/firewall/rules.pl b/config/firewall/rules.pl index 4bb40a4f9..a0bc32c96 100755 --- a/config/firewall/rules.pl +++ b/config/firewall/rules.pl @@ -39,6 +39,7 @@ my $CHAIN_NAT_SOURCE = "NAT_SOURCE"; my $CHAIN_NAT_DESTINATION = "NAT_DESTINATION"; my $CHAIN_MANGLE_NAT_DESTINATION_FIX = "NAT_DESTINATION"; my @VALID_CHAINS = ($CHAIN_INPUT, $CHAIN_FORWARD, $CHAIN_OUTPUT); +my @ANY_ADDRESSES = ("0.0.0.0/0.0.0.0", "0.0.0.0/0", "0/0"); my @PROTOCOLS = ("tcp", "udp", "icmp", "igmp", "ah", "esp", "gre", "ipv6", "ipip"); my @PROTOCOLS_WITH_PORTS = ("tcp", "udp"); @@ -169,10 +170,13 @@ sub buildrules { } # Collect all sources. - my @sources = &get_addresses($hash, $key, "src"); + my @sources = &fwlib::get_addresses($hash, $key, "src"); # Collect all destinations. - my @destinations = &get_addresses($hash, $key, "tgt"); + my @destinations = &fwlib::get_addresses($hash, $key, "tgt"); + + # True if the destination is the firewall itself. + my $destination_is_firewall = ($$hash{$key}[5] eq "ipfire"); # Check if logging should be enabled. my $LOG = ($$hash{$key}[17] eq 'ON'); @@ -245,22 +249,36 @@ sub buildrules { } # Prepare protocol options (like ICMP types, ports, etc...). - my @protocol_options = &get_protocol_options($hash, $key, $protocol); + my @protocol_options = &get_protocol_options($hash, $key, $protocol, 0); # Check if this protocol knows ports. my $protocol_has_ports = ($protocol ~~ @PROTOCOLS_WITH_PORTS); - foreach my $source (@sources) { - foreach my $destination (@destinations) { + foreach my $src (@sources) { + # Skip invalid source. + next unless ($src); + + # Sanitize source. + my $source = $src; + if ($source ~~ @ANY_ADDRESSES) { + $source = ""; + } + + foreach my $dst (@destinations) { # Skip invalid rules. - next if (!$source || !$destination || ($destination eq "none")); + next if (!$dst || ($dst eq "none")); + + # Sanitize destination. + my $destination = $dst; + if ($destination ~~ @ANY_ADDRESSES) { + $destination = ""; + } # Array with iptables arguments. my @options = (); # Append protocol. if ($protocol ne "all") { - push(@options, ("-p", $protocol)); push(@options, @protocol_options); } @@ -268,19 +286,27 @@ sub buildrules { my @source_options = (); if ($source =~ /mac/) { push(@source_options, $source); - } else { + } elsif ($source) { push(@source_options, ("-s", $source)); } # Prepare destination options. - my @destination_options = ("-d", $destination); + my @destination_options = (); + if ($destination) { + push(@destination_options, ("-d", $destination)); + } # Add time constraint options. push(@options, @time_options); + my $firewall_is_in_source_subnet = 0; + if ($source) { + $firewall_is_in_source_subnet = &firewall_is_in_subnet($source); + } + # Process NAT rules. if ($NAT) { - my $nat_address = &get_nat_address($$hash{$key}[29]); + my $nat_address = &fwlib::get_nat_address($$hash{$key}[29], $source); # Skip NAT rules if the NAT address is unknown # (i.e. no internet connection has been established, yet). @@ -289,27 +315,57 @@ sub buildrules { # Destination NAT if ($NAT_MODE eq "DNAT") { # Make port-forwardings useable from the internal networks. - &add_dnat_mangle_rules($nat_address, @options); + my @internal_addresses = &fwlib::get_internal_firewall_ip_addresses(1); + unless ($nat_address ~~ @internal_addresses) { + &add_dnat_mangle_rules($nat_address, @options); + } - my @nat_options = @options; + my @nat_options = (); + if ($protocol ne "all") { + my @nat_protocol_options = &get_protocol_options($hash, $key, $protocol, 1); + push(@nat_options, @nat_protocol_options); + } push(@nat_options, @source_options); push(@nat_options, ("-d", $nat_address)); + push(@nat_options, @time_options); - my ($dnat_address, $dnat_mask) = split("/", $destination); - @destination_options = ("-d", $dnat_address); - + my $dnat_port; if ($protocol_has_ports) { - my $dnat_port = &get_dnat_target_port($hash, $key); + $dnat_port = &get_dnat_target_port($hash, $key); + } - if ($dnat_port) { - $dnat_address .= ":$dnat_port"; + my @nat_action_options = (); + + # Use iptables REDIRECT + my $use_redirect = ($destination_is_firewall && !$destination && $protocol_has_ports && $dnat_port); + if ($use_redirect) { + push(@nat_action_options, ("-j", "REDIRECT", "--to-ports", $dnat_port)); + + # Use iptables DNAT + } else { + if ($destination_is_firewall && !$destination) { + $destination = &fwlib::get_external_address(); } + next unless ($destination); + + my ($dnat_address, $dnat_mask) = split("/", $destination); + @destination_options = ("-d", $dnat_address); + + if ($protocol_has_ports) { + my $dnat_port = &get_dnat_target_port($hash, $key); + + if ($dnat_port) { + $dnat_address .= ":$dnat_port"; + } + } + + push(@nat_action_options, ("-j", "DNAT", "--to-destination", $dnat_address)); } if ($LOG) { run("$IPTABLES -t nat -A $CHAIN_NAT_DESTINATION @nat_options @log_limit_options -j LOG --log-prefix 'DNAT '"); } - run("$IPTABLES -t nat -A $CHAIN_NAT_DESTINATION @nat_options -j DNAT --to-destination $dnat_address"); + run("$IPTABLES -t nat -A $CHAIN_NAT_DESTINATION @nat_options @nat_action_options"); # Source NAT } elsif ($NAT_MODE eq "SNAT") { @@ -326,6 +382,14 @@ sub buildrules { } push(@options, @source_options); + + if ($firewall_is_in_source_subnet && ($fwdfwsettings{"POLICY"} eq "MODE1") && ($chain eq $CHAIN_FORWARD)) { + if ($LOG && !$NAT) { + run("$IPTABLES -A $CHAIN_INPUT @options @log_limit_options -j LOG --log-prefix '$CHAIN_INPUT '"); + } + run("$IPTABLES -A $CHAIN_INPUT @options -j $target"); + } + push(@options, @destination_options); # Insert firewall rule. @@ -339,49 +403,6 @@ sub buildrules { } } -sub get_external_interface() { - open(IFACE, "/var/ipfire/red/iface") or return ""; - my $iface = ; - close(IFACE); - - return $iface; -} - -sub get_external_address() { - open(ADDR, "/var/ipfire/red/local-ipaddress") or return ""; - my $address = ; - close(ADDR); - - return $address; -} - -sub get_alias { - my $id = shift; - - foreach my $alias (sort keys %aliases) { - if ($id eq $alias) { - return $aliases{$alias}; - } - } -} - -sub get_nat_address { - my $zone = shift; - - # Any static address of any zone. - if ($zone eq "RED" || $zone eq "GREEN" || $zone eq "ORANGE" || $zone eq "BLUE") { - return $defaultNetworks{$zone . "_ADDRESS"}; - - } elsif ($zone eq "Default IP") { - return &get_external_address(); - - } else { - return &get_alias($zone); - } - - print_error("Could not find NAT address"); -} - # Formats the given timestamp into the iptables format which is "hh:mm" UTC. sub format_time { my $val = shift; @@ -447,155 +468,6 @@ sub p2pblock { } } -sub get_addresses { - my $hash = shift; - my $key = shift; - my $type = shift; - - my @addresses = (); - my $addr_type; - my $value; - my $group_name; - - if ($type eq "src") { - $addr_type = $$hash{$key}[3]; - $value = $$hash{$key}[4]; - - } elsif ($type eq "tgt") { - $addr_type = $$hash{$key}[5]; - $value = $$hash{$key}[6]; - } - - if ($addr_type ~~ ["cust_grp_src", "cust_grp_tgt"]) { - foreach my $grp (sort {$a <=> $b} keys %customgrp) { - if ($customgrp{$grp}[0] eq $value) { - my @address = &get_address($customgrp{$grp}[3], $customgrp{$grp}[2], $type); - - if (@address) { - push(@addresses, @address); - } - } - } - } else { - my @address = &get_address($addr_type, $value, $type); - - if (@address) { - push(@addresses, @address); - } - } - - return @addresses; -} - -sub get_address { - my $key = shift; - my $value = shift; - my $type = shift; - - my @ret = (); - - # If the user manually typed an address, we just check if it is a MAC - # address. Otherwise, we assume that it is an IP address. - if ($key ~~ ["src_addr", "tgt_addr"]) { - if (&General::validmac($value)) { - push(@ret, "-m mac --mac-source $value"); - } else { - push(@ret, $value); - } - - # If a default network interface (GREEN, BLUE, etc.) is selected, we - # try to get the corresponding address of the network. - } elsif ($key ~~ ["std_net_src", "std_net_tgt", "Standard Network"]) { - my $external_interface = &get_external_interface(); - - my $network_address = &fwlib::get_std_net_ip($value, $external_interface); - if ($network_address) { - push(@ret, $network_address); - } - - # Custom networks. - } elsif ($key ~~ ["cust_net_src", "cust_net_tgt", "Custom Network"]) { - my $network_address = &fwlib::get_net_ip($value); - if ($network_address) { - push(@ret, $network_address); - } - - # Custom hosts. - } elsif ($key ~~ ["cust_host_src", "cust_host_tgt", "Custom Host"]) { - my $host_address = &fwlib::get_host_ip($value, $type); - if ($host_address) { - push(@ret, $host_address); - } - - # OpenVPN networks. - } elsif ($key ~~ ["ovpn_net_src", "ovpn_net_tgt", "OpenVPN static network"]) { - my $network_address = &fwlib::get_ovpn_net_ip($value, 1); - if ($network_address) { - push(@ret, $network_address); - } - - # OpenVPN hosts. - } elsif ($key ~~ ["ovpn_host_src", "ovpn_host_tgt", "OpenVPN static host"]) { - my $host_address = &fwlib::get_ovpn_host_ip($value, 33); - if ($host_address) { - push(@ret, $host_address); - } - - # OpenVPN N2N. - } elsif ($key ~~ ["ovpn_n2n_src", "ovpn_n2n_tgt", "OpenVPN N-2-N"]) { - my $network_address = &fwlib::get_ovpn_n2n_ip($value, 11); - if ($network_address) { - push(@ret, $network_address); - } - - # IPsec networks. - } elsif ($key ~~ ["ipsec_net_src", "ipsec_net_tgt", "IpSec Network"]) { - my $network_address = &fwlib::get_ipsec_net_ip($value, 11); - if ($network_address) { - push(@ret, $network_address); - } - - # The firewall's own IP addresses. - } elsif ($key ~~ ["ipfire", "ipfire_src"]) { - # ALL - if ($value eq "ALL") { - push(@ret, "0/0"); - - # GREEN - } elsif ($value eq "GREEN") { - push(@ret, $defaultNetworks{"GREEN_ADDRESS"}); - - # BLUE - } elsif ($value eq "BLUE") { - push(@ret, $defaultNetworks{"BLUE_ADDRESS"}); - - # ORANGE - } elsif ($value eq "ORANGE") { - push(@ret, $defaultNetworks{"ORANGE_ADDRESS"}); - - # RED - } elsif ($value ~~ ["RED", "RED1"]) { - my $address = &get_external_address(); - if ($address) { - push(@ret, $address); - } - - # Aliases - } else { - my %alias = &get_alias($value); - if (%alias) { - push(@ret, $alias{"IPT"}); - } - } - - # If nothing was selected, we assume "any". - } else { - push(@ret, "0/0"); - } - - return @ret; -} - sub get_protocols { my $hash = shift; my $key = shift; @@ -655,8 +527,16 @@ sub get_protocol_options { my $hash = shift; my $key = shift; my $protocol = shift; + my $nat_options_wanted = shift; my @options = (); + # Nothing to do if no protocol is specified. + if ($protocol eq "all") { + return @options; + } else { + push(@options, ("-p", $protocol)); + } + # Process source ports. my $use_src_ports = ($$hash{$key}[7] eq "ON"); my $src_ports = $$hash{$key}[10]; @@ -674,7 +554,7 @@ sub get_protocol_options { my $dst_ports = $$hash{$key}[15]; if (($dst_ports_mode eq "TGT_PORT") && $dst_ports) { - if ($use_dnat && $$hash{$key}[30]) { + if ($nat_options_wanted && $use_dnat && $$hash{$key}[30]) { $dst_ports = $$hash{$key}[30]; } push(@options, &format_ports($dst_ports, "dst")); @@ -781,3 +661,18 @@ sub make_log_limit_options { return @options; } + +sub firewall_is_in_subnet { + my $subnet = shift; + + # ORANGE is missing here, because nothing may ever access + # the firewall from this network. + my $address = &fwlib::get_internal_firewall_ip_address($subnet, 0); + + if ($address) { + return 1; + } + + return 0; +} +