#!/bin/sh

### BEGIN INIT INFO
# Provides:          dtc-xen-firewall
# Required-Start:    $all
# Required-Stop:
# Should-Start:      $local_fs
# Should-Stop:       $local_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: A small firewall script for your dom0
# Description:       If running in a production environment, you might want
#                    to have a basic firewall running on your dom0 to avoid
#                    having DoS attack. This is not the state-of-the-art, but
#                    just another attempt to make things a bit more smooth.
### END INIT INFO

# To maintainers of this script:
# NEVER a dtc-xen-fw should have an ACCEPT statement, as this would disable the
# Xen anti-spoof rules. Instead, use RETURN to exit the chain.

IPTABLES=/sbin/iptables

if [ -f /etc/dtc-xen/dtc-xen-firewall.sh ] ; then
	. /etc/dtc-xen/dtc-xen-firewall.sh
fi

if [ -e /etc/dtc-xen/dtc-xen-firewall-config ] ; then
	. /etc/dtc-xen/dtc-xen-firewall-config
fi

if [ -z ${soap_server_allowed_ip} ] ; then
	soap_server_allowed_ip=0.0.0.0
fi

flush_input_chain () {
	${IPTABLES} -F dtc-xen-input
}

create_dtc_xen_forward_chain () {
	# Create the chain (if it doesn't exists, then it should be inserted in the INPUT or FORWARD chain)
	if ${IPTABLES} --new-chain dtc-xen-in ; then
		${IPTABLES} -I INPUT -j dtc-xen-in
	fi
	if ${IPTABLES} --new-chain dtc-xen-fw ; then
		${IPTABLES} -I FORWARD -j dtc-xen-fw
	fi
	# If the chains already existed, flush them
	${IPTABLES} -F dtc-xen-fw
	${IPTABLES} -F dtc-xen-in
}

accept_localhost_traffic () {
	${IPTABLES} -A dtc-xen-in -i lo -j ACCEPT
}

soap_server_limit () {
	# Allow only our management server to connect
	if ! [ ${soap_server_allowed_ip} = "0.0.0.0" ] ; then
		${IPTABLES} -A dtc-xen-in -p tcp --dport 8089 -s ! ${soap_server_allowed_ip} -j LOGREJECT
	fi
	if [ -z "${SOAP_ACCEPTING_RATE}" ] ; then
		SOAP_ACCEPTING_RATE=20
	fi
	if [ -z "${SOAP_ACCEPTING_TIME}" ] ; then
		SOAP_ACCEPTING_TIME=5
	fi
	# Rate limit connections to our SOAP server (20 connections per minutes should be more than enough...)
	${IPTABLES} -A dtc-xen-in -p tcp --dport 8089 -m state --state NEW -m recent --set
	${IPTABLES} -A dtc-xen-in -p tcp --dport 8089 -m state --state NEW -m recent --update --seconds ${SOAP_ACCEPTING_TIME} --hitcount ${SOAP_ACCEPTING_RATE} -j LOGREJECT
}

port25_reject () {
	${IPTABLES} -A dtc-xen-in -p tcp --dport 25 -j LOGREJECT
}

call_add_custom_rules () {
	if [ -e /etc/dtc-xen/dtc-xen-firewall-custom-rules ] ; then
		. /etc/dtc-xen/dtc-xen-firewall-custom-rules
		add_custom_rules
	fi
}

limit_ssh_login_rate () {
	if [ -z "${SSH_ACCEPTING_RATE}" ] ; then
		SSH_ACCEPTING_RATE=10
	fi
	if [ -z "${SSH_ACCEPTING_TIME}" ] ; then
		SSH_ACCEPTING_TIME=300
	fi
	if [ -z "${SSH_FORWARDING_RATE}" ] ; then
		SSH_FORWARDING_RATE=5
	fi
	if [ -z "${SSH_FORWARDING_TIME}" ] ; then
		SSH_FORWARDING_TIME=10
	fi

	# Anti DoS SSH : deny ssh for 300 seconds after 4 attempts
	# This can't be too low because of the use of scp
	# For the dom0 first:
	${IPTABLES} -A dtc-xen-in -p tcp --dport 22 -m state --state NEW -m recent --set 
	${IPTABLES} -A dtc-xen-in -p tcp --dport 22 -m state --state NEW -m recent --update --seconds ${SSH_ACCEPTING_TIME} --hitcount ${SSH_ACCEPTING_RATE} -j LOGREJECT
	# The for the domUs:
	${IPTABLES} -A dtc-xen-fw -p tcp --dport 22 -m state --state NEW -m recent --set 
	${IPTABLES} -A dtc-xen-fw -p tcp --dport 22 -m state --state NEW -m recent --update --seconds ${SSH_FORWARDING_TIME} --hitcount ${SSH_FORWARDING_RATE} -j LOGREJECT
}

