#!/bin/bash

## http://www.ibm.com/developerworks/edu/l-dw-linuxfw-i.html

# Our complete stateful firewall script. This firewall can be customized for a
# laptop, workstation, router or even a server. :)

# All your network interfaces, including lo.
#interfaces=(lo eth0 wlan0)
interfaces=(lo enp4s0 wlp0s29u1u8)

# The interfaces that provides your "uplink".
#uplink=(eth0 wlan0)
uplink=(enp4s0 wlp0s29u1u8)

# The local (ie., trusted) interfaces.
local_interfaces=(lo)

# Local network address range (with mask).
lan="192.168.1.0/24"

# Change this line so that it lists the assigned numbers or symbolic names
# (from /etc/services) of all the services that you'd like to provide to the
# general public. If you don't want any services enabled, set it to ().
services_tcp=()
services_udp=()
lan_services_tcp=(ssh)
lan_services_udp=()

# Our default INPUT policy: DROP.
default_input_policy=DROP


function start_fw() {
    echo -n "Starting IPv4 firewall... "

    local has_ssh_22=false

    # Input table default policy.
    iptables -P INPUT "$default_input_policy"

    # Accept local (trusted) traffic.
    for i in "${local_interfaces[@]}"
    do
        iptables -A INPUT -i "$i" -j ACCEPT
    done

    # Accept ESTABLISHED or RELATED connections.
    iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

    # Enable public access to global services.
    for s in "${services_tcp[@]}"
    do
        iptables -A INPUT -p tcp --dport "$s" -m conntrack --ctstate NEW -j LOG --log-prefix "[iptables] $s TCP svc:"
        iptables -A INPUT -p tcp --dport "$s" -m conntrack --ctstate NEW -j ACCEPT
        # Check whether ssh is part of our service set
        [[ "$s" == "ssh" ]] && has_ssh_22=true
    done
    for s in "${services_udp[@]}"
    do
        iptables -A INPUT -p udp --dport "$s" -m conntrack --ctstate NEW -j LOG --log-prefix "[iptables] $s UDP svc:"
        iptables -A INPUT -p udp --dport "$s" -m conntrack --ctstate NEW -j ACCEPT
    done

    # Enable LAN access to LAN services.
    for s in "${lan_services_tcp[@]}"
    do
        iptables -A INPUT -p tcp --dport "$s" -m conntrack --ctstate NEW -s "$lan" -j LOG --log-prefix "[iptables] $s TCP lan-svc:"
        iptables -A INPUT -p tcp --dport "$s" -m conntrack --ctstate NEW -s "$lan" -j ACCEPT
        # Check whether ssh is part of our service set
        [[ "$s" == "ssh" ]] && has_ssh_22=true
    done
    for s in "${lan_services_udp[@]}"
    do
        iptables -A INPUT -p udp --dport "$s" -m conntrack --ctstate NEW -s "$lan" -j LOG --log-prefix "[iptables] $s UDP lan-svc:"
        iptables -A INPUT -p udp --dport "$s" -m conntrack --ctstate NEW -s "$lan" -j ACCEPT
    done

    # Log and reject attempts at SSH connection on port 22 separately.
    if ! $has_ssh_22
    then
        iptables -A INPUT -p tcp --dport ssh -m conntrack --ctstate NEW -j LOG --log-prefix "[iptables] ssh22fail:"
        iptables -A INPUT -p tcp --dport ssh -m conntrack --ctstate NEW -j "$default_input_policy"
    fi

    # Other rejected attempts.
    iptables -A INPUT -p tcp -j LOG --log-prefix "[iptables] bad TCP:"
    iptables -A INPUT -p udp -j LOG --log-prefix "[iptables] bad UDP:"
    iptables -A INPUT -p tcp -j "$default_input_policy"
    iptables -A INPUT -p udp -j "$default_input_policy"

    # Only non-UDP/TCP eth0/wlan0 traffic might end up here: low-level traffic.
    iptables -A INPUT -j LOG --log-prefix "[iptables] bad other:"

    # Output table default policy: ACCEPT.
    iptables -P OUTPUT ACCEPT

    # Enable reverse path filtering on all interfaces.
    for i in "${interfaces[@]}"
    do
        echo 1 > "/proc/sys/net/ipv4/conf/$i/rp_filter"
    done

    echo "done"
}

function stop_fw() {
    echo -n "Stopping IPv4 firewall... "

    iptables -F INPUT
    iptables -P INPUT ACCEPT
    iptables -F OUTPUT
    iptables -P OUTPUT ACCEPT

    echo "done"
}

if [ "$1" = "start" ]
then
    start_fw
elif [ "$1" = "stop" ]
then
    stop_fw
elif [ "$1" = "restart" ]
then
    stop_fw
    start_fw
else
    echo "Syntax: ${0##*/} <start|stop|restart>"
fi