#!/bin/bash
#
### BEGIN INIT INFO
# Provides:          firewall
# Required-Start:    $network $remote_fs
# Required-Stop:     $network $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start firewalling
# Description:       Enable firewall on server.
### END INIT INFO

#
# firewall: firewall init script
# Copyright (C) 2007-2014 Simone Piccardi <piccardi@truelite.it>
# Copyright (C) 2007-2025 FUSS Project <info@fuss.bz.it>
# Authors: Simone Piccardi <piccardi@truelite.it>
#
# 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; version 2 of the License.
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program or from the site that you downloaded it
# from; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA  02111-1307   USA
#

MYNAME="$0"

# Kill me on all errors
# set -e    # no good, stops on resolution errors

# Stop processing if iptables is not there
if [ -x /sbin/iptables ]; then
    IPT_OK=1
else
    echo "ERROR: Cannot find iptables command"
    exit 1
fi

# sanitize evironment and define command
ENV="env -i LANG=C PATH=/usr/local/bin:/usr/bin:/bin"
IPT="$ENV /sbin/iptables"

# define used files
BASE_DIR={{etc_dir}}

PORT_REACH=$BASE_DIR/firewall-allowed-wan-services
HOST_REACH=$BASE_DIR/firewall-allowed-wan-hosts
HOST_DENY=$BASE_DIR/firewall-denied-lan-hosts
HOST_ALLOW=$BASE_DIR/firewall-allowed-lan-hosts
SERVICES_ALLOW=$BASE_DIR/firewall-external-services
PORT_REACH_HOSTS=$BASE_DIR/firewall-allowed-wan-host-services
DEFAULT_ADDED_RULES=$BASE_DIR/firewall-default-added-rules
CUSTOM_RULES=$BASE_DIR/firewall-custom-rules

# Define variables from configuration (by ansible)
LOCALNET="{{localnet}}"
INTERN_IFACES='{{internal_ifaces|join(' ')}}'
EXTERN_IFACE="{{external_ifaces|join(' ')}}"
EXTERN_IP="{{fuss_eth[external_ifaces[0]]['ipv4']['address']}}"

##
## Function to block a set of hosts
## use file firewall-denied-lan-hosts to block a LAN host 
## (to block single client)
##
deny_lan_hosts () {
    if [ -f "$HOST_DENY" ]; then
	for i in `grep -v ^#  $HOST_DENY|cut -d: -f1` ; do
	    if $IPT -A outside -s $i -j DROP; then
		$IPT -A INPUT -s $i -p tcp --dport 8080 -j DROP
	    else
		echo "WARN: host $i invalid"
	    fi
	done
    else
	echo "WARN: file $HOST_DENY not found, no inside host totally blocked"
    fi
}

##
## Function to open ports on fuss-server
## Use file firewall-allowed-wan-services for allowed WAN services
## always reachable from LAN
##
allowed_wan_services () {
    if [ -f "$PORT_REACH" ]; then
	for i in `grep -v ^# $PORT_REACH| cut -d: -f1` ; do
	    PORT=`echo $i|cut -d"/" -f 1`
	    PROTO=`echo $i|cut -d"/" -f 2`
	    $IPT -A outside -p $PROTO --dport $PORT -j ACCEPT
	done
    else 
	echo "WARN: file $PORT_REACH not found, no outside port always allowed"
    fi
}

##
## Function to open set of external hosts reacheable from anyone
## Use file firewall-allowed-wan-hosts for hosts on WAN always
## reachable from LAN
##
allowed_wan_hosts () {
    if [ -f "$HOST_REACH" ]; then
	for i in `grep -v ^# $HOST_REACH|cut -d: -f1` ; do
	    $IPT -A outside -d $i -j ACCEPT
	done
    else
	echo "WARN: file $HOST_REACH not found, no outside host always allowed"
    fi
}

##
## Function to open internet to a set of lan hosts
## Use file firewall-allowed-lan-host for hosts on LAN allowed to
## reach any host on WAN
## 
allowed_lan_hosts () {
    if [ -f "$HOST_ALLOW" ]; then
	for i in `grep -v ^# $HOST_ALLOW | cut -d: -f1` ; do
	    $IPT -A outside -s $i -j ACCEPT
	done
    else
	echo "WARN: file $HOST_ALLOW not found, no inside host totally allowed"
    fi
}

