#!/bin/bash # # rc.firewall, ver 0.3 - 1 Nov 2002 # http://www.honeynet.org/papers/honeynet/tools/ # # Rob McMillen # # # CHANGES: # 19 Nov 2002: Restricted Firewall Outbound traffic # see the TCP_ALLOWED_OUT and # UDP_ALLOWED_OUT variables. # 5 Nov 2002: Added MANAGER variable to # restrict what ips have access # to the management interface. # 1 Nov 2002: Added rules for management # Interface. # Added rule to allow outbound # DHCP requests in bridge mode. # * NEEDS MORE WORK * # Moved variables around in an # effort to better organize them # into a logical order. # Changed DNS query handling. # 20 Oct 2002: Changed PURPOSE STATEMENT # Changed Variable names and # grouped like variables # together. # Removed BRIDGE_IFACE var. # # PURPOSE # To deploy Data Control requirements for a Honeynet deployment. # This script uses IPTables to create a gateway that counts inbound # and outbound connections and blocks connections once a limit # has been met. Also has the capability to work with Snort-Inline. # Script can work in either GenI(routing) or GenII(bridging) mode. # For more about Honeynets, refer to # # http://www.honeynet.org/papers/honeynet/ # # REQUIREMENTS # In order for the genII script to work, your kernel must # be compiled with bride and bridge firewall support. # Red Hat kernel 2.4.18-3 has this by default, most other # kernels do not. If yours does not, you will most likely # need to patch and recompile your kernel. You can find the # patch at # http://bridge.sourceforge.net/download.html # # You will also need bridge utilities to allow this script to # enable/configure bridging. I used bridge-utils-0.9.3-4 during # the testing of this script. # # # INSTALLATION # Once you have configured the variables of this script, you # simply execute this script. It calls on IPTables and # does everything for you (nice, huh? :). You must have IPTables # installed on your system, and kernel version 2.4.x. # # # MODE is the mode the firewall will use to operate. There are # two possible values at this time: nat, bridge. "nat" is GenI # where your gateway is routing in layer3. "bridge" is GenII # where your gateway is bridging in layer2. Of these two # options, "bridge" is the prefered, more secure MODE. # # MODE="nat" In this mode, the firewall will translate each ip # in the PUBLIC_IP variable to each ip in the HPOT_IP variable. # Order is important, so make sure you place the ips in the # variables as you would like them translated. For example, # PUBLIC_IP="192.168.1.1 192.168.1.2 192.168.1.3" # HPOT_IP="192.168.0.1 192.168.0.2 192.168.0.3" # # will translate as follows: # 192.168.1.1 => 192.168.0.1 # 192.168.1.2 => 192.168.0.2 # 192.168.1.3 => 192.168.0.3 # # Each variable is a space delimited list; therefore, you can have # as many as you want (or as many as an interface can have aliases). # # The following variables must match the setting for the translated # network. In the example above, they would be the settings of the # 192.168.0.* network # # LAN_IP_RANGE="192.168.0.0/24" # LAN_BCAST_ADDRESS="192.168.0.255" # # MODE="bridge" In this mode, the firewall will act as a bridge, # bridging and bridge firewalling will need to be compiled into the # kernel. Default kernel for Redhat 7.3 (2.4.18-3) has it, but its # upgrade does not. All other default kernels do not support IPtables # in bridging mode. Therefore, your bridge will allow everything in and # everything out, completely bypassing your firewall rules. # # The following variables must match the settings for the bridged # network. If both sides of the bridge are on 10.0.0.*, # # LAN_IP_RANGE="192.168.1.0/24" # LAN_BCAST_ADDRESS="192.168.1.255" # # # NOTE: A quick check to ensure you have the LAN variables correct # is to check /var/log/messages. If you see logs stating # SPOOFED SOURCE, you probably have them set wrong. Also, # make sure your Honeypot default gateway is set to the # firewall internal interface when in nat mode and the # border router or routing device when in bridge mode. #### If you want to see all the commands or which command is giving your # problems, remove the comment below. #set -x #************************************************************************* # USER VARIABLE SECTION #************************************************************************* ############### # COMMON VARS # ############### # The MODE variable tells the script to #setup a bridge HoneyWall # or a NATing HoneyWall. #MODE="nat" MODE="bridge" # A space delimited list of honeypots IPs (public IP) # If you are in "bridge" mode, this is the list of your # honeypot IP's that will be behind the bridge. If you are # in "nat" mode, this is the list of public IPs you will # be using for IP address translation. PUBLIC_IP="192.168.1.144" # We need to allow continual dns service to internal honeypots in order # for everything to work. If you have a dns server in the honeynet, we # can make all other honeypots use it as a primary and only allow the # server to perform unlimited dns queries (udp only) to the internet # or we can allow all (your choice). I only have one honeypot so my # choice is simple. DNS_SVRS="" # Space delimited list of # Legal DNS Servers ### Variable for external network INET_IFACE="eth0" # Firewall Public interface ### Variables for internal network LAN_IFACE="eth1" # Firewall interface on internal network LAN_IP_RANGE="192.168.0.0/24" # IP Range of internal network LAN_BCAST_ADRESS="192.168.0.255" # IP Broadcast range for internal network ### IPTables script can be used with the Snort-Inline filter ### You can find the current release at ### http://www.snort.org/dl/contrib/patches/inline/ #QUEUE="yes" # Use experimental QUEUE support QUEUE="no" # Do not use experimental QUEUE support ### Set the connection outbound limits for different protocols. SCALE="hour" # second, minute, hour, etc. TCPRATE="9" # Number of TCP connections per $SCALE UDPRATE="20" # Number of UDP connections per $SCALE ICMPRATE="50" # Number of ICMP connections per $SCALE OTHERRATE="10" # Number of other IP connections per $SCALE ###################### # END OF COMMON VARS # ###################### ########################## # VARIABLES FOR NAT MODE # ########################## # You use these variables ONLY if you are using NAT mode. # If you are in bridging mode, then these variables will # not be used. # ALIAS_MASK="255.255.255.0" # Network mask to be used alias HPOT_IP="192.168.0.144" # Space delimited list of Honeypot ips # NOTE: MUST HAVE SAME NUMBER OF IPS AS # PUBLIC_IP VARIABLE. ############################# # END OF NAT MODE VARIABLES # ############################# ################################## # SPECIAL CONSIDERATION VARIABLE # ################################## # Space delimited list of honypots authorized to do external dns # queries (udp only). Please plan accordingly. If in nat mode, # the host must use the translated address. If in bridge mode, # the host must use the public address. Or, you can designate # a single host to perform queries for the Honeynet and limit # all other hosts. # # I've actually made an attempt to make it real easy to set this up. # If you want to restrict the hosts that can access public dns servers, # set the DNS_HOST variable to the ip of the honeypot allowed to # make queries. Otherwise, leave it blank and the proper set of # ips will be assigned in order to allow all of your honeypots to # make dns queries. DNS_HOST="" ###################################### # VARIABLES FOR MANAGEMENT INTERFACE # ###################################### # Interface for remote management. If set to br0, it will assign # MANAGE_IP to the bridge logical interface and allow its use # as a management interface. If you do not want to use a # management interface, set it to "none" #MANAGE_IFACE="br0" #MANAGE_IFACE="eth2" MANAGE_IFACE="none" MANAGE_IP="192.168.0.104" # IP of management Interface MANAGE_NETMASK="255.255.255.0" # Netmask of management Interface # Space delimited list of tcp ports allowed into the management interface ALLOWED_TCP_IN="22" # IP allowed to connect to the management interface # If set to "any", it will allow anyone to attempt to connect. # The notation ip/mask or a space delimited list of ips are # allowed. #MANAGER="any" MANAGER="10.1.1.1 192.168.0.0/24" #################### # END OF MANAGE VARS #################### ########################################################## # VARIABLES THAT RESTRICT WHAT THE FIREWALL CAN SEND OUT # ########################################################## # This variable will limit outbound Firewall connections # to ports identified in the ALLOWED_TCP_OUT and # ALLOWED_UDP_OUT variables. If set to yes, it will # restrict the firewall. If set to no, it will allow all # outbound connections generated by the firewall. # NOTE: There must be a management interface in bridge # mode in order to have a firewall interface to restrict. RESTRICT="yes" #RESTRICT="no" ALLOWED_UDP_OUT="53 123" ALLOWED_TCP_OUT="22" ########################## # END RESTRICT VARIABLES # ########################## ############################################ # LOCATION OF PROGRAMS USED BY THIS SCRIPT # ############################################ IPTABLES="/sbin/iptables" BRIDGE="/usr/sbin/brctl" IFCONFIG="/sbin/ifconfig" ROUTE="/sbin/route" MODPROBE="/sbin/modprobe" #################### # END OF PROG VARS # #################### #************************************************************************* # END OF USER VARIABLE SECTION (DO NOT EDIT BEYOND THIS POINT) #************************************************************************* ######### # Flush rules # $IPTABLES -F $IPTABLES -F -t nat $IPTABLES -F -t mangle $IPTABLES -X ########## # Let's setup the firewall according to the Mode selected: bridge or nat # if [ $MODE = "bridge" ] then ######### # Let's clean up the bridge. This will only work if this script # started the bridge. # $BRIDGE delif br0 ${INET_IFACE} 2> /dev/null $BRIDGE delif br0 ${LAN_IFACE} 2> /dev/null $IFCONFIG br0 down 2> /dev/null $BRIDGE delbr br0 2> /dev/null ######### # Let's make sure our interfaces don't have ip information # $IFCONFIG $INET_IFACE 0.0.0.0 up -arp $IFCONFIG $LAN_IFACE 0.0.0.0 up -arp ######### # Let's start the bridge # $BRIDGE addbr br0 $BRIDGE addif br0 ${LAN_IFACE} $BRIDGE addif br0 ${INET_IFACE} # Let's make sure our bridge is not sending out # BPDUs (part of the spanning tree protocol). $BRIDGE stp br0 off if [ "$MANAGE_IFACE" = "br0" ] then $IFCONFIG br0 $MANAGE_IP netmask $MANAGE_NETMASK up else $IFCONFIG br0 0.0.0.0 up -arp fi elif [ $MODE = "nat" ] then i=0 z=1 tempPub=( $PUBLIC_IP ) for host in $HPOT_IP; do # Bring up eth aliases $IFCONFIG $INET_IFACE:${z} ${tempPub[$i]} netmask ${ALIAS_MASK} up # Ensure proper NATing is performed for all honeypots $IPTABLES -t nat -A POSTROUTING -s ${host} -j SNAT --to-source ${tempPub[$i]} $IPTABLES -t nat -A PREROUTING -d ${tempPub[$i]} -j DNAT --to-destination ${host} let "i += 1" let "z += 1" done fi # Let's figure out dns if [ $DNS_HOST -z ] then if [ $MODE = "bridge" ] then DNS_HOST=$PUBLIC_IP else DNS_HOST=$HPOT_IP fi fi ######### # Load all required IPTables modules # ### Needed to initially load modules /sbin/depmod -a ### Add iptables target LOG. $MODPROBE ipt_LOG ### Add iptables QUEUE support (Experimental) if test $QUEUE = "yes" then $MODPROBE ip_queue fi ### Support for connection tracking of FTP and IRC. $MODPROBE ip_conntrack_ftp $MODPROBE ip_conntrack_irc ### Enable ip_forward echo "1" > /proc/sys/net/ipv4/ip_forward ### Create protocol handling chains $IPTABLES -N tcpHandler $IPTABLES -N udpHandler $IPTABLES -N icmpHandler $IPTABLES -N otherHandler # Forward Chain: # Some of these rules may look redundant, but they allow us to catch # 'other' protocols. # Internet -> honeypot - # This logs all inbound new connections and we must # specifically allow all inbound traffic because # the default policy for forwarding traffic # will be drop. This will ensure if something # goes wrong with outbound connections, we # default to drop. # # Also, in case we have something listening to the QUEUE, we # will send all packets via the QUEUE. ### Inbound TCP $IPTABLES -A FORWARD -i $INET_IFACE -p tcp -m state --state NEW -j LOG --log-prefix "INBOUND TCP: " $IPTABLES -A FORWARD -i $INET_IFACE -p tcp -m state --state NEW -j ACCEPT ### Inbound UDP $IPTABLES -A FORWARD -i $INET_IFACE -p udp -m state --state NEW -j LOG --log-prefix "INBOUND UDP: " $IPTABLES -A FORWARD -i $INET_IFACE -p udp -m state --state NEW -j ACCEPT ### Inbound ICMP $IPTABLES -A FORWARD -i $INET_IFACE -p icmp -m state --state NEW -j LOG --log-prefix "INBOUND ICMP: " $IPTABLES -A FORWARD -i $INET_IFACE -p icmp -m state --state NEW -j ACCEPT ### Inbound anything else $IPTABLES -A FORWARD -i $INET_IFACE -m state --state NEW -j LOG --log-prefix "INBOUND OTHER: " $IPTABLES -A FORWARD -i $INET_IFACE -m state --state NEW -j ACCEPT # The remainder of established connections will be ACCEPTED. The rules above # are required in order to log new inbound connections. $IPTABLES -A FORWARD -i $INET_IFACE -j ACCEPT # Okay, this is where the magic all happens. All outbound traffic is counted, # logged, and limited here. Targets (called Handlers) are what actually limit # the connections. All 'Handlers' are defined at the bottom of the script. # Egress filtering, don't want to let our compromised honeypot send spoofed packets. # Stops most outbound DoS attacks. However, we might want to allow our honeypots to # use dhcp to get an ip while in bridge mode. if [ $MODE = "bridge" ] then $IPTABLES -A FORWARD -i $LAN_IFACE -p udp -s $LAN_IP_RANGE --sport 68 -d 255.255.255.255 --dport 67 -j LOG --log-prefix "DHCP OUT REQUEST: " $IPTABLES -A FORWARD -i $LAN_IFACE -p udp -s $LAN_IP_RANGE --sport 68 -d 255.255.255.255 --dport 67 -j ACCEPT fi $IPTABLES -A FORWARD -i $LAN_IFACE -s ! $LAN_IP_RANGE -j LOG --log-prefix "SPOOFED SOURCE: " $IPTABLES -A FORWARD -i $LAN_IFACE -s ! $LAN_IP_RANGE -j DROP ### DNS / NTP Perhaps one of your honeypots needs consistent ### outbound access to provide internal service. for srvr in ${DNS_SVRS}; do for host in ${DNS_HOST}; do $IPTABLES -A FORWARD -p udp -i $LAN_IFACE -s ${host} -d ${srvr} --dport 53 -j LOG --log-prefix "Legal DNS: " $IPTABLES -A FORWARD -p udp -i $LAN_IFACE -s ${host} -d ${srvr} --dport 53 -j ACCEPT done done # Since this is a bridge, we want to allow broadcast. By default, we allow all # inbound traffic (including broadcast). We also want to allow outbound broadcast # (such as NetBIOS) but we do not want to count it as an outbound session. So # we allow it here *before* we begin counting outbound connections $IPTABLES -A FORWARD -i $LAN_IFACE -d ${LAN_BCAST_ADRESS} -j LOG --log-prefix "Legal Broadcast: " $IPTABLES -A FORWARD -i $LAN_IFACE -d ${LAN_BCAST_ADRESS} -j ACCEPT $IPTABLES -A FORWARD -i $LAN_IFACE -d 255.255.255.255 -j LOG --log-prefix "Legal Broadcast: " $IPTABLES -A FORWARD -i $LAN_IFACE -d 255.255.255.255 -j ACCEPT if [ $MODE = "nat" ] then LIMIT_IP=$HPOT_IP elif [ $MODE = "bridge" ] then LIMIT_IP=$PUBLIC_IP fi ### Count and limit all other outbound connections for host in ${LIMIT_IP}; do # TCP # This next rule is the connection limiter. If it has not exceeded # the limit, the packet will be sent to the tcpHandler. The # tcpHandler will log and either QUEUE or ACCEPT depending on # the Architecture selected. # # NOTE: The purpose of the drop rule is to ensure we can catch 'other' # protocols that enter our network. If this statement is not here # we will get false log entries stating Drop other after xxx # connections. $IPTABLES -A FORWARD -p tcp -i $LAN_IFACE -m state --state NEW -m limit --limit ${TCPRATE}/${SCALE} --limit-burst ${TCPRATE} -s ${host} -j tcpHandler $IPTABLES -A FORWARD -p tcp -i $LAN_IFACE -m state --state NEW -m limit --limit 1/${SCALE} --limit-burst 1 -s ${host} -j LOG --log-prefix "Drop TCP after ${TCPRATE} connections " $IPTABLES -A FORWARD -p tcp -i $LAN_IFACE -m state --state NEW -s ${host} -j DROP # # UDP - see TCP comments above. # $IPTABLES -A FORWARD -p udp -i $LAN_IFACE -m state --state NEW -m limit --limit ${UDPRATE}/${SCALE} --limit-burst ${UDPRATE} -s ${host} -j udpHandler $IPTABLES -A FORWARD -p udp -i $LAN_IFACE -m state --state NEW -m limit --limit 1/${SCALE} --limit-burst 1 -s ${host} -j LOG --log-prefix "Drop udp after ${UDPRATE} attempts " $IPTABLES -A FORWARD -p udp -i $LAN_IFACE -m state --state NEW -s ${host} -j DROP # # ICMP - see TCP comments above. # $IPTABLES -A FORWARD -p icmp -i $LAN_IFACE -m state --state NEW -m limit --limit ${ICMPRATE}/${SCALE} --limit-burst ${ICMPRATE} -s ${host} -j icmpHandler $IPTABLES -A FORWARD -p icmp -i $LAN_IFACE -m state --state NEW -m limit --limit 1/${SCALE} --limit-burst 1 -s ${host} -j LOG --log-prefix "Drop icmp after ${ICMPRATE} attempts " $IPTABLES -A FORWARD -p icmp -i $LAN_IFACE -m state --state NEW -s ${host} -j DROP # # EVERYTHING ELSE - see TCP comments above. # $IPTABLES -A FORWARD -i $LAN_IFACE -m state --state NEW -m limit --limit ${OTHERRATE}/${SCALE} --limit-burst ${OTHERRATE} -s ${host} -j otherHandler $IPTABLES -A FORWARD -i $LAN_IFACE -m state --state NEW -m limit --limit 1/${SCALE} --limit-burst 1 -s ${host} -j LOG --log-prefix "Drop other after ${OTHERRATE} attempts " done # This portion of the script will ensure that established or related # connections that were allowed, continue to work. If these lines # are not here, only the first packet of each connection that hasn't # reached the limit will be allowed in because we are dropping # all outbound connections by default. if test $QUEUE = "yes" then $IPTABLES -A FORWARD -i $LAN_IFACE -m state --state RELATED,ESTABLISHED -j QUEUE fi $IPTABLES -A FORWARD -i $LAN_IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT ### These define the handlers that actually limit outbound connection. # # tcpHandler - The only packets that should make it into these chains are new # connections, as long as the host has not exceeded their limit. # $IPTABLES -A tcpHandler -j LOG --log-prefix "OUTBOUND CONN TCP: " if test $QUEUE = "yes" then $IPTABLES -A tcpHandler -j QUEUE fi $IPTABLES -A tcpHandler -j ACCEPT # # udpHandler - see tcpHandler comments above. # $IPTABLES -A udpHandler -j LOG --log-prefix "OUTBOUND CONN UDP: " if test $QUEUE = "yes" then $IPTABLES -A udpHandler -j QUEUE fi $IPTABLES -A udpHandler -j ACCEPT # # icmpHandler - see tcpHandler comments above. # $IPTABLES -A icmpHandler -j LOG --log-prefix "OUTBOUND CONN ICMP: " if test $QUEUE = "yes" then $IPTABLES -A icmpHandler -j QUEUE fi $IPTABLES -A icmpHandler -j ACCEPT # # otherHandler - see tcpHandler comments above. # $IPTABLES -A otherHandler -j LOG --log-prefix "OUTBOUND CONN OTHER: " if test $QUEUE = "yes" then $IPTABLES -A otherHandler -j QUEUE fi $IPTABLES -A otherHandler -j ACCEPT $IPTABLES -A INPUT -i $INET_IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT $IPTABLES -A INPUT -i $LAN_IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT ### Lets make sure our firewall can talk to itself $IPTABLES -A INPUT -i lo -j ACCEPT ############################## # MANAGEMENT INTERFACE RULES # ############################## if [ $MANAGE_IFACE != "none" ] then for ports in $ALLOWED_TCP_IN; do if [ "$MANAGER" = "any" ] then $IPTABLES -A INPUT -i $MANAGE_IFACE -p tcp --dport $ports -m state --state NEW -j LOG --log-prefix "MANAGE port:$ports=>" $IPTABLES -A INPUT -i $MANAGE_IFACE -p tcp --dport $ports -m state --state NEW -j ACCEPT $IPTABLES -A INPUT -i $MANAGE_IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT else for ips in $MANAGER; do $IPTABLES -A INPUT -i $MANAGE_IFACE -p tcp -s $ips --dport $ports -m state --state NEW -j LOG --log-prefix "MANAGE port:$ports=>" $IPTABLES -A INPUT -i $MANAGE_IFACE -p tcp -s $ips --dport $ports -m state --state NEW -j ACCEPT $IPTABLES -A INPUT -i $MANAGE_IFACE -s $ips -m state --state RELATED,ESTABLISHED -j ACCEPT done fi done fi ### Set default policies for the INPUT, FORWARD and OUTPUT chains # By default, drop all connections sent to firewall $IPTABLES -P INPUT DROP # If we selected to restrict the firewall, lets implement it here. if [ $RESTRICT = "yes" ] then for port in $ALLOWED_TCP_OUT; do $IPTABLES -A OUTPUT -p tcp --dport $port -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT done for port in $ALLOWED_UDP_OUT; do $IPTABLES -A OUTPUT -p udp --dport $port -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT done # By default, drop firewall outbound connection $IPTABLES -P OUTPUT DROP else # By default, accept firewall outbound connection $IPTABLES -P OUTPUT ACCEPT fi # By default, if FORWARDED connections are not within limit, DROP. # This is a fail close policy, and more secure. $IPTABLES -P FORWARD DROP