#!/bin/bash # # rc.firewall Linux Firewall version 2.0 (final) -- 07/25/06 # Homepage: http://lfw.sf.net/ # E-mail: # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details: # http://www.gnu.org/licenses/gpl.html # ##################################### # -- Basic Configuration Options -- # ##################################### # # For an explanation of command line arguments run "./rc.firewall help" # # Notes: # - Firewall must be re-run after changing any configuration options. # - All lists are delimited by single spaces, e.g. "eth0 eth1 ppp0". # - Where applicable, network masks use the format "/<# of bits>" where # /0 is 0.0.0.0 in dotted decimal format, /24 is 255.255.255.0, etc. # # The PERMIT option allows remote access to this machine in the three ways # listed below. By default hosts in internal networks are already allowed to # connect to all services on the firewall. # 1.) Listed PORTS will be open to any connecting host. Protocols 'tcp' and # 'udp' can optionally be specified. If no protocol is specified, then # connections for either protocol will be accepted on the given port(s). # Format: [/] # Example: PERMIT="80/tcp 53 2400-2500/tcp" # 2.) Listed NETWORKS or HOSTS will be allowed to connect to any service # on the firewall itself. This option should be used to specify machines # allowed to connect for administrative purposes. # Format: [/] # Example: PERMIT="207.198.61.33 128.173.0.0/16" # Note that these networks and hosts will also be allowed to bypass # DENY_OUTBOUND and ALLOW_INBOUND restrictions for Linux routers. # 3.) Connections can be allowed from specific NETWORKS or HOSTS to specific # ports by listing entries in the following format: # Format:[/]:[/] # Example: PERMIT="198.82.0.0/16:80/tcp" -- (Allow web traffic from 198.82.*.*) PERMIT="" # List internal (private) interfaces here to allow this machine to act as a # router. All interfaces NOT listed here are considered external (public) # and will be automatically protected by the firewall. # Example: INTERNAL_INTERFACES="eth1 eth2 brg0" INTERNAL_INTERFACES="" # List dial-up and other interfaces without a static IP address here. # Interfaces configured to obtain an IP address automatically (DHCP) do not # need to be listed here unless for some reason your DHCP client does not # receive the same address each time it renews the lease. # Example: DYNAMIC_INTERFACES="ppp0" DYNAMIC_INTERFACES="" # Most users do not need to change anything below this point. ######################################## # -- Advanced Configuration Options -- # ######################################## # See documentation below or in html format online: # http://lfw.sf.net/config.html DENY_OUTBOUND="" ALLOW_INBOUND="" BLACKLIST="" STATIC_INSIDE_OUTSIDE="" PORT_FORWARDS="" PORT_FWD_ALL="yes" PORT_FWD_ROUTED_NETWORKS="yes" ADDITIONAL_ROUTED_NETWORKS="" TRUST_ROUTED_NETWORKS="yes" SHARED_INTERNAL="yes" FIREWALL_IP="" TRUST_LOCAL_EXTERNAL_NETWORKS="no" DMZ_INTERFACES="" NAT_EXTERNAL="yes" ADDITIONAL_NAT_INTERFACES="" IGNORE_INTERFACES="" LOGGING="no" REQUIRE_EXTERNAL_CONFIG="no" # Remember: Firewall must be re-run after changing any configuration options. ############################################ # -- Advanced Firewall Behavior Options -- # ############################################ # The default settings provide the suggested firewall configuration. NO_RP_FILTER_INTERFACES="" INTERNAL_DHCP="yes" RFC_1122_COMPLIANT="depends" DROP_NEW_WITHOUT_SYN="no" DUMP_TCP_ON_INIT="no" TTL_STEALTH_ROUTER="no" LOG_LIMIT="1/minute" LOG_BURST="5" LOG_LEVEL="notice" # Nothing below this point should need modification. ######################################## # -- Firewall Documentation Section -- # ######################################## # # Advanced Configuration Options # # Conventions # - in {yes/no} directives, 'no' is considered to be anything other than the # string 'yes'. # - Optional parameters are enclosed in brackets (e.g. []). # # * DENY_OUTBOUND - {List} Use this directive to block outgoing traffic through a Linux router. Connection attempts matching entries here will be denied. # Format: [[[/]][:[/]:][[/]] # Simplified Format: [][:][:] # Examples: # DENY_OUTBOUND="192.168.1.123" Block all outbound traffic from host 192.168.1.123 # DENY_OUTBOUND=":216.35.208.0/24 25/tcp" Block traffic to IMesh and smtp traffic from anyone (except from networks/hosts specified in PERMIT) # DENY_OUTBOUND=":131.107.0.0/16:53" Block DNS traffic to 131.107.*.* # DENY_OUTBOUND="192.168.120.150/30:0.0.0.0/2:1-65535/udp" Block all UDP traffic from 192.168.120.148 through 192.168.120.151 to half the Internet # # * ALLOW_INBOUND - {List} This directive is used only for the reasons listed below. If your application is not one of the these, then you probably should be using PERMIT. # 1. Selectively allow traffic into a DMZ as configured by DMZ_INTERFACES. # 2. Allow inbound traffic to an single internal host defined in STATIC_INSIDE_OUTSIDE. # 3. Allow traffic through the firewall to internal hosts when you are not doing NAT, i.e. you have changed NAT_EXTERNAL to "no" (in which case internal hosts must have publicly routable addresses). # This directive has the same format as DENY_OUTBOUND above. Note that this directive has no effect on PORT_FORWARDS. # Examples: # ALLOW_INBOUND="66.187.224.0/20" Permit traffic from Red Hat, Inc. to reach all internal hosts with static routes through the firewall. # ALLOW_INBOUND=":192.168.10.25:80/tcp" Permit external hosts to access an internal webserver at 192.168.10.25. # # * BLACKLIST - {List} Packets to or from any host, network, or port listed here will be dropped. This includes inbound, outbound and forwarded connections. This blocking takes precedence over all other configuration preferences. This option has the same syntax as PERMIT explained in the Basic Configuration Section. # Format: [[/]][:[[/]] # Example: # BLACKLIST="10.1.1.1" Block all connections to and from host 10.1.1.1. # # * STATIC_INSIDE_OUTSIDE - {List} This directive is only useful if you have multiple external (public) IP addresses. It is used to create static conduits through the firewall, with the following effect: 1.) Outbound traffic from the given internal network or host is mapped onto a given external address. 2.) If the internal address is a single host, then inbound traffic to the external address is mapped inside the network according to the contents of ALLOW_INBOUND. 3.) If the outside address contains a subnet mask (i.e. represents a network of external addresses) then STATIC_INSIDE_OUTSIDE will map the given external network onto the internal network, provided each network is the same size, as if each address from the two networks was entered separately. This option is usually used to allow hosts behind a NAT firewall to have their own public static IP address. # Format: [/]:[/] # Example: # STATIC_INSIDE_OUTSIDE="192.168.0.12:64.28.67.35" Source NAT outgoing traffic from 192.168.0.12 to 64.28.67.35. Also forward (DNAT) inbound traffic destined for 64.28.67.35 to 192.168.0.12. # # * PORT_FORWARDS - {List} This directive forwards connections received on a specific port to a specified host and port. If PORT_FWD_ALL below is disabled, the PERMIT option can be used to allow access to a port forwarding definition. Note that by default PORT_FWD_ALL is enabled so allowing access in PERMIT is not necessary. If no protocol is specified, then it is assumed that connections of either protocol should be forwarded. If the destination ports are missing, the inbound port number will not be modified. # Format:[:]:[:] # Examples: # PORT_FORWARDS="53:192.168.0.11" Forward incoming connections on port 53 to 192.168.0.11 # PORT_FORWARDS="tcp:8080-8090:192.168.0.10:80" Forward incoming tcp connections ports 8080 through 8090 to 192.168.0.10 port 80 # # * PORT_FWD_ALL - {yes/no} Enabling this option allows all hosts to use all port forwarding definitions and is the equivalent of adding each inbound port in PORT_FORWARDS to PERMIT. # # * PORT_FWD_ROUTED_NETWORKS - {yes/no} Enabling this option allows machines inside a firewall (internal and additional routed networks) to use port forwarding definitions. # # * ADDITIONAL_ROUTED_NETWORKS - {List} Some users may wish to allow routing from additional networks besides those directly connected to internal interfaces. This is where to specify such networks. For security reasons, these connections must still originate from an internal interface. Don't forget to add a route in the kernel routing table for these networks. # Format: [/] # # * TRUST_ROUTED_NETWORKS - {yes/no} Enabling this option allows machines inside the firewall (internal and additional routed networks) to connect to services on the firewall itself. This does not include DMZ interfaces and is the same as adding your internal and routed networks to the PERMIT directive above. Disable it if you do not wholeheartedly trust the hosts inside the firewall. # # * SHARED_INTERNAL - {yes/no} The default answer of "yes" allows hosts in internal and additional routed networks to use the firewall as a router in order to access to other internal networks. # # * FIREWALL_IP - {List} This option allows you to use the firewall to protect an network of hosts with public IP addresses without subnetting the network by assigning public IP address(es) to an internal interface of the firewall and private (e.g. 10.x.x.x) address(es) to the public interface. With this option enabled the firewall then rewrites the source address of its own outbound traffic, which would normally have been addressed to the address of the external interface, to the public address of the internal interface, allowing the firewall to access, and be accessed by, the Internet. This requires access to your border router in order to configure this private network between the firewall and the border router, as well as add a route for the internal network through the Linux firewall. This directive takes two addresses separated by a colon. The first address is the private address used on the interface connected to the border router. The second is a publicly routable address which must be configured on a DMZ or internal interface if NAT_EXTERNAL is disabled, either as the base address or as an IP alias. Multiple entires can be entered for hosts with multiple Internet gateways. # Format: : # Examples: # FIREWALL_IP="10.0.0.2:64.28.67.35" Perform NAT to change the source address of traffic that would otherwise leave the router with the private address 10.0.0.2 to the public address 64.28.67.35 which is configured on a DMZ or internal interface. # # * TRUST_LOCAL_EXTERNAL_NETWORKS - {yes/no} Often local networks attached to external interfaces can be trusted. Enabling this option will trust local external networks as if you had added them to the PERMIT directive. This might be useful for a mobile system which is frequently transported from one trusted network to another. This way firewall settings do not need to be adjusted for each network (though the firewall does need to be run again so that it can recognize its new location and configure itself accordingly). # # * DMZ_INTERFACES - {List} DMZ interfaces are treated as internal interfaces except that network address translation is not done on packets routed through the firewall. This means that hosts in a DMZ must have publicly routable IP addresses. Note that by default all inbound connections from non-internal interfaces are blocked and must be opened using the ALLOW_INBOUND directive if you wish to permit inbound traffic from the Internet to hosts in the DMZ. # # * NAT_EXTERNAL - {yes/no} This option will tell the router to perform source network address translation (SNAT) for all connections originating from an internal network outbound an external interface. This allows internal hosts on a private internal network to share a single Internet IP addresses. Enabling this option is the equivalent of manually adding each your external interfaces to the "ADDITIONAL_NAT_INTERFACES" directive below. # # * ADDITIONAL_NAT_INTERFACES - {List} List of additional interfaces besides those automatically selected by NAT_EXTERNAL through which to source NAT outbound connections. This option is provided separately to offer the opportunity to more precisely define interfaces from which we want to SNAT traffic. # Example: # ADDITIONAL_NAT_INTERFACES="eth3" # # * IGNORE_INTERFACES - {List} This is a special option for systems with special interfaces that cause problems with firewall execution. # Example: # IGNORE_INTERFACES="nas0" # # * LOGGING - {yes/no} This option enables logging of connections blocked by the firewall and what appear to be ICMP flood attempts. Broadcast traffic is not logged. The script then limits the number of log entries that can be generated according to the LOG variables in the next section in order to prevent denial of service by means of filling up your logs. Unfortunately this means that it is possible that not all denied traffic will be logged. Since a blocked connection is really a success of the firewall and most people will never use the cryptic looking kernel logs that this option generates, it is turned off by default. # # * REQUIRE_EXTERNAL_CONFIG - {yes/no} Setting this option to 'yes' will force the firewall to load data from an external configuration file, by default '/etc/firewall.conf'. If an external configuration file cannot be found, the firewall will print a failure message and exit. # # Advanced Firewall Behavior Options # # * NO_RP_FILTER_INTERFACES - {List} DISABLE kernel level reverse path filtering on interfaces listed in this directive. # Example: # NO_RP_FILTER_INTERFACES="ipsec0" # # * INTERNAL_DHCP - {yes/no} Since DHCP clients do not have a source address when connecting to a DHCP server, there is no way to tell if they are part of the internal network. Enabling this option tells the firewall to accepts all DHCP requests received on an internal interface. # # * RFC_1122_COMPLIANT - {yes/no/depends} As of version 2.0rc11 the default option of this configuration directive is 'depends' which configures the host to respond to ICMP-ECHO-REQUEST packets (pings) and send ICMP-PORT-UNREACHABLE packets in response to connection attempts to services which are not available only if the host is a server, as determined by checking for open ports or port forwards. This behavior can be overridden by setting this option to 'yes' in which case the host will always respond to pings and send ICMP-PORT-UNREACHABLE packets or anything other than 'yes' or 'depends' to never respond to pings or send ICMP-PORT-UNREACHABLE packets. Before version 2.0rc11 this option was set to "yes" by default. # # * DROP_NEW_WITHOUT_SYN - {yes/no} By default netfilter classifies tcp packets without the SYN flag set but not part of an existing session, as state "NEW". In most cases these packets should really be considered INVALID, but this behavior would have a few consequences. First, it seems that some applications mistakenly continue to transmit necessary packets after sending a FIN/ACK to tear down the session. Second, it allows Linux to support hot failover firewalls which maintain currently established sessions without access to the actual connection tracking information. In this case a currently established session would be tried against firewall rules as a NEW session and allowed into the ESTABLISHED state if it passes. If all of your applications follow the tcp protocol correctly and you do not have a hot failover firewall you can enable this option for a slightly more preclusive firewall as normally all packets making new connections will have the syn flag set. # # * DUMP_TCP_ON_INIT - {yes/no} The firewall relies heavily on stateful inspection to decide the fate of passing packets. This method usually provides a good balance between security and usability, but due to the current implementation, connection tracking must be enabled before the firewall is configured. This means current sessions are grandfathered into the ESTABLISHED state even if they would have been blocked as new sessions. In short, running the firewall does not disconnect anyone already connected no matter where they are coming from. This directive adds a few temporary rules to reset currently established connections by responding to any incoming data with a tcp packet with the RST flag set. Use [./rc.firewall cleanup] to remove these temporary rules once you feel secure that all the evil crackers connected to your machine have been expelled. The cleanup step is not critical, and is not required at all if you do not dump any connections (the firewall will tell you how many sessions it is dropping). Theoretically the default timeout for an established connections is five days, but with tcp_keepalive enabled on either side of the connection, any keepalive packet (or a response to one we generated) will be met with a tcp reset. So you can safely do a [./rc.firewall cleanup] after whatever your tcp_keepalive_time is set to or five days, whichever is shorter. You can have fun with this option by running your firewall via ssh and watching your session be "reset by peer". # # * TTL_STEALTH_ROUTER - {yes/no} This option increments the "time to live" field in the IP headers of passing packets to compensate for the fact that routers decrement the TTL of each passing packet. This will leave you with a router that behaves 'incorrectly' as it will pass packets as if no router had touched them. THIS SHOULD NEVER BE DONE. See warnings below. To enable this option on 2.4 kernels you need to patch your kernel with the netfilter patch-o-matic base/TTL.patch and for all kernels you must enable "TTL target support" (CONFIG_IP_NF_TARGET_TTL, not to be confused with CONFIG_IP_NF_MATCH_TTL "TTL match support"). # # /A warning from the netfilter team/: Setting or incrementing the TTL field can potentially be very dangerous, so it should be avoided at any cost. Don't ever set or increment the value on packets that leave your local network! # # /Another warning from the Linux kernel team/: [What we are doing] is EXTREMELY DANGEROUS since you can easily create immortal packets that loop forever on the network. # # * LOG_LIMIT - {Rate} This option is used to limit how fast messages are logged via syslog for each of 5 different methods of logging. tcp, udp, icmp, icmp floods, and packets drops from the DROP_NEW_WITHOUT_SYN option. Logging preference options are only consulted if the LOGGING option above is enabled. Note that no sanity checking is performed on the contents of these parameters which should be in standard netfilter log limiting syntax. [default: "1/minute"] # # * LOG_BURST - {Rate} Number of log entries allowed before the above rate is applied. [default "5"] # # * LOG_LEVEL - {Level} Syslog log level for firewall logging. [default "5"] # # Conventions # # All lists are delimited by single spaces. # Optional parameters are enclosed in brackets (e.g. []). # In all {yes/no} directives, 'no' is considered to be anything other than the string 'yes'. # indicates a single port or a dash (-) separated range of ports between 1 to 65535. # is either 'tcp' or 'udp'. If the protocol is left off, then the firewall assumes either protocol is valid. # indicates a network definition in dotted decimal format (i.e. 192.168.0.123). This option can always be appended by a network mask between /0 (all hosts; equivalent to netmask 0.0.0.0 in dotted decimal format) and /32 (a single host; equivalent to netmask 255.255.255.255 in dotted decimal format). If a mask is not specified, then /32 (a single host) is assumed. # EXAMPLE: The class 'C' network 192.168.10.0 netmask 255.255.255.0 is denoted by 192.168.10.0/24. #
or is a single IP address in dotted decimal format. # denotes a single interface such as ppp0, eth0, or eth1. # # ############################ # -- More Documentation -- # ############################ # # Setting up Static Conduits Through the Firewall # # 1. Add an IP alias on the appropriate external interface with the public address you wish to use for STATIC_INSIDE_OUTSIDE. [e.g. ifconfig eth0:0 <2nd public address> netmask ]. # 2. Add entry in STATIC_INSIDE_OUTSIDE. # 3. Run the firewall. # # Public addresses for static conduits must be a part of the same network as the address configured on the original interface. # # When mapping a network of external addresses onto a network of internal addresses, it is important to note the following: # # * This case requires that both network definitions are the same size and have the same fourth octet. # * This case does not support mapping an address pool larger than a class C network (255 hosts). # * This case is the equivalent of entering each address in a separate STATIC_INSIDE_OUTSIDE definition, therefor: # * Each address in the external network must be configured on an IP alias. # # Note: If you are using FIREWALL_IP, then IP Aliases should be created on the DMZ interface (or internal interface if you have NAT_EXTERNAL disabled). This will allow the STATIC_INSIDE_OUTSIDE definitions to be available from the DMZ and special routes in your next-hop router, required for FIREWALL_IP, will allow traffic to flow through the Linux firewall correctly. # # Access Control # # Outbound access control is divided up into five layers. While this method may not be as elegant as a pure access-list, it is simple and covers most applicable scenarios. Here are the layers: # # 1. Packets inbound internal interfaces are blocked by default. # 2. Packets are allowed if they have a source address from a routed network. (see below) # 3. Packets are blocked if they match criteria found in DENY_OUTBOUND. # 4. Packets are allowed if they are from a host or network listed in PERMIT. # 5. Packets are blocked if they are to or from an address or network listed in BLACKLIST. # # Actions by higher layers always trump that of lower layers. # # Selecting Packets to Route # # Adding an interface to the INTERNAL_INTERFACES directive will permit routing based on the following criteria: # # 1. The packet is received on an internal interface and its source address is from the network to which that interface is connected. The network is determined by meshing the IP address and netmask of the interface obtained from the output of 'ifconfig' for all IP addresses assigned to the interface. # -or- # 2. The packet is received from a network or host listed in ADDITIONAL_ROUTED_NETWORKS and is received on any internal interfaces. # -or- # 3. The packet is received on an internal interface that is listed as a dynamic interface. In this case, ALL connections received on the interface are permitted, regardless of source address. # # A feature of the firewall is the ability to have IP aliases assigned to an external interface (anything not listed as an internal interface) and then list the interface as a dynamic interface. The result is that the firewall consults the kernel routing table to determine what address to use when doing source NAT on the outgoing connections. This is because the 'MASQUERADE' target is used in lieu of the 'SNAT' target. In short, if you have multiple IP addresses, you can change your identity based on where you are connecting. ################################# # -- Sanity checking section -- # ################################# # Set version information. VERSION="2.0" COMPATIBLE_VERSIONS="2.0rc9 2.0rc10 2.0rc11 2.0" # Exit if we do not have a reasonable command line argument. if [ -n "$1" ]; then if [ "$1" != "update" ] && [ "$1" != "load" ] && [ "$1" != "fast" ] && \ [ "$1" != "save" ] && [ "$1" != "check" ] && [ "$1" != "status" ] && \ [ "$1" != "clear" ] && [ "$1" != "stop" ] && [ "$1" != "restart" ] && \ [ "$1" != "cleanup" ] && [ "$1" != "help" ] && [ "$1" != "--help" ] && \ [ "$1" != "start" ]; then echo "$0: invalid option -- $1" echo "Try \`$0 help' for more information." exit 1 fi fi # Handle help argument. if [ "$1" == "help" ] || [ "$1" == "--help" ]; then cat << EOF Usage: $0 [OPTION [FILE]] help This help page. start Start firewall, same as executing with no arguments. stop Completely unload all firewall rules from the system. restart Same as start, firewall automatically overwrites current running firewall rules when started. clear Same as stop. cleanup Cleans up special iptables rules left over when DUMP_TCP_ON_INIT is enabled and has no other effect on the running firewall. save [FILE] Start firewall and save configuration directives related firewall information to [FILE]. If [FILE] is not specified, the default /etc/firewall.conf is used. load [FILE] Load configuration directives from file generated with the 'save' command and start firewall. If [FILE] is not specified, the default /etc/firewall.conf is used. update [FILE] Load configuration directives from [FILE], start firewall, then save configuration data back to [FILE]. This can be used to update firewall configuration files generated by previous versions of rc.firewall. It can also be used to update non directive settings if the configuration file was manually edited. If [FILE] is not specified, the default /etc/firewall.conf is used. check [FILE] Perform system sanity checks without starting the firewall. If [FILE] is specified, load configuration directives from [FILE] before performing checks. fast [FILE] Bypass most system checks and restore firewall to the way it was when configuration [FILE] was written. status [FILE] Display status of running firewall by probling iptables and firewall related files. This option requires a previously saved configuration. If [FILE] is not specified, the default /etc/firewall.conf is used. EOF exit fi # Print welcome message. echo "-> rc.firewall version $VERSION running." # Set PATH explicitly. export PATH="/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin" export LANG="en_US" # Save external file name in CONFIG variable and tell everyone when we are loading data from an external configuration file. if [ "$1" == "update" ] || [ "$1" == "load" ] || [ "$1" == "fast" ] || \ [ "$1" == "save" ] || [ "$REQUIRE_EXTERNAL_CONFIG" == "yes" ] || \ ( [ "$1" == "check" ] && [ -n "$2" ] ) || [ "$1" == "status" ]; then if [ -z "$2" ]; then CONFIG="/etc/firewall.conf" else CONFIG=`echo $2 | sed s#^\./#$PWD/#` fi if [ "$1" != "save" ] && [ "$1" != "status" ]; then echo "-> Loading configuration from $CONFIG." fi fi # Define exit/failure function. exit_failure() { echo " [ FAILED ]" echo "-> FATAL: $FAILURE" 1>&2 if [ "$1" != "check" ]; then echo "-> Firewall configuration aborted." 1>&2 fi exit 1 } # Sanity checking section echo -n "-> Performing sanity checks." # Make sure we are running the script with root privileges. if [ "$EUID" != "0" ]; then FAILURE="You must have root privileges to configure the firewall." exit_failure $1 fi # Make sure we have iptables installed. if (( `iptables -V 2>&1 | grep -c "command not found"` )); then FAILURE="Cannot find 'iptables' command. Did you forget to install iptables?" exit_failure $1 fi # Add SysV style initialization support. (start and restart are the same as running the script without any arguments) if [ "$1" == "stop" ] || [ "$1" == "clear" ]; then echo " [ PASSED ]" iptables -t filter -F > /dev/null 2>&1 iptables -t filter -X > /dev/null 2>&1 iptables -t nat -F > /dev/null 2>&1 iptables -t nat -X > /dev/null 2>&1 iptables -t mangle -F > /dev/null 2>&1 iptables -t mangle -X > /dev/null 2>&1 iptables -t filter -P INPUT ACCEPT > /dev/null 2>&1 iptables -t filter -P OUTPUT ACCEPT > /dev/null 2>&1 iptables -t filter -P FORWARD ACCEPT > /dev/null 2>&1 iptables -t nat -P PREROUTING ACCEPT > /dev/null 2>&1 iptables -t nat -P POSTROUTING ACCEPT > /dev/null 2>&1 iptables -t nat -P OUTPUT ACCEPT > /dev/null 2>&1 iptables -t mangle -P POSTROUTING ACCEPT > /dev/null 2>&1 iptables -t mangle -P OUTPUT ACCEPT > /dev/null 2>&1 iptables -t mangle -P PREROUTING ACCEPT > /dev/null 2>&1 iptables -t mangle -P INPUT ACCEPT > /dev/null 2>&1 iptables -t mangle -P FORWARD ACCEPT > /dev/null 2>&1 if !(( `which modprobe 2>&1 | grep -c "which: no modprobe in"` )) && [ -a "/proc/modules" ]; then for MODULE in ipt_TTL iptable_mangle ipt_mark ipt_MARK ipt_MASQUERADE \ ip_nat_irc ip_nat_ftp ipt_LOG ipt_limit ipt_REJECT \ ip_conntrack_irc ip_conntrack_ftp ipt_state iptable_nat \ iptable_filter ip_tables; do if (( `lsmod | grep -c "$MODULE"` )); then rmmod $MODULE > /dev/null 2>&1 fi done fi echo "-> Firewall disabled." exit fi # Cleanup tcp session dump rules. Note that an established TCP session can take up to 5 days to expire. if [ "$1" == "cleanup" ]; then echo " [ PASSED ]" COUNT=`iptables -nL INPUT | grep -c "tcp-reset"` TAB=$COUNT while [ "$((COUNT--))" -gt "0" ]; do iptables -D INPUT 1 done COUNT=`iptables -nL FORWARD | grep -c "tcp-reset"` while [ "$((COUNT--))" -gt "0" ]; do iptables -D FORWARD 1 done echo "-> Old TCP session dump rules expunged ( $TAB )...." exit fi # Check for iptables-save if we will need it if [ "$1" == "status" ] || [ "$1" == "save" ] || [ "$1" == "update" ]; then if (( `which iptables-save 2>&1 | grep -c "which: no iptables-save in"` )); then FAILURE="iptables-save not found; required for '$1' argument." exit_failure $1 fi fi # Check for iptables-restore if we will need it if [ "$1" == "fast" ]; then if (( `which iptables-restore 2>&1 | grep -c "which: no iptables-restore in"` )); then FAILURE="iptables-restore not found; required for '$1' argument." exit_failure $1 fi fi # Check external configuration file and source its variables if it is safe. if [ "$1" == "update" ] || [ "$1" == "load" ] || [ "$1" == "fast" ] || \ ( [ "$REQUIRE_EXTERNAL_CONFIG" == "yes" ] && [ "$1" != "save" ] ) || \ ( [ "$1" == "check" ] && [ -n "$2" ] ) || [ "$1" == "status" ]; then if [ -r "$CONFIG" ]; then if (( `head -n 1 "$CONFIG" | grep -c "# rc.firewall Linux Firewall configuration"` )) || (( `head -c 30 "$CONFIG" | grep -c "# Linux Firewall configuration"` )); then CONFIG_VERSION=`head -n 4 "$CONFIG" | tail -n 1 | cut -d\" -f2` if [ "$1" == "update" ] || ( [ "$1" == "status" ] && \ [ "$VERSION" == "$CONFIG_VERSION" ] ) || ( [ "$1" != "status" ] && \ (( `echo "$COMPATIBLE_VERSIONS" | grep -c "$CONFIG_VERSION"` )) ); then . $CONFIG else if [ "$CONFIG" == "/etc/firewall.conf" ]; then # Cosmetic CONFIG="" else CONFIG=" $CONFIG" fi FAILURE="Configuration file outdated. Please run '$0 update$CONFIG' before using the '$1' argument." exit_failure $1 fi else FAILURE="The configuration file '$CONFIG' does not appear to be associated with this program. Refusing to load data." exit_failure $1 fi elif [ "$1" = "status" ]; then FAILURE="Cannot read from file '$CONFIG'. 'status' command line argument requires a previously stored configuration." exit_failure $1 else FAILURE="Cannot read from file '$CONFIG'. Did you forget to save your configuration or has the file moved?" exit_failure $1 fi fi # Make sure we have proc filesystem support. if ! [ -a "/proc/version" ]; then FAILURE="proc filesystem support required. Please mount /proc or add proc filesystem support in your kernel." exit_failure $1 fi # Create DUMP_TCP_ON_INIT function dump_tcp() { COUNT=`cat /proc/net/ip_conntrack | grep "^tcp" | wc -l` echo -n "-> Dumping inbound TCP sessions ( of $COUNT total TCP connections ..." if [ "$1" != "fast" ]; then sleep 1 # Allow a few moments for that last message to be delivered before we reset remote connections. fi COUNT=1 TAB=0 NET=`cat /proc/net/ip_conntrack | grep "^tcp" | grep ESTABLISHED | awk '{ gsub(/\ /,"\n"); print }' | grep -E 'src|dst|sport|dport'` for ADDRESS in `echo "$NET" | sed -n $COUNT~8p | cut -d= -f2`; do if !(( `echo $INTERNAL_ADDRESSES $EXTERNAL_ADDRESSES | grep -c "$ADDRESS"` )); then DEST=`echo "$NET" | sed -n $((COUNT+1))~8p | cut -d= -f2 | head -n 1` PORTS=`echo "$NET" | sed -n $((COUNT+2))~8p | cut -d= -f2 | head -n 1` DPORTS=`echo "$NET" | sed -n $((COUNT+3))~8p | cut -d= -f2 | head -n 1` iptables -I INPUT -s $ADDRESS -d $DEST -p tcp --sport $PORTS --dport $DPORTS -j REJECT --reject-with tcp-reset if [ "$IS_ROUTER" == "yes" ]; then iptables -I FORWARD -s $ADDRESS -d $DEST -p tcp --sport $PORTS --dport $DPORTS -j REJECT --reject-with tcp-reset fi TAB=$((TAB+1)) fi COUNT=$((COUNT+8)) done echo " $TAB dumped )" } # Handle 'fast' argument. if [ "$1" == "fast" ]; then echo " [ PASSED ]" echo "1" > /proc/sys/net/ipv4/conf/all/rp_filter for INTERFACE in $NO_RP_FILTER_INTERFACES; do echo "0" > /proc/sys/net/ipv4/conf/$INTERFACE/rp_filter done cat $CONFIG | sed -n 41~1p | iptables-restore if [ -n "$DYNAMIC_INTERFACES" ]; then echo "1" > /proc/sys/net/ipv4/ip_dynaddr else echo "0" > /proc/sys/net/ipv4/ip_dynaddr fi if [ "$LOGGING" == "yes" ]; then echo "1" > /proc/sys/net/ipv4/conf/all/log_martians else echo "0" > /proc/sys/net/ipv4/conf/all/log_martians fi if [ -n "$INTERNAL_INTERFACES" ] || [ -n "$PORT_FORWARDS" ]; then echo "1" > /proc/sys/net/ipv4/ip_forward else echo "0" > /proc/sys/net/ipv4/ip_forward fi echo "-> Firewall configuration complete. No sanity checking was performed." if [ "$DUMP_TCP_ON_INIT" == "yes" ]; then dump_tcp fast fi exit fi # Functions for 'status' argument. rcfw_md5check() { if [ x`head -n 5 "$CONFIG" | tail -n 1 | cut -d\" -f2` != x`md5sum $0 | cut -d\ -f1` ]; then return 1 # Failed check fi } iptables_md5check() { if [ x`head -n 6 "$CONFIG" | tail -n 1 | cut -d\" -f2` != x`iptables-save | grep -v "Generated" | grep -v "Completed" | sed s/\\\[.*\\\]//g | md5sum | cut -d\ -f1` ]; then return 1 # Failed check fi } noiptables_md5check() { if [ "xf003cdefa90692d176c02443b5c7d85e" != x`iptables-save | grep -v "Generated" | grep -v "Completed" | sed s/\\\[.*\\\]//g | md5sum | cut -d\ -f1` ]; then return 1 fi } directives_md5check() { if [ x`head -n 7 "$CONFIG" | tail -n 1 | cut -d\" -f2` != x`echo "|$PERMIT|$INTERNAL_INTERFACES|$DYNAMIC_INTERFACES|$DENY_OUTBOUND|$ALLOW_INBOUND||$BLACKLIST|$STATIC_INSIDE_OUTSIDE|$PORT_FORWARDS|$PORT_FWD_ALL|$PORT_FWD_ROUTED_NETWORKS|$ADDITIONAL_ROUTED_NETWORKS|$TRUST_ROUTED_NETWORKS|$SHARED_INTERNAL|$FIREWALL_IP|$TRUST_LOCAL_EXTERNAL_NETWORKS|$DMZ_INTERFACES|$NAT_EXTERNAL|$ADDITIONAL_NAT_INTERFACES|$IGNORE_INTERFACES|$LOGGING|$NO_RP_FILTER_INTERFACES|$INTERNAL_DHCP|$RFC_1122_COMPLIANT|$DROP_NEW_WITHOUT_SYN|$DUMP_TCP_ON_INIT|$TTL_STEALTH_ROUTER|$LOG_LIMIT|$LOG_BURST|$LOG_LEVEL|" | md5sum | cut -d\ -f1` ]; then return 1 # Failed check fi } # Handle 'status' argument and warn if rc.firewall is world readable, writable, or executable. if [ "$1" == "status" ]; then echo " [ PASSED ]" if [ `ls -l $0 | head -c 10 | tail -c 3` != "---" ]; then echo "-> Warning: `echo $0 | sed s#^\./##` is world readable, writable, or executable." echo "-> to fix this, run \`chmod o-rwx $0'." fi if ! rcfw_md5check; then echo "-> `echo $0 | sed s#^\./##` has been modified since $CONFIG was last saved." echo "-> Update the configuration file using the 'save' or 'update' argument." echo "-> Firewall status cannot be determined." elif ! directives_md5check; then echo "-> $CONFIG has been modified since it was last updated." echo "-> Update the configuration file using the 'update' command line argument." echo "-> Firewall status cannot be determined." elif noiptables_md5check; then echo "-> Firewall status: [ DISABLED ]" elif ! iptables_md5check; then echo "-> Firewall status: An unknown firewall configuration is enabled." else echo "-> Firewall status: [ ENABLED ]" fi exit fi # Create a few sanity checking functions. check_network() { # Checks NET variable. FAILURE="" HOST=`echo "$NET/" | cut -d/ -f1` MASK=`echo "$NET/" | cut -d/ -f2` # Optional: Netfilter assumes /32 if not defined. if (( `echo "$NET/" | cut -d/ -f3 | grep -c "."` )); then FAILURE="Syntax error" return 1 fi if [ -z "$HOST" ]; then FAILURE="Syntax error" return 1 fi for OCTET in 1 2 3 4; do OCTET=`echo "$HOST." | cut -d. -f$OCTET --output-delimiter=" "` if [ -z "$OCTET" ] || (( `echo "$OCTET" | grep -c "[^[:digit:]]"` )) || [ "$OCTET" -lt "0" ] || [ "$OCTET" -gt "255" ]; then FAILURE="Network addresses must be in dotted decimal format" return 1 fi done if (( `echo "$HOST." | cut -d. -f5 | grep -c "."` )); then FAILURE="Network address must be in dotted decimal format" return 1 fi if [ -n "$MASK" ]; then if (( `echo "$MASK" | grep -c "[^[:digit:]]"` )) || [ "$MASK" -lt "0" ] || [ "$MASK" -gt "32" ]; then FAILURE="Network mask must be between '/0' (0.0.0.0) and '/32' (255.255.255.255) inclusive" return 1 fi else if (( `echo "$NET" | grep -c "/"` )); then FAILURE="Mask expected but not found" return 1 fi fi } check_ports() { # Checks PORTS variable. FAILURE="" RANGE=`echo "$PORTS/" | cut -d/ -f1` PROTOCOL=`echo "$PORTS/" | cut -d/ -f2` if (( `echo "$PORTS/" | cut -d/ -f3 | grep -c "."` )); then FAILURE="Syntax error" return 1 fi if [ -z "$RANGE" ]; then FAILURE="Syntax error" return 1 fi if (( `echo "$RANGE" | grep -c "[^[:digit:]]"` )) || [ "$RANGE" -lt "1" ] || [ "$RANGE" -gt "65535" ]; then for PORT in `echo "$RANGE-" | cut -d- -f1,2 --output-delimiter=" "`; do if (( `echo "$PORT" | grep -c "[^[:digit:]]"` )) || [ "$PORT" -lt "1" ] || [ "$PORT" -gt "65535" ]; then FAILURE="Valid port numbers must be between 1 and 65535" return 1 fi done if (( `echo "$RANGE-" | cut -d- -f3 | grep -c "."` )); then FAILURE="Syntax error" return 1 fi fi if [ -n "$PROTOCOL" ]; then if ! ( [ "$PROTOCOL" == "tcp" ] || [ "$PROTOCOL" == "udp" ] ); then FAILURE="Invalid protocol" return 1 fi else if (( `echo "$PORTS" | grep -c "/"` )); then FAILURE="Protocol expected but not found" return 1 fi fi } xbits() { # Set XBITS to the number of network bits that two addresses NET and NET1 are the same. XBITS=0 for NUM in 1 2 3 4; do OCTET=`echo "$NET./" | cut -d/ -f1 | cut -d. -f$NUM` OCTET1=`echo "$NET1./" | cut -d/ -f1 | cut -d. -f$NUM` if [ "$OCTET" == "$OCTET1" ]; then XBITS=$((XBITS + 8)) continue fi for SUBTRACT in 128 64 32 16 8 4 2 1; do if [ "$((OCTET - SUBTRACT))" -ge "0" ] && [ "$((OCTET1 - SUBTRACT))" -ge "0" ]; then XBITS=$((XBITS + 1)) OCTET=$((OCTET - SUBTRACT)) OCTET1=$((OCTET1 - SUBTRACT)) elif [ "$((OCTET - SUBTRACT))" -lt "0" ] && [ "$((OCTET1 - SUBTRACT))" -lt "0" ]; then XBITS=$((XBITS + 1)) else return fi done done } # Save selected variable settings if we are going to back up our configuration. if [ "$1" == "save" ] || [ "$1" == "update" ]; then ORIG_PERMIT="$PERMIT" ORIG_INTERNAL_INTERFACES="$INTERNAL_INTERFACES" ORIG_PORT_FORWARDS="$PORT_FORWARDS" ORIG_ALLOW_INBOUND="$ALLOW_INBOUND" ORIG_DENY_OUTBOUND="$DENY_OUTBOUND" ORIG_DYNAMIC_INTERFACES="$DYNAMIC_INTERFACES" ORIG_STATIC_INSIDE_OUTSIDE="$STATIC_INSIDE_OUTSIDE" ORIG_RFC_1122_COMPLIANT="$RFC_1122_COMPLIANT" fi # Add DMZ interfaces to list of internal interfaces. if [ -n "$DMZ_INTERFACES" ]; then INTERNAL_INTERFACES="$INTERNAL_INTERFACES $DMZ_INTERFACES" fi # Remove duplicate internal interfaces. if [ -n "$INTERNAL_INTERFACES" ]; then for INTERFACE in $INTERNAL_INTERFACES; do if !(( `echo "$MOD_INTERFACES" | grep -c "$INTERFACE"` )); then MOD_INTERFACES="$MOD_INTERFACES $INTERFACE" fi done INTERNAL_INTERFACES=`echo $MOD_INTERFACES` fi # Determine if we are a router. if [ -n "$INTERNAL_INTERFACES" ] || [ -n "$PORT_FORWARDS" ]; then IS_ROUTER="yes" fi # Sanity check PERMIT and BLACKLIST. for PARAM in PERMIT BLACKLIST; do if [ "$PARAM" == "PERMIT" ]; then ITEM="$PERMIT" else ITEM="$BLACKLIST" fi for NETWORK in $ITEM; do NET=`echo "$NETWORK:" | cut -d: -f1` PORTS=`echo "$NETWORK:" | cut -d: -f2` if (( `echo "$NETWORK:" | cut -d: -f3 | grep -c "."` )); then FAILURE="Syntax error in $PARAM." exit_failure $1 fi if ! check_network; then PORTS="$NET" FAIL="$FAILURE in $PARAM." if ! check_ports; then FAILURE="$FAIL" exit_failure $1 fi fi if [ -n "$PORTS" ]; then if ! check_ports; then FAILURE="$FAILURE in $PARAM." exit_failure $1 fi fi done done echo -n "." # Remove entries with ports and put them in their own variable. TRUSTED_PORTS="" OPEN_PORTS="" if [ -n "$PERMIT" ]; then TEMP_PERMIT="" for NETWORK in $PERMIT; do NET=`echo "$NETWORK:" | cut -d: -f1` PORTS=`echo "$NETWORK:" | cut -d: -f2` if [ -z "$PORTS" ]; then if ! check_network; then OPEN_PORTS="$OPEN_PORTS $NET" else TEMP_PERMIT="$TEMP_PERMIT $NET" fi else PROTOCOL=`echo "$PORTS/" | cut -d/ -f2` if [ "$PROTOCOL" == "tcp" ] || [ "$PROTOCOL" == "udp" ]; then TRUSTED_PORTS="$TRUSTED_PORTS $NET:$PORTS/$PROTOCOL" else TRUSTED_PORTS="$TRUSTED_PORTS $NET:$PORTS/tcp" TRUSTED_PORTS="$TRUSTED_PORTS $NET:$PORTS/udp" fi fi done PERMIT=`echo $TEMP_PERMIT` TRUSTED_PORTS=`echo $TRUSTED_PORTS` OPEN_PORTS=`echo $OPEN_PORTS` fi # Determine if we should be RFC 1122 compliant if RFC_1122_COMPLIANT is set to "depends". if [ "$RFC_1122_COMPLIANT" == "depends" ] && ( [ -n "$OPEN_PORTS" ] || ( [ -n "$PORT_FORWARDS" ] && [ "$PORT_FWD_ALL" == "yes" ] ) ); then RFC_1122_COMPLIANT="yes" fi # Sanity check additional routed networks. if [ -n "$INTERNAL_INTERFACES" ]; then for NET in $ADDITIONAL_ROUTED_NETWORKS; do if ! check_network; then FAILURE="$FAILURE in ADDITIONAL_ROUTED_NETWORKS." exit_failure $1 fi done fi # Sanity check port forwarding definitions. Protocol and destination ports are optional. if [ -n "$PORT_FORWARDS" ]; then MOD_FORWARDS="" for FORWARD in $PORT_FORWARDS; do if (( `echo "$FORWARD:" | cut -d: -f5 | grep -c "."` )); then FAILURE="Syntax error in PORT_FORWARDS." exit_failure $1 fi PROTOCOL=`echo "$FORWARD:" | cut -d: -f1` if [ "$PROTOCOL" != "tcp" ] && [ "$PROTOCOL" != "udp" ]; then COUNT="1" MOD_FORWARDS="$MOD_FORWARDS tcp:$FORWARD udp:$FORWARD" PROT="" else COUNT="0" MOD_FORWARDS="$MOD_FORWARDS $FORWARD" PROT="/$PROTOCOL" fi PORTS=`echo "$FORWARD:" | cut -d: -f$((2-COUNT))` FAILURE="Invalid syntax " if ! check_ports || [ -n "$PROTOCOL" ]; then FAILURE="$FAILURE in PORT_FORWARDS." exit_failure $1 fi if [ "$PORT_FWD_ALL" == "yes" ]; then for TAB in NULL; do for PORT in $OPEN_PORTS; do if [ "$PORT" == "${PORTS}$PROT" ]; then break 2 fi done OPEN_PORTS="$OPEN_PORTS ${PORTS}$PROT" done fi NET=`echo "$FORWARD:" | cut -d: -f$((3-COUNT))` FAILURE="Destination must be a single host" if ! check_network || [ -n "$MASK" ]; then FAILURE="$FAILURE in PORT_FORWARDS." exit_failure $1 fi PORTS=`echo "$FORWARD:" | cut -d: -f$((4-COUNT))` if [ -n "$PORTS" ]; then FAILURE="Invalid syntax" if ! check_ports || [ -n "$PROTOCOL" ]; then FAILURE="$FAILURE in PORT_FORWARDS." exit_failure $1 fi fi done PORT_FORWARDS=`echo $MOD_FORWARDS` OPEN_PORTS=`echo $OPEN_PORTS` fi # Sanity check open ports. Expand undefined protocols into tcp and udp. if [ -n "$OPEN_PORTS" ]; then MOD_PORTS="" for PORTS in $OPEN_PORTS; do if ! check_ports; then FAILURE="$FAILURE in OPEN_PORTS." exit_failure $1 fi if [ "$PROTOCOL" == "tcp" ] || [ "$PROTOCOL" == "udp" ]; then MOD_PORTS="$MOD_PORTS $PORTS" else MOD_PORTS="$MOD_PORTS $PORTS/tcp $PORTS/udp" fi done OPEN_PORTS=`echo $MOD_PORTS` fi # Sanity check static inside,outside translations. if [ -n "$STATIC_INSIDE_OUTSIDE" ]; then MOD_STATIC="" for FORWARD in $STATIC_INSIDE_OUTSIDE; do if (( `echo "$FORWARD:" | cut -d: -f3 | grep -c "."` )); then FAILURE="Syntax error in STATIC_INSIDE_OUTSIDE." exit_failure $1 fi NET=`echo "$FORWARD:" | cut -d: -f1` if ! check_network; then FAILURE="$FAILURE in inside address of STATIC_INSIDE_OUTSIDE." exit_failure $1 fi NET1=`echo $NET | cut -d\/ -f1` STROKE="$MASK" NET=`echo "$FORWARD:" | cut -d: -f2` if ! check_network; then FAILURE="$FAILURE in outside address of STATIC_INSIDE_OUTSIDE." exit_failure $1 fi if [ -n "$MASK" ]; then if [ "$STROKE" != "$MASK" ]; then FAILURE="If outside address in STATIC_INSIDE_OUTSIDE has a netmask, it must be the same as the inside address." exit_failure $1 fi if [ "$MASK" -lt "24" ]; then FAILURE="Networks larger than class C (254 hosts) are not supported on the OUTSIDE address of STATIC_INSIDE_OUTSIDE." exit_failure $1 fi TAB="1" NET=`echo $NET | cut -d\/ -f1` COUNT=`echo $NET | cut -d. -f4` if [ "$COUNT" -ne "`echo $NET1 | cut -d. -f4`" ]; then FAILURE="When using a subnet mask on the OUTSIDE address in STATIC_INSIDE_OUTSIDE, last octet of both inside and outside addresses must be the same." exit_failure $1 fi NET1=`echo $NET1 | cut -d. -f1,2,3` NET=`echo $NET | cut -d. -f1,2,3` while [ "$MASK" -lt "32" ]; do TAB=$((TAB * 2)) MASK=$((MASK+1)) done STROKE="$TAB" while [ "$TAB" -gt "0" ]; do TAB=$((TAB-1)) OCTET=$((COUNT-(COUNT%STROKE)+TAB)) if [ "$OCTET" != "0" ] && [ "$OCTET" != "255" ]; then MOD_STATIC="$MOD_STATIC $NET1.$OCTET:$NET.$OCTET" fi done else MOD_STATIC="$MOD_STATIC $FORWARD" fi done STATIC_INSIDE_OUTSIDE=`echo $MOD_STATIC` fi # Sanity check FIREWALL_IP. for ADDRESS in $FIREWALL_IP; do if (( `echo "$ADDRESS:" | cut -d: -f3 | grep -c "."` )); then FAILURE="Syntax error in FIREWALL_IP." exit_failure $1 fi NET=`echo "$ADDRESS:" | cut -d: -f1` if ! check_network; then FAILURE="$FAILURE in FIREWALL_IP." exit_failure $1 fi if [ -n "$MASK" ]; then FAILURE="FIREWALL_IP may not contain network masks." exit_failure $1 fi NET=`echo "$ADDRESS:" | cut -d: -f2` if ! check_network; then FAILURE="$FAILURE in FIREWALL_IP." exit_failure $1 fi if [ -n "$MASK" ]; then FAILURE="FIREWALL_IP may not contain network masks." exit_failure $1 fi done # Make sure dynamic, nat, and rp_filter interface definitions do not use IP aliases. if (( `echo "$INTERNAL_INTERFACES $DYNAMIC_INTERFACES $NO_RP_FILTER_INTERFACES $ADDITIONAL_NAT_INTERFACES" | grep -c ":"` )); then FAILURE="Definitions cannot contain IP aliases." exit_failure $1 fi # Obtain list of external interfaces. EXTERNAL_INTERFACES=`ifconfig | grep "^[[:alpha:]]" | cut -d\ -f1 | sed s/^lo.*//` PARAM=`echo $EXTERNAL_INTERFACES` for INTERFACE in $INTERNAL_INTERFACES; do EXTERNAL_INTERFACES=`echo "$EXTERNAL_INTERFACES" | sed s/^$INTERFACE//` done for INTERFACE in $PARAM; do if !(( `echo "$EXTERNAL_INTERFACES $INTERNAL_INTERFACES" | grep -c "$INTERFACE"` )); then INTERNAL_INTERFACES="$INTERNAL_INTERFACES $INTERFACE" fi done EXTERNAL_INTERFACES=`echo $EXTERNAL_INTERFACES | sed 's/[^0-9]\:[0-9]\+//g'` # Cleanup. for INTERFACE in $IGNORE_INTERFACES; do if !(( `echo "$EXTERNAL_INTERFACES" | grep -c "$INTERFACE"` )); then FAILURE="Interface specified to IGNORE was not found. Check the configuration." exit_failure $1 else EXTERNAL_INTERFACES=`echo $EXTERNAL_INTERFACES | sed s#$INTERFACE##g` fi done EXTERNAL_INTERFACES=`echo $EXTERNAL_INTERFACES` # Remove whitespace. echo -n "." # Divide internal and external interfaces into static and dynamic groups. DYNAMIC_INTERNAL_INTERFACES="" DYNAMIC_EXTERNAL_INTERFACES="" STATIC_INTERNAL_INTERFACES="" STATIC_EXTERNAL_INTERFACES="" for INTERFACE in $INTERNAL_INTERFACES; do if (( `echo "$DYNAMIC_INTERFACES" | grep -c "$INTERFACE"` )); then if [ -n "$DMZ_INTERFACES" ]; then FAILURE="Cannot have dynamic internal interfaces with a DMZ." exit_failure $1 fi DYNAMIC_INTERNAL_INTERFACES="$DYNAMIC_INTERNAL_INTERFACES $INTERFACE" else STATIC_INTERNAL_INTERFACES="$STATIC_INTERNAL_INTERFACES $INTERFACE" fi done for INTERFACE in $EXTERNAL_INTERFACES; do if (( `echo "$DYNAMIC_INTERFACES" | grep -c "$(echo $INTERFACE | cut -d: -f1)"` )); then if !(( `echo "$INTERFACE" | grep -c ":"` )); then DYNAMIC_EXTERNAL_INTERFACES="$DYNAMIC_EXTERNAL_INTERFACES $INTERFACE" fi else STATIC_EXTERNAL_INTERFACES="$STATIC_EXTERNAL_INTERFACES $INTERFACE" fi done for INTERFACE in $DYNAMIC_INTERFACES; do if !(( `echo "$INTERNAL_INTERFACES $EXTERNAL_INTERFACES" | grep -c "$INTERFACE"` )); then DYNAMIC_EXTERNAL_INTERFACES="$DYNAMIC_EXTERNAL_INTERFACES $INTERFACE" fi done DYNAMIC_INTERNAL_INTERFACES=`echo $DYNAMIC_INTERNAL_INTERFACES` DYNAMIC_EXTERNAL_INTERFACES=`echo $DYNAMIC_EXTERNAL_INTERFACES` STATIC_INTERNAL_INTERFACES=`echo $STATIC_INTERNAL_INTERFACES` STATIC_EXTERNAL_INTERFACES=`echo $STATIC_EXTERNAL_INTERFACES` # If we are configured to be a router, then make sure we have somewhere to route traffic. if [ -n "$INTERNAL_INTERFACES" ] && [ -z "$STATIC_EXTERNAL_INTERFACES" ] && [ -z "$DYNAMIC_EXTERNAL_INTERFACES" ]; then if (( `echo "$INTERNAL_INTERFACES" | wc -w | grep -c "1"` )); then FAILURE="Routing enabled, with no place to route traffic! Did you forget to ifconfig an interface, or list DYNAMIC_INTERFACES?" exit_failure $1 fi fi # Obtain list of interfaces to NAT outbound connections STATIC_NAT_INTERFACES="" if [ "$IS_ROUTER" == "yes" ]; then if [ "$NAT_EXTERNAL" == "yes" ]; then for INTERFACE in $STATIC_EXTERNAL_INTERFACES; do if !(( `echo "$INTERFACE" | grep -c ":"` )); then STATIC_NAT_INTERFACES="$STATIC_NAT_INTERFACES $INTERFACE" fi done for INTERFACE in $DYNAMIC_EXTERNAL_INTERFACES; do if !(( `echo "$INTERFACE" | grep -c ":"` )); then DYNAMIC_NAT_INTERFACES="$DYNAMIC_NAT_INTERFACES $INTERFACE" fi done fi for INTERFACE in $ADDITIONAL_NAT_INTERFACES; do if (( `echo "$DYNAMIC_INTERFACES" | grep -c "$INTERFACE"` )); then DYNAMIC_NAT_INTERFACES="$DYNAMIC_NAT_INTERFACES $INTERFACE" else STATIC_NAT_INTERFACES="$STATIC_NAT_INTERFACES $INTERFACE" fi done fi # If we are a router, check that all static internal interfaces are up. for INTERFACE in $STATIC_INTERNAL_INTERFACES; do if !(( `ifconfig | grep -c "^$INTERFACE\ "` )); then FAILURE="A static internal interface is down. Did you forgot to configure interfaces before running the firewall?" exit_failure $1 fi done # Obtain list of NAT addresses if we are a router doing nat. NAT_ADDRESSES="" if [ -n "$STATIC_NAT_INTERFACES" ]; then MOD_STATIC_EXTERNAL_INTERFACES="" MOD_STATIC_NAT_INTERFACES="" for INTERFACE in $STATIC_NAT_INTERFACES; do ADDRESS=`ifconfig | grep "^$INTERFACE\ " -A1 | grep "inet" | cut -d: -f2 | cut -d\ -f1 | head -n 1` if [ -z "$ADDRESS" ]; then echo " [ WAIT ]" echo -n "-> $INTERFACE has no IP address. Waiting for DHCP" for COUNT in 1 2 3 4 5 6 7 8 9 10; do sleep 1 echo -n "." ADDRESS=`ifconfig | grep "^$INTERFACE\ " -A1 | grep "inet" | cut -d: -f2 | cut -d\ -f1 | head -n 1` if [ -n "$ADDRESS" ]; then echo " [ FOUND ]" break else if [ "$COUNT" == "10" ]; then echo " [ MISSING ]" echo "-> WARNING: IP address for $INTERFACE not found. Converting to dynamic interface." DYNAMIC_EXTERNAL_INTERFACES="$DYNAMIC_EXTERNAL_INTERFACES $INTERFACE" DYNAMIC_INTERFACES="$DYNAMIC_INTERFACES $INTERFACE" if [ "$NAT_EXTERNAL" == "yes" ]; then DYNAMIC_NAT_INTERFACES="$DYNAMIC_NAT_INTERFACES $INTERFACE" fi for INT in $STATIC_EXTERNAL_INTERFACES; do if [ "$INTERFACE" != "$INT" ]; then MOD_STATIC_EXTERNAL_INTERFACES="$MOD_STATIC_EXTERNAL_INTERFACES $INTERFACE" fi done STATIC_EXTERNAL_INTERFACES=`echo $MOD_STATIC_EXTERNAL_INTERFACES` fi fi done echo -n "-> Continuing sanity checks.." else MOD_STATIC_NAT_INTERFACES="$MOD_STATIC_NAT_INTERFACES $INTERFACE" if [ -n "$FIREWALL_IP" ]; then for FORWARD in $FIREWALL_IP; do if [ `echo "$FORWARD" | cut -d: -f1` == "$ADDRESS" ]; then ADDRESS=`echo "$FORWARD" | cut -d: -f2` fi done fi NAT_ADDRESSES="$NAT_ADDRESSES $ADDRESS" fi done STATIC_NAT_INTERFACES=`echo $MOD_STATIC_NAT_INTERFACES` NAT_ADDRESSES=`echo $NAT_ADDRESSES` fi echo -n "." # Determine if this is a modular kernel, if so modprobe the required modules. if !(( `which modprobe 2>&1 | grep -c "which: no modprobe in"` )) && [ -a "/proc/modules" ]; then if (( `lsmod | grep -c "ipchains"` )); then rmmod ipchains > /dev/null 2>&1 fi REQUIRED_MODULES="ip_tables ip_conntrack ipt_state iptable_filter ip_conntrack_irc ip_conntrack_ftp" if [ "$RFC_1122_COMPLIANT" == "yes" ] || [ "$RFC_1122_COMPLIANT" == "depends" ]; then REQUIRED_MODULES="$REQUIRED_MODULES ipt_REJECT" fi if [ "$LOGGING" == "yes" ]; then REQUIRED_MODULES="$REQUIRED_MODULES ipt_LOG ipt_limit" fi if [ "$IS_ROUTER" == "yes" ]; then if [ -n "$STATIC_NAT_INTERFACES" ] || [ -n "$DYNAMIC_NAT_INTERFACES" ] || \ [ -n "$PORT_FORWARDS" ] || [ -n "$STATIC_INSIDE_OUTSIDE" ]; then REQUIRED_MODULES="$REQUIRED_MODULES iptable_nat ip_nat_irc ip_nat_ftp" if [ -n "$DYNAMIC_NAT_INTERFACES" ] || \ ( [ -n "$DYNAMIC_INTERFACES" ] && ( [ -n "$PORT_FORWARDS" ] || [ -n "$STATIC_INSIDE_OUTSIDE" ] ) ); then REQUIRED_MODULES="$REQUIRED_MODULES ipt_MASQUERADE" fi fi if [ -n "$PORT_FORWARDS" ] || [ "$TTL_STEALTH_ROUTER" == "yes" ]; then REQUIRED_MODULES="$REQUIRED_MODULES iptable_mangle" fi if [ -n "$PORT_FORWARDS" ]; then REQUIRED_MODULES="$REQUIRED_MODULES ipt_mark ipt_MARK" fi if [ "$TTL_STEALTH_ROUTER" == "yes" ]; then REQUIRED_MODULES="$REQUIRED_MODULES ipt_TTL" fi fi for MODULE in $REQUIRED_MODULES; do if (( `modprobe -l | grep -c "$MODULE"` )); then modprobe $MODULE > /dev/null 2>&1 fi done fi # Obtain list of internal networks with subnet masks corresponding to internal interfaces. INTERNAL_ADDRESSES="" INTERNAL_NETWORKS="" NAT_NETWORKS="" if [ -n "$STATIC_INTERNAL_INTERFACES" ]; then for INTERFACE in $STATIC_INTERNAL_INTERFACES; do STROKE="0" MASK=`ifconfig | grep "^$INTERFACE\ " -A1 | awk '{ gsub(/\ /,"\n"); print }' | grep "Mask" | cut -d: -f2` for OCTET in 1 2 3 4; do BINARY=`echo "$MASK" | cut -d. -f$OCTET` for SUBTRACT in 128 64 32 16 8 4 2 1; do if [ "$((BINARY - SUBTRACT))" -ge "0" ]; then BINARY=$((BINARY - SUBTRACT)) STROKE=$((STROKE + 1)) fi done done ADDRESS=`ifconfig | grep "^$INTERFACE\ " -A1 | grep "inet" | cut -d: -f2 | cut -d\ -f1 | head -n 1` INTERNAL_ADDRESSES="$INTERNAL_ADDRESSES $ADDRESS" INTERNAL_NETWORKS="$INTERNAL_NETWORKS $ADDRESS/$STROKE" if [ -z "$DMZ_INTERFACES" ] || !(( `echo "$DMZ_INTERFACES" | grep -c "$INTERFACE"` )); then NAT_NETWORKS="$NAT_NETWORKS $ADDRESS/$STROKE" fi done INTERNAL_ADDRESSES=`echo $INTERNAL_ADDRESSES` INTERNAL_NETWORKS=`echo $INTERNAL_NETWORKS` NAT_NETWORKS=`echo $NAT_NETWORKS` fi # Obtain a list of external addresses. EXTERNAL_ADDRESSES="" EXTERNAL_NETWORKS="" if [ -n "$STATIC_EXTERNAL_INTERFACES" ]; then for INTERFACE in $STATIC_EXTERNAL_INTERFACES; do STROKE="0" MASK=`ifconfig | grep "^$INTERFACE\ " -A1 | awk '{ gsub(/\ /,"\n"); print }' | grep "Mask" | cut -d: -f2` for OCTET in 1 2 3 4; do BINARY=`echo "$MASK" | cut -d. -f$OCTET` for SUBTRACT in 128 64 32 16 8 4 2 1; do if [ "$((BINARY - SUBTRACT))" -ge "0" ]; then BINARY=$((BINARY - SUBTRACT)) STROKE=$((STROKE + 1)) fi done done ADDRESS=`ifconfig | grep "^$INTERFACE\ " -A1 | grep "inet" | cut -d: -f2 | cut -d\ -f1 | head -n 1` EXTERNAL_ADDRESSES="$EXTERNAL_ADDRESSES $ADDRESS" EXTERNAL_NETWORKS="$EXTERNAL_NETWORKS $ADDRESS/$STROKE" done EXTERNAL_ADDRESSES=`echo $EXTERNAL_ADDRESSES` EXTERNAL_NETWORKS=`echo $EXTERNAL_NETWORKS` fi # Make a table of interfaces for marking packets based on their incoming interface for port forwarding. if [ -n "$PORT_FORWARDS" ]; then PORT_FORWARD_ADDRESSES="" COUNT="0" TAB="1" for INTERFACE in $STATIC_INTERNAL_INTERFACES $STATIC_EXTERNAL_INTERFACES; do COUNT=$((COUNT + 1)) ADDRESS=`echo $INTERNAL_ADDRESSES $EXTERNAL_ADDRESSES | cut -d\ -f$COUNT` # Do not forward packets destined for an address in STATIC_INSIDE_OUTSIDE. if !(( `echo "$STATIC_INSIDE_OUTSIDE" | awk '{ gsub(/\ /,"\n"); print }' | \ cut -d: -f2 | grep -c "$ADDRESS"` )); then if [ -n "$FIREWALL_IP" ]; then # Nobody will ever use the address of the private network between us and our gateway for port forwarding. if (( `echo "$FIREWALL_IP" | awk '{ gsub(/\ /,"\n"); print }' | \ cut -d: -f2 | grep -c "$ADDRESS"` )); then continue fi if (( `echo "$FIREWALL_IP" | awk '{ gsub(/\ /,"\n"); print }' | \ cut -d: -f1 | grep -c "$ADDRESS"` )); then ADDRESS=`echo "$FIREWALL_IP" | awk '{ gsub(/\ /,"\n"); print }' | \ grep "$ADDRESS" | cut -d: -f2` fi fi INTERFACE=`echo "$INTERFACE" | cut -d: -f1` INTERFACE_TAB[$TAB]="$INTERFACE" ADDRESS_TAB[$TAB]="$ADDRESS" PORT_FORWARD_ADDRESSES="$PORT_FORWARD_ADDRESSES $ADDRESS" if [ "$PORT_FWD_ROUTED_NETWORKS" == "yes" ]; then if (( `echo "$STATIC_INTERNAL_INTERFACES" | grep -c "$INTERFACE"` )); then NETWORK_TAB[$TAB]="$ADDITIONAL_ROUTED_NETWORKS `echo "$INTERNAL_NETWORKS" | cut -d\ -f$COUNT`" fi fi TAB=$((TAB + 1)) fi done for INTERFACE in $DYNAMIC_INTERNAL_INTERFACES $DYNAMIC_EXTERNAL_INTERFACES; do INTERFACE_TAB[$TAB]="$INTERFACE" TAB=$((TAB + 1)) done PORT_FORWARD_ADDRESSES=`echo $PORT_FORWARD_ADDRESSES` fi # Obtain broadcast list if we are doing logging (so that we will not log them). # We have to do this before we modify STATIC_INTERNAL_INTERFACES. if [ "$LOGGING" == "yes" ]; then BCAST_LIST="255.255.255.255" for INTERFACE in $STATIC_INTERNAL_INTERFACES $STATIC_EXTERNAL_INTERFACES; do BROADCAST=`ifconfig | grep "^$INTERFACE\ " -A1 | grep "Bcast" | cut -d: -f3 | cut -d\ -f1 | head -n 1` if !(( `echo "$BCAST_LIST" | grep -c "$BROADCAST"` )); then BCAST_LIST="$BCAST_LIST $BROADCAST" fi done fi # Remove redundant networks from INTERNAL_INTERFACES, STATIC_INTERNAL_INTERFACES, and INTERNAL_NETWORKS. if [ -n "$INTERNAL_NETWORKS" ]; then CHANGE=1 until [ "$CHANGE" == "0" ]; do COUNT=0 CHANGE=0 for NET in $INTERNAL_NETWORKS; do TAB=0 COUNT=$((COUNT + 1)) INTERFACE=`echo "$STATIC_INTERNAL_INTERFACES" | cut -d\ -f$COUNT` STROKE=`echo "$NET" | cut -d/ -f2` for NET1 in $INTERNAL_NETWORKS; do TAB=$((TAB + 1)) INTERFACE1=`echo "$STATIC_INTERNAL_INTERFACES" | cut -d\ -f$TAB` if [ "$INTERFACE" == "$INTERFACE1" ]; then continue # Obviously we don't want to compare a network to itself. fi PARAM=`echo "$INTERFACE1" | cut -d: -f1` if !(( `echo "$INTERFACE" | cut -d: -f1 | grep -c "$PARAM"` )); then continue # We only want to compare networks attached to the same interface. fi MASK=`echo "$NET1/" | cut -d/ -f2` xbits if [ "$STROKE" -le "$MASK" ]; then # Then NET defines a larger network than NET1. if [ "$XBITS" -ge "$STROKE" ]; then # Then delete the second one if they are the same up to STROKE. INTERNAL_NETWORKS=`echo "$INTERNAL_NETWORKS" | sed s#$NET1##` INTERNAL_NETWORKS=`echo $INTERNAL_NETWORKS` STATIC_INTERNAL_INTERFACES=`echo "$STATIC_INTERNAL_INTERFACES" | sed s#$INTERFACE1##` STATIC_INTERNAL_INTERFACES=`echo $STATIC_INTERNAL_INTERFACES` if [ -z "$DMZ_INTERFACES" ] || !(( `echo "$DMZ_INTERFACES" | grep -c "$PARAM"` )); then NAT_NETWORKS=`echo "$NAT_NETWORKS" | sed s#$NET1##` NAT_NETWORKS=`echo $NAT_NETWORKS` fi CHANGE=1 continue 3 fi elif [ "$XBITS" -ge "$MASK" ]; then # Else delete the first one (provided it is still the same interface). INTERNAL_NETWORKS=`echo "$INTERNAL_NETWORKS" | sed s#$NET##` INTERNAL_NETWORKS=`echo $INTERNAL_NETWORKS` STATIC_INTERNAL_INTERFACES=`echo "$STATIC_INTERNAL_INTERFACES" | sed s#$INTERFACE##` STATIC_INTERNAL_INTERFACES=`echo $STATIC_INTERNAL_INTERFACES` if [ -z "$DMZ_INTERFACES" ] || !(( `echo "$DMZ_INTERFACES" | grep -c "$PARAM"` )); then NAT_NETWORKS=`echo "$NAT_NETWORKS" | sed s#$NET##` NAT_NETWORKS=`echo $NAT_NETWORKS` fi CHANGE=1 continue 3 fi done done done fi echo -n "." # Sanity check ALLOW_INBOUND and DENY_OUTBOUND and compare against INTERNAL_NETWORKS. if [ -n "$ALLOW_INBOUND" ] || [ -n "$DENY_OUTBOUND" ]; then MOD_ALLOW_INBOUND="" MOD_DENY_OUTBOUND="" for PARAM in DENY_OUTBOUND ALLOW_INBOUND; do if [ "$PARAM" == "DENY_OUTBOUND" ]; then LIST="$DENY_OUTBOUND" else LIST="$ALLOW_INBOUND" fi for FORWARD in $LIST; do for TEMP in NULL; do # You'll see why. if (( `echo "$FORWARD:" | cut -d: -f4 | grep -c "."` )); then FAILURE="Too many parameters in $PARAM." exit_failure $1 fi NET=`echo "$FORWARD:" | cut -d: -f1` if [ -n "$NET" ] && ! check_network; then PORTS="$NET" if ! check_ports; then FAILURE="Syntax error in $PARAM." exit_failure $1 else eval TEMP_$PARAM="any:any:$PORTS" break fi elif [ -z "$NET" ]; then NET="any" else if [ "$PARAM" == "DENY_OUTBOUND" ]; then STROKE=`echo "$NET/" | cut -d/ -f2` if [ -z "$STROKE" ]; then STROKE=32 fi for TAB in NULL; do for NET1 in $INTERNAL_NETWORKS $ADDITIONAL_ROUTED_NETWORKS; do MASK=`echo "$NET1/" | cut -d/ -f2` if [ -z "$MASK" ]; then MASK=32 fi xbits # Is this host/network from one of our internal networks? if [ "$STROKE" -lt "$MASK" ]; then continue # Can't tell elif [ "$XBITS" -ge "$MASK" ]; then break 2 # Yes! fi # Can't tell done FAILURE="Source host from DENY_OUTBOUND not found in an internal network." exit_failure $1 done fi fi NET2="$NET" NET=`echo "$FORWARD:" | cut -d: -f2` if [ -n "$NET" ] && ! check_network; then PORTS="$NET" if ! check_ports; then FAILURE="Syntax error in $PARAM." exit_failure $1 else eval TEMP_$PARAM="$NET2:any:$PORTS" break fi elif [ -z "$NET" ]; then eval TEMP_$PARAM="$NET2:any:any" break fi if [ "$PARAM" == "ALLOW_INBOUND" ]; then STROKE=`echo "$NET/" | cut -d/ -f2` if [ -z "$STROKE" ]; then STROKE=32 fi for TAB in NULL; do for NET1 in $INTERNAL_NETWORKS $ADDITIONAL_ROUTED_NETWORKS; do MASK=`echo "$NET1/" | cut -d/ -f2` if [ -z "$MASK" ]; then MASK=32 fi xbits # Is this host/network from one of our internal networks? if [ "$STROKE" -lt "$MASK" ]; then continue # Can't tell on this network elif [ "$XBITS" -ge "$MASK" ]; then break 2 # Yes! fi # Can't tell done FAILURE="Destination host in ALLOW_INBOUND not found in an internal network." exit_failure $1 done fi PORTS=`echo "$FORWARD:" | cut -d: -f3` if [ -z "$PORTS" ]; then eval TEMP_$PARAM="$NET2:$NET:any" break else if ! check_ports; then FAILURE="$FAILURE in $PARAM." exit_failure $1 else eval TEMP_$PARAM="$NET2:$NET:$PORTS" break fi fi done if [ "$PARAM" == "ALLOW_INBOUND" ]; then MOD_ALLOW_INBOUND="$MOD_ALLOW_INBOUND $TEMP_ALLOW_INBOUND" else MOD_DENY_OUTBOUND="$MOD_DENY_OUTBOUND $TEMP_DENY_OUTBOUND" fi done done ALLOW_INBOUND=`echo $MOD_ALLOW_INBOUND` DENY_OUTBOUND=`echo $MOD_DENY_OUTBOUND` fi # Remove duplicate external addresses, for example those created when using channel bonding. if [ -n "$EXTERNAL_ADDRESSES" ]; then MOD_ADDRESSES="" for ADDRESS in $EXTERNAL_ADDRESSES; do if !(( `echo "$MOD_ADDRESSES" | grep -c "$ADDRESS"` )); then MOD_ADDRESSES="$MOD_ADDRESSES $ADDRESS" fi done EXTERNAL_ADDRESSES=`echo $MOD_ADDRESSES` fi # Make sure we own the addresses that we are statically mapping through the firewall, and verify internal hosts are actually from internal networks. if [ -n "$STATIC_INSIDE_OUTSIDE" ]; then for FORWARD in $STATIC_INSIDE_OUTSIDE; do NET1=`echo "$FORWARD:" | cut -d: -f1` OUTSIDE=`echo "$FORWARD:" | cut -d: -f2` for TAB in NULL; do for ADDRESS in $EXTERNAL_ADDRESSES $INTERNAL_ADDRESSES; do if [ "$ADDRESS" == "$OUTSIDE" ]; then break 2 fi done FAILURE="Outside address from STATIC_INSIDE_OUTSIDE not found on an external interface." exit_failure $1 done STROKE=`echo "$NET1/" | cut -d/ -f2` if [ -z "$STROKE" ]; then STROKE=32 fi for TAB in NULL; do for NET in $INTERNAL_NETWORKS $ADDITIONAL_ROUTED_NETWORKS; do MASK=`echo "$NET/" | cut -d/ -f2` if [ -z "$MASK" ]; then MASK=32 fi xbits # Is this host/network from one of our internal networks? if [ "$STROKE" -lt "$MASK" ]; then continue # Can't tell elif [ "$XBITS" -ge "$MASK" ]; then break 2 # Yes! fi # Can't tell done FAILURE="Inside address from STATIC_INSIDE_OUTSIDE not found in an internal network." exit_failure $1 done if (( `echo "$INTERNAL_ADDRESSES" | grep -c "$NET1"` )); then FAILURE="Internal address from STATIC_INSIDE_OUTSIDE found assigned to an internal interface." exit_failure $1 fi done fi # For FIREWALL_IP, make sure that our source address is on an external interface and our destination address is on an internal interface. for ADDRESS in $FIREWALL_IP; do OUTSIDE=`echo "$ADDRESS:" | cut -d: -f1` INSIDE=`echo "$ADDRESS:" | cut -d: -f2` for TAB in NULL; do for ADDRESS in $EXTERNAL_ADDRESSES; do if [ "$ADDRESS" == "$OUTSIDE" ]; then break 2 fi done FAILURE="Source address given in FIREWALL_IP must be configured on an *external* interface." exit_failure $1 done for TAB in NULL; do for ADDRESS in $INTERNAL_ADDRESSES; do if [ "$ADDRESS" == "$INSIDE" ]; then break 2 fi done FAILURE="Destination address given in FIREWALL_IP must be configured on an *internal* interface." exit_failure $1 done done # If we do not trust routed networks then add internal interfaces as "secured" addresses in the exit message. if [ -n "$STATIC_INTERNAL_INTERFACES" ] && [ "$TRUST_ROUTED_NETWORKS" != "yes" ]; then EXTERNAL_ADDRESSES="$EXTERNAL_ADDRESSES $INTERNAL_ADDRESSES" fi echo -n "." # Check that rp_filter interfaces are valid. for INTERFACE in $NO_RP_FILTER_INTERFACES; do if ! [ -w "/proc/sys/net/ipv4/conf/$INTERFACE/rp_filter" ]; then FAILURE="Cannot write to /proc/sys/net/ipv4/conf/$INTERFACE/rp_filter. Is the interface definition valid?" exit_failure $1 fi done # Check for Local Loopback interface. if !(( `ifconfig | grep -A1 "^lo" | grep "127\." | grep -c "255\.0\.0\.0"` )); then FAILURE="Local Loopback interface (lo) required but not found." exit_failure $1 fi # Make sure the filter table exists. if (( `iptables -t filter -nL 2>&1 | grep -c "Table does not exist"` )) || (( `iptables -t filter -nL 2>&1 | grep -c "can't initialize iptables table"` )); then FAILURE="Could not find 'filter' table. Did you compile support for all necessary modules?" exit_failure $1 fi # Check for the REJECT target if RFC 1122 compliance is enabled. if [ "$RFC_1122_COMPLIANT" == "yes" ] || [ "$RFC_1122_COMPLIANT" == "depends" ]; then if ((`iptables -t filter -i lo -o lo -I FORWARD -j REJECT 2>&1 | grep -c "No chain/target/match by that name"`)); then FAILURE="Could not find 'REJECT' target. Did you compile support for all necessary modules?" exit_failure $1 else iptables -t filter -D FORWARD 1 fi fi # If logging is enabled check for LOG and limit targets. if [ "$LOGGING" == "yes" ]; then if (( `iptables -t filter -i lo -o lo -I FORWARD -m limit 2>&1 | \ grep -c "No chain/target/match by that name"` )); then FAILURE="Could not find 'limit' target. Did you compile support for all necessary modules?" exit_failure $1 else iptables -t filter -D FORWARD 1 fi if (( `iptables -t filter -i lo -o lo -I FORWARD -j LOG 2>&1 | grep -c "No chain/target/match by that name"` )); then FAILURE="Could not find 'LOG' target. Did you compile support for all necessary modules?" exit_failure $1 else iptables -t filter -D FORWARD 1 fi fi # Check for the nat table if we need it. if [ -n "$STATIC_NAT_INTERFACES" ] || [ -n "$DYNAMIC_NAT_INTERFACES" ] || [ -n "$PORT_FORWARDS" ]; then if (( `iptables -t nat -nL 2>&1 | grep -c "Table does not exist"` )) || (( `iptables -t nat -nL 2>&1 | grep -c "can't initialize iptables table"` )); then FAILURE="Could not find 'nat' table. Did you compile support for all necessary modules?" exit_failure $1 fi fi # Check for the mangle table if we need it. if [ -n "$PORT_FORWARDS" ] || [ -n "$TTL_STEALTH_ROUTER" ]; then if (( `iptables -t mangle -nL 2>&1 | grep -c "Table does not exist"` )) || (( `iptables -t mangle -nL 2>&1 | grep -c "can't initialize iptables table"` )); then FAILURE="Could not find 'mangle' table, required for port forwarding and TTL stealth router mode. Did you compile support for all necessary modules?" exit_failure $1 fi fi # Determine if we need the MASQUERADE target. if [ -n "$DYNAMIC_NAT_INTERFACES" ] || ( [ -n "$DYNAMIC_INTERFACES" ] && [ -n "$PORT_FORWARDS" ] ); then if ((`iptables -t nat -I POSTROUTING -o lo -j MASQUERADE 2>&1 | grep -c "No chain/target/match by that name"`)); then FAILURE="Could not find 'MASQUERADE' target. Did you compile support for all necessary modules?" exit_failure $1 else iptables -t nat -D POSTROUTING 1 fi fi # Check for state match module. if (( `iptables -t filter -I OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 2>&1 | \ grep -c "No chain/target/match by that name"` )); then FAILURE="Failed to load state match module. Did you compile support for all necessary modules?" exit_failure $1 else iptables -t filter -D OUTPUT 1 fi # Check for various port forwarding requirements. if [ -n "$PORT_FORWARDS" ]; then if (( `iptables -t mangle -I OUTPUT -j MARK --set-mark "1" 2>&1 | grep -c "No chain/target/match by that name"` )); then FAILURE="Failed to load MARK target, required for port forwarding. Did you compile support for all necessary modules?" exit_failure $1 else iptables -t mangle -D OUTPUT 1 fi if (( `iptables -t mangle -I OUTPUT -m mark --mark "1" -j ACCEPT 2>&1 | grep -c "No chain/target/match by that name"` )); then FAILURE="Failed to netfilter MARK match module, required for port forwarding. Did you compile support for all necessary modules?" exit_failure $1 else iptables -t mangle -D OUTPUT 1 fi fi # Test for various ttl stealth router requirements. if [ "$TTL_STEALTH_ROUTER" == "yes" ]; then if (( `iptables -t mangle -I FORWARD -j ACCEPT 2>&1 | grep -c "No chain/target/match by that name"` )); then FAILURE="Linux kernel 2.4.18 or newer required for TTL_STEALTH_ROUTER mode." exit_failure $1 else iptables -t mangle -D FORWARD 1 fi if (( `iptables -t mangle -I FORWARD -j TTL --ttl-inc "1" 2>&1 | grep -c "No chain/target/match by that name"` )); then FAILURE="TTL_STEALTH_ROUTER mode requires a patched kernel." exit_failure $1 else iptables -t mangle -D FORWARD 1 fi fi # System and configuration approved. echo " [ PASSED ]" # Exit if we only want to do sanity checking. if [ "$1" == "check" ]; then exit fi ########################## # -- Firewall Section -- # ########################## echo -n "-> Building firewall." # Let no packets slip by while we are configuring the firewall. echo "0" > /proc/sys/net/ipv4/ip_forward # Enable kernel level reverse path filtering. echo "1" > /proc/sys/net/ipv4/conf/all/rp_filter for INTERFACE in $NO_RP_FILTER_INTERFACES; do echo "0" > /proc/sys/net/ipv4/conf/$INTERFACE/rp_filter done # Enable kernel level dynamic address handling. if [ -n "$DYNAMIC_INTERFACES" ]; then echo "1" > /proc/sys/net/ipv4/ip_dynaddr else echo "0" > /proc/sys/net/ipv4/ip_dynaddr fi # Flush all iptables rules and delete all user-defined chains including nat and mangle tables if they exist. iptables -t filter -F iptables -t filter -X if !(( `iptables -t nat -F 2>&1 | grep -c "Table does not exist"` )); then iptables -t nat -X fi if !(( `iptables -t mangle -F 2>&1 | grep -c "Table does not exist"` )); then iptables -t mangle -X fi # Drop traffic to and from blacklisted networks. for NETWORK in $BLACKLIST; do NET=`echo "$NETWORK:" | cut -d: -f1` if ! check_network; then PORTS="$NET" NET="0.0.0.0/0" else PORTS=`echo "$NETWORK:" | cut -d: -f2` fi if [ -n "$PORTS" ]; then PROTOCOL=`echo "$PORTS/" | cut -d/ -f2` PORT="--dport `echo "$PORTS/" | cut -d/ -f1 | cut -d- -f1,2 --output-delimiter=":"`" if [ "$PROTOCOL" == "tcp" ] || [ -z "$PROTOCOL" ]; then if [ "$IS_ROUTER" == "yes" ]; then iptables -t filter -I FORWARD -s $NET -p tcp $PORT -j DROP iptables -t filter -I FORWARD -d $NET -p tcp $PORT -j DROP fi iptables -t filter -I INPUT -s $NET -p tcp $PORT -j DROP iptables -t filter -I INPUT -d $NET -p tcp $PORT -j DROP iptables -t filter -I OUTPUT -s $NET -p tcp $PORT -j DROP iptables -t filter -I OUTPUT -d $NET -p tcp $PORT -j DROP fi if [ "$PROTOCOL" == "udp" ] || [ -z "$PROTOCOL" ]; then if [ "$IS_ROUTER" == "yes" ]; then iptables -t filter -I FORWARD -s $NET -p udp $PORT -j DROP iptables -t filter -I FORWARD -d $NET -p udp $PORT -j DROP fi iptables -t filter -I INPUT -s $NET -p udp $PORT -j DROP iptables -t filter -I INPUT -d $NET -p udp $PORT -j DROP iptables -t filter -I OUTPUT -s $NET -p udp $PORT -j DROP iptables -t filter -I OUTPUT -d $NET -p udp $PORT -j DROP fi else if [ "$IS_ROUTER" == "yes" ]; then iptables -t filter -I FORWARD -s $NET -j DROP iptables -t filter -I FORWARD -d $NET -j DROP fi iptables -t filter -I INPUT -s $NET -j DROP iptables -t filter -I INPUT -d $NET -j DROP iptables -t filter -I OUTPUT -s $NET -j DROP iptables -t filter -I OUTPUT -d $NET -j DROP fi done # Initialize trusted chain iptables -t filter -N TRUSTED if [ "$RFC_1122_COMPLIANT" == "yes" ]; then iptables -t filter -A TRUSTED -p icmp -j DROP # ICMP will be permitted elsewhere. iptables -t filter -A TRUSTED -j REJECT else iptables -t filter -A TRUSTED -j DROP fi # Reject state NEW without SYN flag set. (paranoia setting) if [ "$DROP_NEW_WITHOUT_SYN" == "yes" ]; then if [ "$LOGGING" == "yes" ]; then iptables -A INPUT -p tcp ! --syn -m state --state NEW -m limit --limit $LOG_LIMIT \ --limit-burst $LOG_BURST -j LOG --log-level $LOG_LEVEL --log-prefix "firewall: " fi iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP if [ "$IS_ROUTER" == "yes" ]; then if [ "$LOGGING" == "yes" ]; then iptables -A FORWARD -p tcp ! --syn -m state --state NEW -m limit --limit $LOG_LIMIT \ --limit-burst $LOG_BURST -j LOG --log-level $LOG_LEVEL --log-prefix "firewall: " fi iptables -A FORWARD -p tcp ! --syn -m state --state NEW -j DROP fi fi # Set logging preferences. Do not log broadcasts. if [ "$LOGGING" == "yes" ]; then echo "1" > /proc/sys/net/ipv4/conf/all/log_martians iptables -t filter -N LOGME iptables -t filter -I TRUSTED -j LOGME for BROADCAST in $BCAST_LIST; do iptables -t filter -I LOGME -d $BROADCAST -j RETURN done iptables -t filter -A LOGME -p icmp -m limit --limit $LOG_LIMIT --limit-burst $LOG_BURST -j LOG --log-level $LOG_LEVEL \ --log-prefix "firewall: " iptables -t filter -A LOGME -p tcp -m limit --limit $LOG_LIMIT --limit-burst $LOG_BURST -j LOG --log-level $LOG_LEVEL \ --log-prefix "firewall: " iptables -t filter -A LOGME -p udp -m limit --limit $LOG_LIMIT --limit-burst $LOG_BURST -j LOG --log-level $LOG_LEVEL \ --log-prefix "firewall: " else echo "0" > /proc/sys/net/ipv4/conf/all/log_martians fi echo -n "." # Accept icmp-echo-request packets if RFC-1122 compliance option is enabled. Limit logging of icmp packets. if [ "$RFC_1122_COMPLIANT" == "yes" ]; then if [ "$LOGGING" == "yes" ]; then for ADDRESS in $INTERNAL_ADDRESSES $EXTERNAL_ADDRESSES; do iptables -t filter -I TRUSTED 2 -d $ADDRESS -p icmp --icmp-type echo-request -j ACCEPT done for FORWARD in $STATIC_INSIDE_OUTSIDE; do ADDRESS=`echo "$FORWARD:" | cut -d: -f1` iptables -t filter -I TRUSTED 2 -d $ADDRESS -p icmp --icmp-type echo-request -j ACCEPT done for ADDRESS in $INTERNAL_ADDRESSES $EXTERNAL_ADDRESSES; do iptables -t filter -I TRUSTED -d $ADDRESS -p icmp --icmp-type echo-request -m limit --limit 2/second --limit-burst 10 -j ACCEPT done for FORWARD in $STATIC_INSIDE_OUTSIDE; do ADDRESS=`echo "$FORWARD:" | cut -d: -f1` iptables -t filter -I TRUSTED -d $ADDRESS -p icmp --icmp-type echo-request -m limit --limit 2/second --limit-burst 10 -j ACCEPT done if [ "$IS_ROUTER" != "yes" ] && [ -z "$EXTERNAL_ADDRESSES" ]; then iptables -t filter -I TRUSTED 2 -p icmp --icmp-type echo-request -j ACCEPT iptables -t filter -I TRUSTED -p icmp --icmp-type echo-request -m limit --limit 2/second --limit-burst 10 -j ACCEPT elif [ -n "$DMZ_INTERFACES" ]; then for INTERFACE in $DMZ_INTERFACES; do iptables -t filter -I TRUSTED 2 -o $INTERFACE -p icmp --icmp-type echo-request -j ACCEPT iptables -t filter -I TRUSTED -o $INTERFACE -p icmp --icmp-type echo-request -m limit --limit 2/second --limit-burst 10 -j ACCEPT done fi else for ADDRESS in $INTERNAL_ADDRESSES $EXTERNAL_ADDRESSES; do iptables -t filter -I TRUSTED -d $ADDRESS -p icmp --icmp-type echo-request -j ACCEPT done for FORWARD in $STATIC_INSIDE_OUTSIDE; do ADDRESS=`echo "$FORWARD:" | cut -d: -f1` iptables -t filter -I TRUSTED -d $ADDRESS -p icmp --icmp-type echo-request -j ACCEPT done if [ "$IS_ROUTER" != "yes" ] && [ -z "$EXTERNAL_ADDRESSES" ]; then iptables -t filter -I TRUSTED -p icmp --icmp-type echo-request -j ACCEPT elif [ -n "$DMZ_INTERFACES" ]; then for INTERFACE in $DMZ_INTERFACES; do iptables -t filter -I TRUSTED -o $INTERFACE -p icmp --icmp-type echo-request -j ACCEPT done fi fi fi # Insert trusted networks into trusted chain before everything else. for NETWORK in $PERMIT ; do iptables -t filter -I TRUSTED -s $NETWORK -j ACCEPT done # Insert local external networks into the trusted chain if option is enabled. if [ "$TRUST_LOCAL_EXTERNAL_NETWORKS" == "yes" ]; then for NETWORK in $EXTERNAL_NETWORKS; do iptables -t filter -I TRUSTED -s $NETWORK -j ACCEPT done fi # Set default policy for ESTABLISHED and RELATED connections to ACCEPT on FORWARD chains. iptables -t filter -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT if [ "$IS_ROUTER" == "yes" ]; then iptables -t filter -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT fi # Configure ALLOW_INBOUND and DENY_OUTBOUND. if [ -n "$INTERNAL_INTERFACES" ]; then for PARAM in ACCEPT TRUSTED; do if [ "$PARAM" == "TRUSTED" ]; then LIST="$DENY_OUTBOUND" else LIST="$ALLOW_INBOUND" fi for ITEM in $LIST; do NET="-s `echo "$ITEM:" | cut -d: -f1`" if [ "$NET" == "-s any" ]; then NET="" fi DEST="-d `echo "$ITEM:" | cut -d: -f2`" if [ "$DEST" == "-d any" ]; then DEST="" fi PORTS=`echo "$ITEM:" | cut -d: -f3` PROTOCOL=`echo "$PORTS/" | cut -d/ -f2` PORT="--dport `echo "$PORTS/" | cut -d/ -f1 | cut -d- -f1,2 --output-delimiter=":"`" if [ -z "$DEST" ] || [ -z "$NET" ]; then if [ "$PARAM" == "ACCEPT" ]; then TAB="-o" # (just to reuse the variable) For ALLOW_INBOUND else # classify packets sent out on internal interfaces and TAB="-i" # for DENY_OUTBOUND, received by internal interfaces, fi # unless both source and destination addresses are available. for INTERFACE in $INTERNAL_INTERFACES; do if !(( `echo "$INTERFACE" | grep -c ":"` )); then if [ "$PORTS" == "any" ]; then iptables -t filter -A FORWARD -m state --state NEW $TAB $INTERFACE $NET $DEST -j $PARAM else if [ "$PROTOCOL" == "tcp" ] || [ -z "$PROTOCOL" ]; then iptables -t filter -A FORWARD -m state --state NEW $TAB $INTERFACE $NET $DEST -p tcp $PORT -j $PARAM fi if [ "$PROTOCOL" == "udp" ] || [ -z "$PROTOCOL" ]; then iptables -t filter -A FORWARD -m state --state NEW $TAB $INTERFACE $NET $DEST -p udp $PORT -j $PARAM fi fi fi done else # The exception where we have both source and destination addresses if [ "$PORTS" == "any" ]; then iptables -t filter -A FORWARD -m state --state NEW $NET $DEST -j $PARAM else if [ "$PROTOCOL" == "tcp" ] || [ -z "$PROTOCOL" ]; then iptables -t filter -A FORWARD -m state --state NEW $NET $DEST -p tcp $PORT -j $PARAM fi if [ "$PROTOCOL" == "udp" ] || [ -z "$PROTOCOL" ]; then iptables -t filter -A FORWARD -m state --state NEW $NET $DEST -p udp $PORT -j $PARAM fi fi fi done done fi # For servers, only allow NEW connections to specified INPUT ports. For port forwarding, allow on FORWARD chain. for ITEM in $OPEN_PORTS $TRUSTED_PORTS; do NET="" if (( `echo "$ITEM:" | cut -d: -f2 | grep -c "."` )); then NET="-s `echo "$ITEM:" | cut -d: -f1`" ITEM=`echo "$ITEM:" | cut -d: -f2` fi PORTS=`echo "$ITEM" | cut -d/ -f1` PORTS=`echo "$PORTS" | cut -d- -f1,2 --output-delimiter=":"` PROTOCOL=`echo "$ITEM" | cut -d/ -f2` COUNT="0" for FORWARD in $PORT_FORWARDS; do IN_PORTS=`echo "$FORWARD" | cut -d: -f2 | cut -d- -f1,2 --output-delimiter=":"` if [ "`echo "$FORWARD" | cut -d: -f1`" == "$PROTOCOL" ] && [ "$PORTS" == "$IN_PORTS" ]; then DEST=`echo "$FORWARD" | cut -d: -f3` DPORTS=`echo "$FORWARD" | cut -d: -f4 | cut -d- -f1,2 --output-delimiter=":"` if [ -z "$DPORTS" ]; then DPORTS="$IN_PORTS" fi iptables -t filter -A FORWARD -m state --state NEW $NET -d $DEST -p $PROTOCOL --dport $DPORTS -j ACCEPT COUNT="1" if [ -z "$NET" ]; then continue 2 # i.e. This port forward is open to everyone. fi fi done if [ "$COUNT" == "0" ]; then iptables -t filter -A INPUT -m state --state NEW $NET -p $PROTOCOL --dport $PORTS -j ACCEPT fi done echo -n "." # For routers, allow routing of internal and routed networks on internal interfaces. Fix traceroutes under DNAT info-leak-bug. if [ "$IS_ROUTER" == "yes" ]; then COUNT="0" TAB="" for INTERFACE in $STATIC_INTERNAL_INTERFACES; do COUNT=$((COUNT + 1)) NETWORK=`echo "$INTERNAL_NETWORKS" | cut -d\ -f$COUNT` INTERFACE=`echo "$INTERFACE" | cut -d: -f1` iptables -t filter -A OUTPUT -o $INTERFACE -d $NETWORK -p icmp -j ACCEPT if [ -z "$DMZ_INTERFACES" ] || !(( `echo "$DMZ_INTERFACES" | grep -c "$INTERFACE"` )); then if [ "$SHARED_INTERNAL" == "yes" ]; then iptables -t filter -A FORWARD -m state --state NEW -i $INTERFACE -s $NETWORK -j ACCEPT else for DEST in $EXTERNAL_INTERFACES $DMZ_INTERFACES; do if !(( `echo "$DEST" | grep -c ":"` )); then iptables -t filter -A FORWARD -m state --state NEW -i $INTERFACE -s $NETWORK -o $DEST -j ACCEPT fi done fi else for DEST in $EXTERNAL_INTERFACES; do if !(( `echo "$DEST" | grep -c ":"` )); then iptables -t filter -A FORWARD -m state --state NEW -i $INTERFACE -s $NETWORK -o $DEST -j ACCEPT fi done fi if [ "$TRUST_ROUTED_NETWORKS" == "yes" ] && ( [ -z "$DMZ_INTERFACES" ] || \ !(( `echo "$DMZ_INTERFACES" | grep -c "$INTERFACE"` )) ); then iptables -t filter -A INPUT -m state --state NEW -i $INTERFACE -s $NETWORK -j ACCEPT fi if [ "$INTERNAL_DHCP" == "yes" ]; then if !(( `echo "$TAB" | grep -c "$INTERFACE"` )); then iptables -t filter -A INPUT -m state --state NEW -i $INTERFACE -p udp --dport 67 -j ACCEPT fi TAB="$TAB $INTERFACE" fi if [ -z "$DMZ_INTERFACES" ] || !(( `echo "$DMZ_INTERFACES" | grep -c "$INTERFACE"` )); then for NETWORK in $ADDITIONAL_ROUTED_NETWORKS; do iptables -t filter -A OUTPUT -o $INTERFACE -d $NETWORK -p icmp -j ACCEPT if [ "$SHARED_INTERNAL" == "yes" ]; then iptables -t filter -A FORWARD -m state --state NEW -i $INTERFACE -s $NETWORK -j ACCEPT else for DEST in $EXTERNAL_INTERFACES $DMZ_INTERFACES; do if !(( `echo "$DEST" | grep -c ":"` )); then iptables -t filter -A FORWARD -m state --state NEW -i $INTERFACE -s $NETWORK -o $DEST -j ACCEPT fi done fi if [ "$TRUST_ROUTED_NETWORKS" == "yes" ]; then iptables -t filter -A INPUT -m state --state NEW -i $INTERFACE -s $NETWORK -j ACCEPT fi done fi done for INTERFACE in $DYNAMIC_INTERNAL_INTERFACES; do iptables -t filter -A OUTPUT -o $INTERFACE -p icmp -j ACCEPT iptables -t filter -A FORWARD -m state --state NEW -i $INTERFACE -j ACCEPT if [ "$TRUST_ROUTED_NETWORKS" == "yes" ]; then iptables -t filter -A INPUT -m state --state NEW -i $INTERFACE -j ACCEPT fi done fi # ICMP DNAT information leak workaround. iptables -t filter -A OUTPUT -p icmp -m state --state INVALID -j DROP # Set up static address translations. if [ "$IS_ROUTER" == "yes" ]; then for FORWARD in $STATIC_INSIDE_OUTSIDE; do INSIDE=`echo "$FORWARD:" | cut -d: -f1` OUTSIDE=`echo "$FORWARD:" | cut -d: -f2` iptables -t nat -A POSTROUTING -s $INSIDE -j SNAT --to-source $OUTSIDE if !(( `echo "$INSIDE" | grep -c "/"` )); then for ITEM in $ALLOW_INBOUND; do NETWORK="-s `echo "$ITEM" | cut -d: -f1`" # Source if [ "$NETWORK" == "-s any" ]; then NETWORK="" fi NET=`echo "$ITEM" | cut -d: -f2` # Destination PORTS=`echo "$ITEM" | cut -d: -f3` if [ -n "$PORTS" ]; then PORT="--dport `echo "$PORTS/" | cut -d/ -f1 | cut -d- -f1,2 --output-delimiter=":"`" PROTOCOL=`echo "$PORTS/" | cut -d/ -f2` fi if [ "$NET" != "any" ]; then # If there is a specific destination -- NET1="$INSIDE" # Determine if this is part of it. xbits MASK=`echo "$NET/" | cut -d/ -f2` if [ -z "$MASK" ]; then MASK=32 fi fi if [ "$NET" == "any" ] || [ "$XBITS" -ge "$MASK" ]; then if [ "$RFC_1122_COMPLIANT" == "yes" ] || [ "$RFC_1122_COMPLIANT" == "depends" ]; then iptables -t nat -A PREROUTING $NETWORK -d $OUTSIDE \ -p icmp --icmp-type echo-request -j DNAT --to-destination $INSIDE fi if [ "$PORTS" == "any" ]; then iptables -t nat -A PREROUTING $NETWORK -d $OUTSIDE -p tcp -j DNAT --to-destination $INSIDE iptables -t nat -A PREROUTING $NETWORK -d $OUTSIDE -p udp -j DNAT --to-destination $INSIDE else if [ "$PROTOCOL" == "tcp" ] || [ "$PROTOCOL" == "udp" ]; then iptables -t nat -A PREROUTING $NETWORK -d $OUTSIDE -p $PROTOCOL $PORT -j DNAT --to-destination $INSIDE else iptables -t nat -A PREROUTING $NETWORK -d $OUTSIDE -p tcp $PORT -j DNAT --to-destination $INSIDE iptables -t nat -A PREROUTING $NETWORK -d $OUTSIDE -p udp $PORT -j DNAT --to-destination $INSIDE fi fi fi done for NETWORK in $PERMIT; do iptables -t nat -A PREROUTING -s $NETWORK -d $OUTSIDE -j DNAT --to-destination $INSIDE done COUNT="0" NET1="$INSIDE" for INTERFACE in $STATIC_INTERNAL_INTERFACES; do COUNT=$((COUNT + 1)) NET=`echo $INTERNAL_NETWORKS | cut -d\ -f$COUNT` INTERFACE=`echo $INTERFACE | cut -d: -f1` iptables -t nat -A PREROUTING -s $NET -d $OUTSIDE -j DNAT --to-destination $INSIDE xbits MASK=`echo "$NET/" | cut -d/ -f2` if [ -z "$MASK" ]; then MASK=32 fi ADDRESS=`echo $INTERNAL_ADDRESSES | cut -d\ -f$COUNT` if [ "$XBITS" -ge "$MASK" ]; then iptables -t nat -A POSTROUTING -s $NET -o $INTERFACE -d $INSIDE -j SNAT --to-source $ADDRESS fi for NET in $ADDITIONAL_ROUTED_NETWORKS; do xbits if [ "$XBITS" -ge "$MASK" ]; then iptables -t nat -A POSTROUTING -s $NET -o $INTERFACE -d $INSIDE -j SNAT --to-source $ADDRESS fi iptables -t nat -A PREROUTING -s $NET -d $OUTSIDE -j DNAT --to-destination $INSIDE done done for INTERFACE in $DYNAMIC_INTERNAL_INTERFACES; do iptables -t nat -A PREROUTING -s $NETWORK -d $OUTSIDE -j DNAT --to-destination $INSIDE done fi # Lets not have people mistaking the router for the internal host. iptables -t filter -I INPUT -d $OUTSIDE -j DROP done fi # Configure NAT. if [ "$IS_ROUTER" == "yes" ]; then COUNT="0" for INTERFACE in $STATIC_NAT_INTERFACES; do COUNT=$((COUNT + 1)) ADDRESS=`echo "$NAT_ADDRESSES" | cut -d\ -f$COUNT` if [ -n "$DYNAMIC_INTERNAL_INTERFACES" ]; then iptables -t nat -A POSTROUTING -o $INTERFACE -j SNAT --to-source $ADDRESS else for NETWORK in $NAT_NETWORKS $ADDITIONAL_ROUTED_NETWORKS; do iptables -t nat -A POSTROUTING -s $NETWORK -o $INTERFACE -j SNAT --to-source $ADDRESS done fi done for INTERFACE in $DYNAMIC_NAT_INTERFACES; do if [ -n "$DYNAMIC_INTERNAL_INTERFACES" ]; then iptables -t nat -I POSTROUTING -o $INTERFACE -j MASQUERADE else for NETWORK in $NAT_NETWORKS $ADDITIONAL_ROUTED_NETWORKS; do iptables -t nat -I POSTROUTING -s $NETWORK -o $INTERFACE -j MASQUERADE done fi done fi echo -n "." # Configure port forwarding. for FORWARD in $PORT_FORWARDS; do PROTOCOL=`echo "$FORWARD" | cut -d: -f1` IN_PORTS=`echo "$FORWARD" | cut -d: -f2` DEST=`echo "$FORWARD" | cut -d: -f3` PORTS=`echo "$FORWARD" | cut -d: -f4` if [ -z "$PORTS" ]; then PORTS="$IN_PORTS" fi IN_PORTS=`echo "$IN_PORTS" | cut -d- -f1,2 --output-delimiter=":"` DPORTS=`echo "$PORTS" | cut -d- -f1,2 --output-delimiter=":"` # Support DNAT for locally generated connections, new in iptables 1.2.6a and kernel 2.4.19 iptables -t nat -A OUTPUT -o lo -p $PROTOCOL --dport $IN_PORTS -j DNAT --to-destination $DEST:$PORTS > /dev/null 2>&1 COUNT="0" while (( `echo "${INTERFACE_TAB[$((COUNT + 1))]}" | grep -c "."` )); do COUNT=$((COUNT + 1)) if (( `echo "$DYNAMIC_INTERFACES" | grep -c "${INTERFACE_TAB[$COUNT]}"` )); then iptables -t nat -I POSTROUTING -m mark --mark "$COUNT" -o ${INTERFACE_TAB[$COUNT]} -d $DEST \ -p $PROTOCOL --dport $DPORTS -j MASQUERADE else iptables -t nat -I POSTROUTING -m mark --mark "$COUNT" -o ${INTERFACE_TAB[$COUNT]} -d $DEST \ -p $PROTOCOL --dport $DPORTS -j SNAT --to-source ${ADDRESS_TAB[$COUNT]} fi if (( `echo "$DYNAMIC_INTERNAL_INTERFACES" | grep -c "${INTERFACE_TAB[$COUNT]}"` )) && \ [ "$PORT_FWD_ROUTED_NETWORKS" == "yes" ]; then iptables -t nat -A PREROUTING -i ${INTERFACE_TAB[$COUNT]} \ -p $PROTOCOL --dport $IN_PORTS -j DNAT --to-destination $DEST:$PORTS iptables -t mangle -A PREROUTING -i ${INTERFACE_TAB[$COUNT]} \ -p $PROTOCOL --dport $IN_PORTS -j MARK --set-mark "$COUNT" continue # We will accept anything on this interface. fi for ADDRESS in $PORT_FORWARD_ADDRESSES; do for ITEM in $OPEN_PORTS $TRUSTED_PORTS; do if (( `echo "$ITEM:" | cut -d: -f2 | grep -c "."` )); then NET="-s `echo "$ITEM:" | cut -d: -f1`" ITEM=`echo "$ITEM:" | cut -d: -f2` else NET="" fi PORT=`echo "$ITEM/" | cut -d/ -f1` PORT=`echo "$PORT" | cut -d- -f1,2 --output-delimiter=":"` if [ "$PROTOCOL" == "`echo "$ITEM/" | cut -d/ -f2`" ] && [ "$PORT" == "$IN_PORTS" ]; then if (( `echo "$DYNAMIC_NAT_INTERFACES" | grep -c "${INTERFACE_TAB[$COUNT]}"` )); then iptables -t nat -A PREROUTING $NET -i ${INTERFACE_TAB[$COUNT]} \ -p $PROTOCOL --dport $IN_PORTS -j DNAT --to-destination $DEST:$PORTS iptables -t mangle -A PREROUTING $NET -i ${INTERFACE_TAB[$COUNT]} \ -p $PROTOCOL --dport $IN_PORTS -j MARK --set-mark "$COUNT" else iptables -t nat -A PREROUTING $NET -d $ADDRESS -i ${INTERFACE_TAB[$COUNT]} \ -p $PROTOCOL --dport $IN_PORTS -j DNAT --to-destination $DEST:$PORTS iptables -t mangle -A PREROUTING $NET -d $ADDRESS -i ${INTERFACE_TAB[$COUNT]} \ -p $PROTOCOL --dport $IN_PORTS -j MARK --set-mark "$COUNT" fi if [ -z "$NET" ]; then continue 2 # This port forward is open to everyone. fi fi done for NETWORK in ${NETWORK_TAB[$COUNT]}; do if (( `echo "$DYNAMIC_NAT_INTERFACES" | grep -c "${INTERFACE_TAB[$COUNT]}"` )); then iptables -t nat -A PREROUTING -i ${INTERFACE_TAB[$COUNT]} -s $NETWORK \ -p $PROTOCOL --dport $IN_PORTS -j DNAT --to-destination $DEST:$PORTS iptables -t mangle -A PREROUTING -i ${INTERFACE_TAB[$COUNT]} -s $NETWORK \ -p $PROTOCOL --dport $IN_PORTS -j MARK --set-mark "$COUNT" else iptables -t nat -A PREROUTING -d $ADDRESS -i ${INTERFACE_TAB[$COUNT]} -s $NETWORK \ -p $PROTOCOL --dport $IN_PORTS -j DNAT --to-destination $DEST:$PORTS iptables -t mangle -A PREROUTING -d $ADDRESS -i ${INTERFACE_TAB[$COUNT]} -s $NETWORK \ -p $PROTOCOL --dport $IN_PORTS -j MARK --set-mark "$COUNT" fi done for NETWORK in $PERMIT; do if (( `echo "$DYNAMIC_NAT_INTERFACES" | grep -c "${INTERFACE_TAB[$COUNT]}"` )); then iptables -t nat -A PREROUTING -i ${INTERFACE_TAB[$COUNT]} -s $NETWORK \ -p $PROTOCOL --dport $IN_PORTS -j DNAT --to-destination $DEST:$PORTS iptables -t mangle -A PREROUTING -i ${INTERFACE_TAB[$COUNT]} -s $NETWORK \ -p $PROTOCOL --dport $IN_PORTS -j MARK --set-mark "$COUNT" else iptables -t nat -A PREROUTING -d $ADDRESS -i ${INTERFACE_TAB[$COUNT]} -s $NETWORK \ -p $PROTOCOL --dport $IN_PORTS -j DNAT --to-destination $DEST:$PORTS iptables -t mangle -A PREROUTING -d $ADDRESS -i ${INTERFACE_TAB[$COUNT]} -s $NETWORK \ -p $PROTOCOL --dport $IN_PORTS -j MARK --set-mark "$COUNT" fi done # Done with sources if (( `echo "$DYNAMIC_NAT_INTERFACES" | grep -c "${INTERFACE_TAB[$COUNT]}"` )); then break fi done # Done with destination addresses done # Done with interfaces done # Done with forwards # Source nat outbound connections generated by the local machine to address defined in FIREWALL_IP. for ADDRESS in $FIREWALL_IP; do OUTSIDE=`echo "$ADDRESS:" | cut -d: -f1` INSIDE=`echo "$ADDRESS:" | cut -d: -f2` iptables -t nat -A POSTROUTING -s $OUTSIDE -j SNAT --to-source $INSIDE done # Accept new connections from the loopback interface (localhost). iptables -t filter -A INPUT -i lo -j ACCEPT # Jump to the trusted chain if this packet establishes a NEW connection. iptables -t filter -A INPUT -m state --state NEW -j TRUSTED if [ "$IS_ROUTER" == "yes" ]; then iptables -t filter -A FORWARD -m state --state NEW -j TRUSTED fi # Enable TTL stealth router mode. if [ "$TTL_STEALTH_ROUTER" == "yes" ]; then iptables -t mangle -I FORWARD -j TTL --ttl-inc "1" fi # Set default policies. This was moved to end of firewall section to avoid problems with filesystems mounted on NFS. iptables -t filter -P INPUT DROP iptables -t filter -P FORWARD DROP iptables -t filter -P OUTPUT ACCEPT if !(( `iptables -t nat -P PREROUTING ACCEPT 2>&1 | grep -c "Table does not exist"` )); then iptables -t nat -P POSTROUTING ACCEPT iptables -t nat -P OUTPUT ACCEPT fi if !(( `iptables -t mangle -P PREROUTING ACCEPT 2>&1 | grep -c "Table does not exist"` )); then iptables -t mangle -P OUTPUT ACCEPT iptables -t mangle -P POSTROUTING ACCEPT > /dev/null 2>&1 # New 2.4.18 builtin mangle chains iptables -t mangle -P INPUT ACCEPT > /dev/null 2>&1 iptables -t mangle -P FORWARD ACCEPT > /dev/null 2>&1 fi # Now that everything is configured we can enable ip_forward for routers. if [ "$IS_ROUTER" == "yes" ]; then echo "1" > /proc/sys/net/ipv4/ip_forward fi # Print exit message. echo " [ DONE ]" if [ -n "$EXTERNAL_ADDRESSES" ]; then echo -n "-> Successfully secured the following addresses: " if [ -n "$FIREWALL_IP" ]; then echo "`echo $EXTERNAL_ADDRESSES | sed s/\"$OUTSIDE"/\"$INSIDE"/ | sed s/\ /,\ /g`." else echo "`echo $EXTERNAL_ADDRESSES | sed s/\ /,\ /g`." fi fi if [ "$IS_ROUTER" == "yes" ]; then if [ -n "$DYNAMIC_EXTERNAL_INTERFACES" ]; then echo "-> Successfully secured the following external interfaces: `echo $DYNAMIC_EXTERNAL_INTERFACES | sed s/\ /,\ /g`." fi if [ -n "$INTERNAL_NETWORKS" ] || [ -n "$ADDITIONAL_ROUTED_NETWORKS" ]; then echo "-> Routing is enabled for the following networks: `echo $INTERNAL_NETWORKS $ADDITIONAL_ROUTED_NETWORKS | \ sed s/\ /,\ /g`." fi if [ -n "$DYNAMIC_INTERNAL_INTERFACES" ]; then echo "-> Alert! Routing is enabled for ALL connections through: `echo $DYNAMIC_INTERNAL_INTERFACES \ | sed s/\ /,\ /g`." fi fi # Write a configuration file if passed appropriate arguments. if [ "$1" == "save" ] || [ "$1" == "update" ]; then if [ -a "$CONFIG" ]; then if (( `head -n 1 "$CONFIG" | grep -c "# rc.firewall Linux Firewall configuration -- http://lfw.sf.net/"` )) || (( `head -c 30 "$CONFIG" | grep -c "# Linux Firewall configuration"` )); then CONFIG_VERSION=`head -n 4 "$CONFIG" | tail -n 1 | cut -d\" -f2` if [ "$1" == "save" ]; then if [ "$VERSION" != "$CONFIG_VERSION" ]; then echo "-> NOTICE: The file $CONFIG is a configuration file saved by an" echo "-> older version of rc.firewall. The 'save' feature will overwrite" echo "-> the contents of this file with the configuration found in" echo "-> `echo $0 | sed s#^\./#$PWD/#`. If you mean to use the 'update'" echo "-> feature instead, press CTRL-C now to abort, otherwise" echo "-> press any key to go ahead and overwrite $CONFIG." read -rsn1 elif [ "$REQUIRE_EXTERNAL_CONFIG" == "yes" ] || ! iptables_md5check ; then echo "-> NOTICE: The file $CONFIG already exists. The 'save' feature" echo "-> will overwrite the contents of this file with the configuration" echo "-> found in `echo $0 | sed s#^\./#$PWD/#`. If this is what you meant" echo "-> to do press any key to continue, otherwise press CTRL-C to abort." read -rsn1 fi fi else echo "-> WARNING: The file $CONFIG is associated with another program!" echo "-> Press any key to overwrite or CTRL-C to abort." read -rsn1 fi fi cat << EOF > $CONFIG # rc.firewall Linux Firewall configuration -- http://lfw.sf.net/ # Generated by: `echo $0 | sed s#^\./#$PWD/#` $1 `echo $2 | sed s#^\./#$PWD/#` # on `date`. # Generated with version: "$VERSION". # `echo $0 | sed s#^\./##` file md5sum: "`md5sum $0 | cut -d\ -f1`" # Running iptables ruleset md5sum: "`iptables-save | grep -v "Generated" | grep -v "Completed" | sed s/\\\[.*\\\]//g | md5sum | cut -d\ -f1`" # Configuration directives md5sum: "`echo "|$ORIG_PERMIT|$ORIG_INTERNAL_INTERFACES|$ORIG_DYNAMIC_INTERFACES|$ORIG_DENY_OUTBOUND|$ORIG_ALLOW_INBOUND||$BLACKLIST|$ORIG_STATIC_INSIDE_OUTSIDE|$ORIG_PORT_FORWARDS|$PORT_FWD_ALL|$PORT_FWD_ROUTED_NETWORKS|$ADDITIONAL_ROUTED_NETWORKS|$TRUST_ROUTED_NETWORKS|$SHARED_INTERNAL|$FIREWALL_IP|$TRUST_LOCAL_EXTERNAL_NETWORKS|$DMZ_INTERFACES|$NAT_EXTERNAL|$ADDITIONAL_NAT_INTERFACES|$IGNORE_INTERFACES|$LOGGING|$NO_RP_FILTER_INTERFACES|$INTERNAL_DHCP|$ORIG_RFC_1122_COMPLIANT|$DROP_NEW_WITHOUT_SYN|$DUMP_TCP_ON_INIT|$TTL_STEALTH_ROUTER|$LOG_LIMIT|$LOG_BURST|$LOG_LEVEL|" | md5sum | cut -d\ -f1`" PERMIT="$ORIG_PERMIT" INTERNAL_INTERFACES="$ORIG_INTERNAL_INTERFACES" DYNAMIC_INTERFACES="$ORIG_DYNAMIC_INTERFACES" DENY_OUTBOUND="$ORIG_DENY_OUTBOUND" ALLOW_INBOUND="$ORIG_ALLOW_INBOUND" BLACKLIST="$BLACKLIST" STATIC_INSIDE_OUTSIDE="$ORIG_STATIC_INSIDE_OUTSIDE" PORT_FORWARDS="$ORIG_PORT_FORWARDS" PORT_FWD_ALL="$PORT_FWD_ALL" PORT_FWD_ROUTED_NETWORKS="$PORT_FWD_ROUTED_NETWORKS" ADDITIONAL_ROUTED_NETWORKS="$ADDITIONAL_ROUTED_NETWORKS" TRUST_ROUTED_NETWORKS="$TRUST_ROUTED_NETWORKS" SHARED_INTERNAL="$SHARED_INTERNAL" FIREWALL_IP="$FIREWALL_IP" TRUST_LOCAL_EXTERNAL_NETWORKS="$TRUST_LOCAL_EXTERNAL_NETWORKS" DMZ_INTERFACES="$DMZ_INTERFACES" NAT_EXTERNAL="$NAT_EXTERNAL" ADDITIONAL_NAT_INTERFACES="$ADDITIONAL_NAT_INTERFACES" IGNORE_INTERFACES="$IGNORE_INTERFACES" LOGGING="$LOGGING" NO_RP_FILTER_INTERFACES="$NO_RP_FILTER_INTERFACES" INTERNAL_DHCP="$INTERNAL_DHCP" RFC_1122_COMPLIANT="$ORIG_RFC_1122_COMPLIANT" DROP_NEW_WITHOUT_SYN="$DROP_NEW_WITHOUT_SYN" DUMP_TCP_ON_INIT="$DUMP_TCP_ON_INIT" TTL_STEALTH_ROUTER="$TTL_STEALTH_ROUTER" LOG_LIMIT="$LOG_LIMIT" LOG_BURST="$LOG_BURST" LOG_LEVEL="$LOG_LEVEL" return EOF iptables-save >> $CONFIG chown root:root $CONFIG chmod 600 $CONFIG echo "-> Firewall configuration saved to $CONFIG" fi # Dump current TCP sessions if requested. if [ "$DUMP_TCP_ON_INIT" == "yes" ]; then dump_tcp fi # Done!