##
## Function to open local service access from outside
## Use firewall-allowed-wan-host-services for allowed ports on WAN
## hosts always reachable from LAN
##
allowed_wan_host_services () {
    if [ -f "$PORT_REACH_HOSTS" ]; then
	for i in `grep -v ^# $PORT_REACH_HOSTS | cut -d: -f1-2` ; do
	    HOST=`echo $i | cut -d: -f1`
	    PORT=`echo $i | cut -d: -f2 |cut -d"/" -f 1`
	    PROT=`echo $i | cut -d: -f2 |cut -d"/" -f 2`
	    $IPT -A outside -d $HOST -p $PROT --dport $PORT -j ACCEPT
	done
    else
	echo "WARN: file $PORT_REACH_HOSTS not found, no host port access"
    fi
}

##
## Function to open access for internal host to outside services
## Use file firewall-external-services for service on the fuss-server
## reachable from WAN
##
external_services () {
    if [ -f "$SERVICES_ALLOW" ]; then
	for i in `grep -v ^# $SERVICES_ALLOW | cut -d: -f1` ; do
	    PORT=`echo $i|cut -d"/" -f 1`
	    PROTO=`echo $i|cut -d"/" -f 2`
	    $IPT -A services -p $PROTO --dport $PORT -j ACCEPT
	done
    else
	echo "WARN: file $SERVICES_ALLOW not found, no external service allowed"
    fi
}

#
# Enable some usefule conntrack modules
#
modprobe ip_conntrack_ftp
modprobe ip_conntrack_sip

