#!/bin/bash ###################################################### # Ubuntu-firewall by Robert C. Pectol # # Version: 0.9 # # Updated: 14 August 2007 # # # # The freshest version of Ubuntu-firewall # # can be downloaded here: # # http://rob.pectol.com/ubuntu-firewall.tgz # ###################################################### # Load LSB init-functions if [ -r /lib/lsb/init-functions ]; then . /lib/lsb/init-functions logbegin="log_begin_msg" logsuccess="log_success_msg" logfail="log_failure_msg" logend="log_end_msg" else logbegin="echo" logsuccess="echo" logfail="echo" logend="echo" fi # Read the firewall configuration file (Custom config file may be specified as the second argument.) if [[ "$2" != "" && "$1" != "stop" && "$1" != "ver" ]]; then echo "Custom configuration file specified." config=$2 . $config &>/dev/null if [ "$DISABLED" == "" ]; then $logfail "Configuration file specified is not complete! Exiting..." exit 1 fi else if [[ "$1" != "stop" && "$1" != "ver" ]]; then if test -f /etc/default/ubuntu-firewall-cfg; then . /etc/default/ubuntu-firewall-cfg else $logfail "Configuration file not specified and the default was not found! Exiting..." exit 1 fi fi fi # Check to see if Ubuntu-firewall is disabled in the config file if [[ "$DISABLED" == "yes" && "$1" != "stop" && "$1" != "status" && "$1" != "ver" ]]; then $logfail "Ubuntu-firewall has been DISABLED in the config file! Exiting..." exit 1 fi # Check to see that we are ROOT! exec_user=`/usr/bin/whoami` if [ "$exec_user" != "root" ] ; then $logfail "Ubuntu-firewall must be executed with root privileges (ex: sudo ubuntu-firewall)!" exit 1 fi # Locate the iptables tool IPT=`which iptables` if [ "$IPT" == "" ]; then $logfail "Error locating the iptables tool! Exiting..." exit 1 fi # Locate modprobe MODPROBE=`which modprobe` # Locate ifconfig IFCONFIG=`which ifconfig` # Function to load the firewall policies and rules start () { # Is this an Ubuntu System? OS_FILE_LOC="`ls /etc | grep release`" OS_INFO="`cat /etc/$OS_FILE_LOC`" IS_UBUNTU="`echo $OS_INFO | egrep 'Debian|Ubuntu'`" if [ "$IS_UBUNTU" == "" ]; then if [ "$VERBOSE" == "yes" ]; then echo -e "\E[1;33mNon-Ubuntu system detected! Safe exit mode will be enabled. Continuing to load...\E[32;0m" fi non_ubuntu="yes" set -e fi # Get the external interface and its IP address if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mDetecting external interface IP address...\E[32;0m" fi if [ "$EXTIF" == "auto" ]; then pass=0 if [ "$IF_BLACKLIST" != "" ]; then IF_BLACKLIST=`echo $IF_BLACKLIST | awk '{$1=$1;print}' | sed 's/ /|/g'` EXTIF=`$IFCONFIG | egrep -v '^lo' | egrep -v "$IF_BLACKLIST" | cut -d ' ' -f1 \ | awk NF | head -n 1 | sed 's/^[ \t]*//;s/[ \t]*$//'` if [ "$EXTIF" == "" ]; then $logfail "Interface auto-detection failed! Please manually assign it in the config file!" exit 1 fi if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mTrying interface $EXTIF...\E[32;0m" fi EXTIP=`$IFCONFIG $EXTIF 2>/dev/null | egrep 'inet ad|inet Ad' | cut -d ':' -f2 \ | cut -d ' ' -f1 | sed 's/^[ \t]*//;s/[ \t]*$//'` while [[ "$pass" -lt "10" && "$EXTIP" == "" ]]; do EXTIF=`$IFCONFIG | egrep -v '^lo' | egrep -v "$IF_BLACKLIST|$EXTIF" | cut -d ' ' -f1 \ | awk NF | head -n 1 | sed 's/^[ \t]*//;s/[ \t]*$//'` if [ "$EXTIF" != "" ]; then if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mTrying interface $EXTIF...\E[32;0m" fi EXTIP=`$IFCONFIG $EXTIF 2>/dev/null | egrep 'inet ad|inet Ad' | cut -d ':' -f2 \ | cut -d ' ' -f1 | sed 's/^[ \t]*//;s/[ \t]*$//'` else EXTIF="null" fi sleep .5 pass=$(($pass + 1)) done else EXTIF=`$IFCONFIG | egrep -v '^lo' | cut -d ' ' -f1 | awk NF \ | head -n 1 | sed 's/^[ \t]*//;s/[ \t]*$//'` if [ "$EXTIF" == "" ]; then $logfail "Interface auto-detection failed! Please manually assign it in the config file!" exit 1 fi if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mTrying interface $EXTIF...\E[32;0m" fi EXTIP=`$IFCONFIG $EXTIF 2>/dev/null | egrep 'inet ad|inet Ad' | cut -d ':' -f2 \ | cut -d ' ' -f1 | sed 's/^[ \t]*//;s/[ \t]*$//'` while [[ "$pass" -lt "10" && "$EXTIP" == "" ]]; do EXTIF=`$IFCONFIG | egrep -v '^lo' | egrep -v "$EXTIF" | cut -d ' ' -f1 \ | awk NF | head -n 1 | sed 's/^[ \t]*//;s/[ \t]*$//'` if [ "$EXTIF" != "" ]; then if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mTrying interface $EXTIF...\E[32;0m" fi EXTIP=`$IFCONFIG $EXTIF 2>/dev/null | egrep 'inet ad|inet Ad' | cut -d ':' -f2 \ | cut -d ' ' -f1 | sed 's/^[ \t]*//;s/[ \t]*$//'` else EXTIF="null" fi sleep .5 pass=$(($pass + 1)) done fi if [ "$EXTIP" == "" ]; then $logfail "Couldn't determine external IP address on $EXTIF! Exiting..." exit 1 fi if [ "$VERBOSE" == "yes" ]; then echo -e "\E[1;33m* $EXTIF * \E[32;0m\E[32;40mauto-selected as external interface...\E[32;0m" fi else EXTIP=`$IFCONFIG $EXTIF 2>/dev/null | egrep 'inet ad|inet Ad' | cut -d ':' -f2 \ | cut -d ' ' -f1 | sed 's/^[ \t]*//;s/[ \t]*$//'` if [ "$EXTIP" == "" ]; then $logfail "Couldn't determine external IP address on $EXTIF! Exiting..." exit 1 fi fi NETWORK_BCAST=`$IFCONFIG $EXTIF 2>/dev/null | egrep 'inet ad|inet Ad' | cut -d ':' -f3 \ | cut -d ' ' -f1 | sed 's/^[ \t]*//;s/[ \t]*$//'` if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mExternal interface IP configuration looks good! Loading ruleset...\E[32;0m" fi # Load some connection-tracking modules # FTP $MODPROBE ip_conntrack_ftp # IRC $MODPROBE ip_conntrack_irc # Set some script variables LOOPIF="lo" # Loopback interface LOOPIP="127.0.0.0/8" # Loopback IP address range CLASS_D_MULTI="224.0.0.0/4" # Class D Mulitcast address range BCAST_SRC="0.0.0.0" # Broadcast source address BCAST_DST="255.255.255.255" # Broadcast destination address MSNETWORKING="137:139" # Micro$oft Networking port range # Remove any existing rules from chains, clear their counters, and set default policies $IPT -F $IPT -X $IPT --zero $IPT --flush FORWARD $IPT -t nat --flush if [ "$NAT_ONLY" != "yes" ]; then $IPT --policy INPUT DROP $IPT --policy OUTPUT DROP $IPT --policy FORWARD DROP else $IPT --policy INPUT ACCEPT $IPT --policy OUTPUT ACCEPT $IPT --policy FORWARD DROP if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mNAT-only configuration selected!\E[32;0m" fi fi ######## DROPPED ######## if [ "$NAT_ONLY" != "yes" ]; then # Drop spoofed packets $IPT -A INPUT -i $EXTIF -s $LOOPIP -j DROP # Drop malformed broadcast packets $IPT -A INPUT -i $EXTIF -s $BCAST_DST -j DROP $IPT -A INPUT -i $EXTIF -d $BCAST_SRC -j DROP # Drop Class D multicast traffic which isn't carrying the UDP protocol $IPT -A INPUT -i $EXTIF -p ! udp -d $CLASS_D_MULTI -j DROP # Drop fragmented ICMP packets (these should never be fragmented, really!) $IPT -A INPUT --fragment -p icmp -d $EXTIP -j DROP ######## ACCEPTED ######## # Allow loopback interface traffic $IPT -A INPUT -i $LOOPIF -j ACCEPT $IPT -A OUTPUT -o $LOOPIF -j ACCEPT # Allow network broadcast traffic in $IPT -A INPUT -i $EXTIF -d $NETWORK_BCAST -j ACCEPT $IPT -A INPUT -i $EXTIF -d $BCAST_DST -j ACCEPT # Allow some ICMP message types $IPT -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT $IPT -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT # If you have an internal "trusted" network interface specified, then allow traffic on it if [ "$INTIF" != "" ]; then if [ "$INTIF" == "$EXTIF" ]; then if [ "$VERBOSE" == "yes" ]; then echo -e "\E[31;40mINTIF and EXTIF can NOT be the same interface! Ignoring INTIF and continuing...\E[32;0m" fi INTIF="" fi fi if [ "$INTIF" != "" ]; then INTIF_UP=`$IFCONFIG | grep "$INTIF"` if [ "$INTIF_UP" == "" ]; then if [ "$VERBOSE" == "yes" ]; then echo -e "\E[31;40mInternal interface $INTIF seems to be down! Ignoring INTIF and continuing...\E[32;0m" fi else $IPT -A INPUT -i $INTIF -j ACCEPT $IPT -A OUTPUT -o $INTIF -j ACCEPT fi fi # Connection-tracking $IPT -A OUTPUT -o $EXTIF -s $EXTIP -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT $IPT -A INPUT -i $EXTIF -d $EXTIP -m state --state ESTABLISHED,RELATED -j ACCEPT # System response to PINGs if [ "$ALLOW_PINGS" == "yes" ]; then $IPT -A INPUT -i $EXTIF -p icmp --icmp-type echo-request -d $EXTIP -m limit --limit 60/minute --limit-burst 3 -j ACCEPT if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mSystem will respond to ping (ICMP echo-request) traffic on external interface.\E[32;0m" fi fi ### Custom Rules ### if [ "$CUSTOM_RULES" != "" ]; then set -e if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mCustom rules were found and are being applied...\E[32;0m" fi . $CUSTOM_RULES set +e fi ### Server Services to the 'Net ### if [ "$VERBOSE" == "yes" ]; then if [[ "$OPEN_TCP_PORTS" != "" || "$OPEN_UDP_PORTS" != "" || "$ALLOW_FTP" == "yes" || "$ALLOW_MSNETWORKING" == "yes" ]]; then echo -e "\E[32;40mLoading ruleset for system servers...\E[32;0m" fi fi if [ "$OPEN_TCP_PORTS" != "" ]; then for i in $OPEN_TCP_PORTS; do $IPT -A INPUT -i $EXTIF -p tcp --syn -d $EXTIP --dport $i -m state --state NEW -j ACCEPT if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mTCP port $i is now accessible.\E[32;0m" fi done fi if [ "$OPEN_UDP_PORTS" != "" ]; then for i in $OPEN_UDP_PORTS; do $IPT -A INPUT -i $EXTIF -p udp -d $EXTIP --dport $i -m state --state NEW -j ACCEPT if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mUDP port $i is now accessible.\E[32;0m" fi done fi # FTP Server if [ "$ALLOW_FTP" == "yes" ]; then # control connections $IPT -A INPUT -i $EXTIF -p tcp --syn -d $EXTIP --dport 21 -m state --state NEW -j ACCEPT # port mode FTP (pasv mode is too risky to the server so it's not allowed) $IPT -A INPUT -i $EXTIF -p tcp ! --syn -d $EXTIP --dport 20 -m state --state NEW -j ACCEPT if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mYour FTP server is now accessible.\E[32;0m" fi fi # Micro$oft Networking on external interface if [ "$ALLOW_MSNETWORKING" == "yes" ]; then $IPT -A INPUT -i $EXTIF -p tcp -d $EXTIP --dport $MSNETWORKING -m state --state NEW -j ACCEPT $IPT -A INPUT -i $EXTIF -p udp -d $EXTIP --dport $MSNETWORKING -m state --state NEW -j ACCEPT $IPT -A INPUT -i $EXTIF -p tcp -d $EXTIP --dport 445 -m state --state NEW -j ACCEPT $IPT -A INPUT -i $EXTIF -p udp --sport $MSNETWORKING -d $EXTIP -m state --state NEW -j ACCEPT $IPT -A INPUT -i $EXTIF -p tcp --sport 445 -d $EXTIP -m state --state NEW -j ACCEPT if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mMicrosoft Networking is now accessible.\E[32;0m" fi fi # If we are NOT running an Ident server, then reject auth requests instead of dropping them - avoids long timeouts! auth_open=`echo $OPEN_TCP_PORTS | grep 113` || true if [ "$auth_open" == "" ]; then $IPT -A INPUT -p tcp -d $EXTIP --dport 113 -j REJECT --reject-with tcp-reset fi fi ###### ADVANCED FEATURES ###### # NAT Routing - Enables this machine to provide Internet Services to other computers via Network Address Translation if [ "$NAT_IF" != "" ]; then if [ "$NAT_IF" == "$EXTIF" ]; then if [ "$VERBOSE" == "yes" ]; then echo -e "\E[31;40mNAT_IF cannot be the same as EXTIF! NAT services are disabled!\E[32;0m" fi NAT_IF="" else INTIP=`$IFCONFIG $NAT_IF | egrep 'inet ad|inet Ad' | cut -d ':' -f2 | cut -d ' ' -f1 | sed 's/^[ \t]*//;s/[ \t]*$//'` INTIPERROR=`echo $INTIP | grep error` INTNET="`echo $INTIP | cut -d . -f 1,2,3`.0/24" NAT_IF_UP=`$IFCONFIG | grep "$NAT_IF"` if [[ "$INTIP" == "" || "$INTIPERROR" != "" || "$NAT_IF_UP" == "" ]]; then if [ "$VERBOSE" == "yes" ]; then echo -e "\E[31;40mThe NAT interface does NOT appear to be up! NAT services are disabled!\E[32;0m" fi NAT_IF="" fi fi fi if [ "$NAT_IF" != "" ]; then echo 1 > /proc/sys/net/ipv4/ip_forward # Load modules $MODPROBE ip_nat_ftp $MODPROBE ip_nat_irc $IPT -A INPUT -i $NAT_IF -j ACCEPT $IPT -A OUTPUT -o $NAT_IF -j ACCEPT # Blindly forwarding all traffic is dangerous! This restricts the forwarding # policy to only our valid internal-to-external NAT sessions. $IPT -t nat -A POSTROUTING --out-interface $EXTIF -j SNAT --to-source $EXTIP $IPT -A FORWARD -i $NAT_IF -o $EXTIF -s $INTNET -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT $IPT -A FORWARD -i $EXTIF -o $NAT_IF -d $INTNET -m state --state ESTABLISHED,RELATED -j ACCEPT if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mNAT Routing Functions are enabled for $INTNET on $NAT_IF!\E[32;0m" fi # Forwarding to internal host(s) if [ "$config" != "" ]; then fhostnum=`cat "$config" | egrep '^FORWARD_HOST.=' | cut -d '=' -f1` else fhostnum=`cat /etc/default/ubuntu-firewall-cfg | egrep '^FORWARD_HOST.=' | cut -d '=' -f1` fi if [ "$fhostnum" != "" ]; then for fh in $fhostnum; do x=`eval echo '$'${fh}_TCP` for i in $x; do z=`eval echo '$'$fh` $IPT -t nat -A PREROUTING -p tcp -d $EXTIP --dport $i -j DNAT --to-destination $z $IPT -A FORWARD -i $EXTIF -o $NAT_IF -p tcp -d $z --dport $i -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mTCP Port $i is now forwarded to $z.\E[32;0m" fi done y=`eval echo '$'${fh}_UDP` for i in $y; do z=`eval echo '$'$fh` $IPT -t nat -A PREROUTING -p udp -d $EXTIP --dport $i -j DNAT --to-destination $z $IPT -A FORWARD -i $EXTIF -o $NAT_IF -p udp -d $z --dport $i -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mUDP Port $i is now forwarded to $z.\E[32;0m" fi done done fi if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mPort Forwarding Established!\E[32;0m" fi else echo 0 > /proc/sys/net/ipv4/ip_forward fi # Logging if [ "$LOG_PACKETS" == "yes" ]; then $IPT -A INPUT -j LOG --log-prefix "Fell off input chain: " $IPT -A OUTPUT -j LOG --log-prefix "Fell off output chain: " if [ "$NAT_IF" != "" ]; then $IPT -A FORWARD -j LOG --log-prefix "Fell off forward chain: " fi # Log packets with impossible addresses for f in /proc/sys/net/ipv4/conf/*/log_martians; do echo 1 > $f done if [ "$VERBOSE" == "yes" ]; then echo -e "\E[32;40mPacket logging is active.\E[32;0m" fi else for f in /proc/sys/net/ipv4/conf/*/log_martians; do echo 0 > $f done fi if [ "$non_ubuntu" == "yes" ]; then if [ "$VERBOSE" == "yes" ]; then echo -e "\E[1;33mNon-Ubuntu system detected but script executed ok! However, check the output above, for any errors.\E[32;0m" fi fi touch /var/run/ubuntu-firewall } # Function to stop the firewall (sets default policies back to ACCEPT and removes all rules) stop () { $IPT -F $IPT -t nat --flush $IPT --zero $IPT --policy INPUT ACCEPT $IPT --policy OUTPUT ACCEPT $IPT --policy FORWARD ACCEPT if [ "$NAT_IF" != "" ]; then echo 0 > /proc/sys/net/ipv4/ip_forward fi rm -f /var/run/ubuntu-firewall } # Function to lock the system down (no traffic will flow at all) lockdown () { $IPT -F $IPT --policy INPUT DROP $IPT --policy OUTPUT DROP $IPT --policy FORWARD DROP touch /var/run/ubuntu-firewall $logsuccess "Ubuntu-firewall is LOCKED DOWN. No data will flow!" } # Function to get status and list the firewall policies/rules status () { clear if ! test -r /var/run/ubuntu-firewall; then $logsuccess "Ubuntu-firewall is NOT active!" sleep 1 echo echo ">>>>>>>>>> Filter table: <<<<<<<<<<" echo $IPT -t filter -L -n echo else $logsuccess "Ubuntu-firewall is active!" sleep 1 echo echo ">>>>>>>>>> Filter table: <<<<<<<<<<" echo $IPT -t filter -L -n -v echo if [ "$NAT_IF" != "" ]; then echo echo ">>>>>>>>>> NAT table: <<<<<<<<<<" echo $IPT -t nat -L -n -v echo fi fi } ver () { version=`head $0 | grep Version | cut -d ':' -f2 | cut -d ' ' -f3 | sed 's/^[ \t]*//;s/[ \t]*$//'` echo -e "\E[32;40mUbuntu-firewall version: $version\E[32;0m" } # Call the start, stop, reload, status, lockdown, and ver functions case "$1" in start) $logbegin "Starting Ubuntu-firewall..." if ! test -r /var/run/ubuntu-firewall; then start else $logfail "Already started!" fi $logend $? ;; stop) $logbegin "Stopping Ubuntu-firewall..." if test -r /var/run/ubuntu-firewall; then stop else $logfail "Already stopped!" fi $logend $? ;; reload) $logbegin "Reloading Ubuntu-firewall..." stop sleep 1 start $logend $? ;; status) status ;; lockdown) lockdown ;; ver) ver ;; *) echo -e "\E[31;40mUsage: $0 {start|stop|reload|status|lockdown|ver}\E[32;0m" exit 1 ;; esac exit 0