ping_flood_protect () {
	if [ -z "${PING_ACCEPTING_RATE}" ] ; then
		PING_ACCEPTING_RATE=5
	fi
	if [ -z "${PING_FORWARDING_RATE}" ] ; then
		PING_FORWARDING_RATE=50
	fi

	# Limit for dom0
	${IPTABLES} -A dtc-xen-in -p icmp --icmp-type echo-request -m limit --limit ${PING_ACCEPTING_RATE}/s -j RETURN
	${IPTABLES} -A dtc-xen-in -p icmp --icmp-type echo-request -j LOGDROP
	# There is no reason why a 20 VPS would be ping more than 50 times per seconds
	${IPTABLES} -A dtc-xen-fw -p icmp --icmp-type echo-request -m limit --limit ${PING_FORWARDING_RATE}/s -j RETURN
	${IPTABLES} -A dtc-xen-fw -p icmp --icmp-type echo-request -j LOGDROP
}
syn_flood_protect () {
	if [ -z "${SYN_ACCEPTING_RATE}" ] ; then
		SYN_ACCEPTING_RATE=10
	fi
	if [ -z "${SYN_FORWARDING_RATE}" ] ; then
		SYN_FORWARDING_RATE=100
	fi
	# For dom0
	${IPTABLES} -A dtc-xen-in -p tcp --syn -m limit --limit ${SYN_ACCEPTING_RATE}/s -j RETURN
	${IPTABLES} -A dtc-xen-in -p tcp --syn -j LOGDROP
	# For VPS
	${IPTABLES} -A dtc-xen-fw -p tcp --syn -m limit --limit ${SYN_FORWARDING_RATE}/s -j RETURN
	${IPTABLES} -A dtc-xen-fw -p tcp --syn -j LOGDROP
}

port_scanner_limitation () {
	if [ -z "${GLOB_CONNECT_ACCEPTING_RATE}" ] ; then
		GLOB_CONNECT_ACCEPTING_RATE=10
	fi
	if [ -z "${GLOB_CONNECT_FORWARDING_RATE}" ] ; then
		GLOB_CONNECT_FORWARDING_RATE=1000
	fi
	#Furtive port scanner a bit more annoying...
	${IPTABLES} -A dtc-xen-in -p tcp --tcp-flags SYN,ACK,FIN,RST RST -m limit --limit ${GLOB_CONNECT_ACCEPTING_RATE}/s -j RETURN
	${IPTABLES} -A dtc-xen-in -p tcp --tcp-flags SYN,ACK,FIN,RST RST -j LOGDROP
	${IPTABLES} -A dtc-xen-fw -p tcp --tcp-flags SYN,ACK,FIN,RST RST -m limit --limit ${GLOB_CONNECT_FORWARDING_RATE}/s -j RETURN
	${IPTABLES} -A dtc-xen-fw -p tcp --tcp-flags SYN,ACK,FIN,RST RST -j LOGDROP
}

setup_log_chain () {
	${IPTABLES} -N LOGDROP > /dev/null 2> /dev/null
	${IPTABLES} -F LOGDROP
	${IPTABLES} -A LOGDROP -m limit --limit 1/s -j LOG --log-prefix "LOGDROP: "
	${IPTABLES} -A LOGDROP -j DROP

	${IPTABLES} -N LOGREJECT > /dev/null 2> /dev/null
        ${IPTABLES} -F LOGREJECT
        ${IPTABLES} -A LOGREJECT -m limit --limit 1/s -j LOG --log-prefix "LOGREJECT: "
        ${IPTABLES} -A LOGREJECT -j REJECT
}

case "${1}" in
	start)
		# flush-input-chain
		setup_log_chain
		create_dtc_xen_forward_chain
		accept_localhost_traffic
		port25_reject
		soap_server_limit
		call_add_custom_rules
		limit_ssh_login_rate
		ping_flood_protect
		syn_flood_protect
		port_scanner_limitation
        ;;

	stop)
		while iptables -D dtc-xen-fw 1 ; do echo -n "" ; done
		while iptables -D dtc-xen-in 1 ; do echo -n "" ; done
	;;

	restart|reload|force-reload)
		${0} stop
		sleep 1
		${0} start
	;;

	*)
		echo "Usage: ${0} "'{start|stop|restart|reload}'
		exit 1

esac

exit 0