case $1 in
  start)
    echo "Starting firewall, local network/s: $LOCALNET"

    # Clean all
    $IPT -F
    $IPT -t mangle -F
    $IPT -t nat -F
    $IPT -X || true

    #
    # Turn on/off some kernel features
    #
    echo 1 > /proc/sys/net/ipv4/ip_forward
    echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
    echo 0 > /proc/sys/net/ipv4/conf/all/accept_source_route
    echo 0 > /proc/sys/net/ipv4/conf/all/accept_redirects
    # echo 1 > /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses

    # Chain allowed, for generic allowed traffic, for both INPUT and FORWARD
    $IPT -N allowed || true
    $IPT -A allowed -m state --state ESTABLISHED,RELATED -j ACCEPT
    $IPT -A allowed -p icmp --icmp-type destination-unreachable -j ACCEPT
    $IPT -A allowed -p icmp --icmp-type time-exceeded -j ACCEPT
    $IPT -A allowed -p icmp --icmp-type echo-request -j ACCEPT
    $IPT -A allowed -p icmp --icmp-type fragmentation-needed -j ACCEPT

    # Chain services, for reacheable services on the fuss-server
    $IPT -N services || true
    external_services

    # Chain outbound, for outbound (internal to internet) connections
    #
    $IPT -N outside || true
    deny_lan_hosts
    allowed_lan_hosts
    allowed_wan_hosts
    allowed_wan_services
    allowed_wan_host_services
{% if wifi_devices is defined and wifi_devices %}
    # Chain wifi_devices, for incoming connections from WiFi devices network
    #
    $IPT -N wifi_devices
{% endif %}

    ##
    ## INPUT
    ##
    # localhost traffic is allowed
    $IPT -A INPUT -i lo -j ACCEPT
    $IPT -A INPUT -s $EXTERN_IP -j ACCEPT
    # local nets traffic allowed
    for i in $LOCALNET; do
	$IPT -A INPUT -s $i -m state --state NEW -p tcp --dport 3128 -j DROP
	$IPT -A INPUT -s $i -m state --state NEW -j ACCEPT
    done
{% if wifi_guest is defined and wifi_guest %}
    # WiFi Guest access rules rules
    $IPT -A INPUT -s {{guest_network}} -m state --state NEW -p tcp --dport 3128 -j DROP
    $IPT -A INPUT -s {{guest_network}} -m state --state NEW -j ACCEPT
{% endif %}
{% if wifi_devices is defined and wifi_devices %}
    # from WiFi Devices network accept connections to some services
    $IPT -A INPUT -s {{devices_network}} -i {{devices_iface}} -m state --state NEW  -j wifi_devices
    $IPT -A wifi_devices -p tcp --dport 53 -j ACCEPT
    $IPT -A wifi_devices -p udp --dport 53 -j ACCEPT
    $IPT -A wifi_devices -p udp --dport 67:68 --sport 67:68 -j ACCEPT
    $IPT -A wifi_devices -p udp --dport 123 -j ACCEPT
{% endif %}

    # SSH always open
    $IPT -A INPUT -p tcp --dport 22 -j ACCEPT
    $IPT -A INPUT -j allowed
    if [ ! -z "$EXTERN_IFACE" ]; then
	$IPT -A INPUT -i $EXTERN_IFACE -m state --state NEW -j services
    fi

    # final closure
    $IPT -A INPUT -j DROP
    
    echo -n "INPUT -- "

    ##
    ## OUTPUT
    ##
    $IPT -A OUTPUT -o lo -j ACCEPT
    # outgoing from local allowed
    $IPT -A OUTPUT -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
    $IPT -A OUTPUT -j DROP
    
    echo -n "OUTPUT -- "

    ##
    ## FORWARD
    ##
    $IPT -A FORWARD -j allowed
    if [ ! -z "$EXTERN_IFACE" ]; then
	for i in $LOCALNET; do
	    $IPT -A FORWARD -o $EXTERN_IFACE -s $i -m state --state NEW -j outside
	done
{% if wifi_guest is defined and wifi_guest %}
	# WiFi Guest network filtered on outside chain
        $IPT -A FORWARD -o $EXTERN_IFACE -s {{guest_network}} -m state --state NEW -j outside
{% endif %}
{% if wifi_devices is defined and wifi_devices %}
	# WiFi Devices can go outside unfiltered
	$IPT -A FORWARD -o $EXTERN_IFACE -s {{devices_network}} -i {{devices_iface}} -m state --state NEW -j ACCEPT
{% endif %}
    fi
    $IPT -A FORWARD -j DROP

    echo -n "FORWARD -- "

    ##
    ## PREROUTING
    ##
    echo -n "PREROUTING -- "
    for i in $INTERN_IFACES; do
	$IPT -t nat -A PREROUTING -i $i -p udp --dport 123 -j REDIRECT
    done
{% if wifi_guest is defined and wifi_guest %}
    # in WiFi Guest network redirect NTP
    $IPT -t nat -A PREROUTING -s {{guest_network}} -p udp --dport 123 -j REDIRECT
{% endif %}

    ##
    ## POSTROUTING
    ##
    # masquerade LAN outgoing packages
    for i in $LOCALNET; do
	$IPT -t nat -A POSTROUTING -o $EXTERN_IFACE -s $i -j MASQUERADE
    done
{% if wifi_guest is defined and wifi_guest %}
    # WiFi Guest network masquerading
    $IPT -t nat -A POSTROUTING -o $EXTERN_IFACE -s {{guest_network}} -j MASQUERADE
{% endif %}
{% if wifi_devices is defined and wifi_devices %}
    # WiFi Devices network masquerading
    $IPT -t nat -A POSTROUTING -o $EXTERN_IFACE -s {{devices_network}} -j MASQUERADE
{% endif %}

    echo "POSTROUTING"

    . $DEFAULT_ADDED_RULES
    . $CUSTOM_RULES

    # END
    exit 0
    ;;

  stop)
    echo "Stopping firewall, local network/s: $LOCALNET"

    # Clean all
    echo cleaning all
    $IPT -F
    $IPT -t mangle -F
    $IPT -t nat -F
    $IPT -X || echo "failing cleaning chains"

    #
    # Revert on/off some kernel features
    #
    echo 0 > /proc/sys/net/ipv4/ip_forward
    echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
    echo 1 > /proc/sys/net/ipv4/conf/all/accept_source_route
    echo 1 > /proc/sys/net/ipv4/conf/all/accept_redirects

    # END
    exit 0
    ;;

   restart|reload|force-reload)
	$0 stop
	$0 start
	;;

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

esac

