#!/bin/sh # # Copyright 2003 Honeynet Project # License BSD http://www.opensource.org/licenses/bsd-license.php # # This is an improved version of the rc.firewall script v0.8 # found in Honeywall CDROM from http://www.honeynet.org/tools/cdrom # that supports the new LAN_BLOCK option. On the other hand, the # handlers' section has been simplified. # # Spanish Honeynet Project # July, 2004 # PATH="/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin" . /etc/default/honeywall.conf start () { lsmod | grep ipchain IPCHAINS=$? if [ "$IPCHAINS" = 0 ]; then echo "" echo "Dooh, IPChains is currently running! IPTables is required by" echo "the rc.firewall script. IPChains will be unloaded to allow" echo "IPTables to run. It is recommened that you permanently" echo "disable IPChains in the /etc/rc.d startup scripts and enable" echo "IPTables instead." ipchains -F rmmod ipchains fi ######### # Flush rules # iptables -F iptables -F -t nat iptables -F -t mangle iptables -X echo "" ########## # Let's setup the firewall according to the Mode selected: bridge or nat # if [ ${MODE} = "nat" ]; then echo "Starting up Routing mode and enabling Network Address Translation." #Let's bring up our internal interface ifconfig ${LAN_IFACE} ${PRIV_IP} netmask ${LAN_BCAST_ADDRESS} up i=0 z=1 tempPub=( ${PUBLIC_IP} ) for host in ${HPOT_IP}; do if [ ${i} = "0" ]; then #This is the first honeypot. Let's attach it to our nic ifconfig ${INET_IFACE} ${tempPub[$i]} netmask ${ALIAS_MASK} up else # Bring up eth aliases ifconfig ${INET_IFACE}:${z} ${tempPub[$i]} netmask ${ALIAS_MASK} up let "z += 1" fi # Ensure proper NATing is performed for all honeypots iptables -t nat -A POSTROUTING -o ${INET_IFACE} -s ${host} \ -j SNAT --to-source ${tempPub[$i]} iptables -t nat -A PREROUTING -i ${INET_IFACE} -d ${tempPub[$i]} \ -j DNAT --to-destination ${host} let "i += 1" done fi # Let's figure out dns if [ -z "${DNS_HOST}" ]; 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 [ ${QUEUE} = "yes" ]; then # Insert kernel mod modprobe ip_queue # check to see if it worked, if not exit with error lsmod | grep ip_queue &>/dev/null IPQUEUE=$? if [ "$IPQUEUE" = 1 ]; then echo "" echo "It appears you do not have the ip_queue kernel module compiled" echo "for your kernel. This module is required for Snort-Inline and" echo "QUEUE capabilities. You either have to disable QUEUE, or compile" echo "the ip_queue kernel module for your kernel. This module is part" echo "of the kernel source." exit fi echo "Enabling Snort-Inline capabilities, make sure Snort-Inline is" echo "running in -Q mode, or all outbound traffic will be blocked" 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. # 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_ADDRESS} -j LOG \ #--log-prefix "Legal Broadcast: " iptables -A FORWARD -d ${LAN_BCAST_ADDRESS} -j ACCEPT #iptables -A FORWARD -i ${LAN_IFACE} -d 255.255.255.255 -j LOG \ #--log-prefix "Legal Broadcast: " iptables -A FORWARD -d 255.255.255.255 -j ACCEPT ### 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 --sport 68 \ -d 255.255.255.255 --dport 67 -j LOG \ --log-prefix "DHCP OUT REQUEST: " iptables -A FORWARD -i ${LAN_IFACE} -p udp --sport 68 \ -d 255.255.255.255 --dport 67 -j ACCEPT fi # This rule is for use with sebek. If sebek is used, and we don't want # the logs filled by SPOOFED SOURCE entries because sebek uses spoofed # IPs, we should drop all traffic in the sebek ip range. if [ ${SEBEK} = "yes" ]; then if [ ${SEBEK_LOG} = "yes" ]; then iptables -A FORWARD -i ${LAN_IFACE} -p udp -d ${SEBEK_DST_IP} \ --dport ${SEBEK_DST_PORT} -j LOG --log-prefix "SEBEK" fi iptables -A FORWARD -i ${LAN_IFACE} -p udp -d ${SEBEK_DST_IP} \ --dport ${SEBEK_DST_PORT} -j ${SEBEK_FATE} fi ### DNS / NTP Perhaps one of your honeypots needs consistent ### outbound access to provide internal service. # If we did not identify a specific destination dns server, let's go ahead # and allow any. if [ -z "${DNS_SVRS}" ]; then DNS_SVRS="0.0.0.0/0" fi 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 tcp -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 iptables -A FORWARD -p tcp -i ${LAN_IFACE} -s ${host} -d ${srvr} \ --dport 53 -j ACCEPT done done ### Count and limit all other outbound connections # This will ensure we don't restrict Honeypots talking to eachother, and # we don't log them as outbound connections (in bridge mode, the # firewall sees all packets; therefore, we have to make sure it doesn't # log packets incorrectly and give false positives). # If you do not want to see this log, comment out the logging rule. # You will still need the ACCEPT rule to ensure they honeypots can talk # to eachother freely. iptables -A FORWARD -i ${LAN_IFACE} -o ${LAN_IFACE} -j LOG \ --log-prefix "Honeypot -> Honeypot: " iptables -A FORWARD -i ${LAN_IFACE} -o ${LAN_IFACE} -j ACCEPT # LAN Protect/Blocking denies access to the LAN IP addresses not included # in the LAN_ALLOWED_IP variable. # If we activated this feature, allow access to the sepecified IP addresses # and finally block the access to the rest of the LAN IP address space if [ ${LAN_BLOCK} = "yes" ]; then for host in ${LAN_ALLOWED_IP}; do iptables -A FORWARD -i ${LAN_IFACE} -d ${host} -j ACCEPT done iptables -A FORWARD -i ${LAN_IFACE} -d ${LAN_IP_RANGE} -j DROP fi if [ ${LAN_BLOCK} = "yes" ]; then for host in ${LAN_ALLOWED_IP}; do iptables -A FORWARD -i ${INET_IFACE} -s ${host} -j ACCEPT done iptables -A FORWARD -i ${INET_IFACE} -s ${LAN_IP_RANGE} -j DROP fi if [ ${MODE} = "nat" ]; then LIMIT_IP="${HPOT_IP}" elif [ ${MODE} = "bridge" ]; then LIMIT_IP="${PUBLIC_IP}" fi 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} attempts" iptables -A FORWARD -p tcp -i ${LAN_IFACE} -m state --state NEW \ -s ${host} -j DROP # This rule is for Mike Clark in order to give him RELATED information. For # example, this will tell him the data channel related to an ftp command # channel of a connection. iptables -A FORWARD -p tcp -i ${LAN_IFACE} -m state --state RELATED \ -s ${host} -j tcpHandler # # 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 [ "${QUEUE}" = "yes" ]; then TARGET=QUEUE else TARGET=ACCEPT fi iptables -A FORWARD -i ${LAN_IFACE} -m state --state RELATED,ESTABLISHED \ -j ${TARGET} ### 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 TCP: " iptables -A tcpHandler -j ${TARGET} # # udpHandler - see tcpHandler comments above. # iptables -A udpHandler -j LOG --log-prefix "OUTBOUND UDP: " iptables -A udpHandler -j ${TARGET} # # icmpHandler - see tcpHandler comments above. # iptables -A icmpHandler -j LOG --log-prefix "OUTBOUND ICMP: " iptables -A icmpHandler -j ${TARGET} # # otherHandler - see tcpHandler comments above. # iptables -A otherHandler -j LOG --log-prefix "OUTBOUND OTHER: " iptables -A otherHandler -j ${TARGET} iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT ### Lets make sure our firewall can talk to itself iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o 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 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 done fi done iptables -A OUTPUT -o ${MANAGE_IFACE} -p tcp -m state \ --state RELATED,ESTABLISHED -j ACCEPT 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 } stop () { echo "Stopping Firewall." ######### # Flush rules # iptables -F iptables -F -t nat iptables -F -t mangle iptables -X # Set default forward to drop iptables -P FORWARD DROP iptables -P INPUT DROP iptables -P OUTPUT DROP # Allow the firewall to talk to itself iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT if [ -n ${MANAGE_IFACE} ]; then iptables -A INPUT -i ${MANAGE_IFACE} -j ACCEPT iptables -A OUTPUT -o ${MANAGE_IFACE} -j ACCEPT fi } initial () { ######### # Flush rules # iptables -F iptables -F -t nat iptables -F -t mangle iptables -X # Set default forward to drop iptables -P FORWARD DROP iptables -P INPUT DROP iptables -P OUTPUT DROP # Allow the firewall to talk to itself iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT } restart () { stop start &>/dev/null } case "$1" in start) start ;; stop) stop ;; restart) restart ;; initial) initial ;; *) echo $"Usage: $0 {start|stop|restart|initial)" exit 1 esac