#!/bin/bash

# Curby's Netfilter Script - intfs.curby.net Configuration
RULESVERSION=1.0.2
DATE=2015-03-12

# Copyright (C) 2015  Michael Lee <curby@cur.by>
#
# 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; either version 2
# of the License, or (at your option) any later version.
#
# 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; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.



#   ,------------------------------------------------------------------------------------,
#   |   Host-specific Configuration                                                      |
#   '------------------------------------------------------------------------------------'

# Iptables executable (leave empty to auto-locate)
IPTABLES=

# Map network roles to physical interfaces
EXTDEV="eth0"

# YES if host is directly accessible from Internet
# NO if host is on a private subnet
EXTDEV_ROUTABLE="NO"



#   ,------------------------------------------------------------------------------------,
#   |   Netfilter Ruleset                                                                |
#   '------------------------------------------------------------------------------------'

generate_rules() {

initialize_ruleset

# ________________________________________________________________________________________
# '-- 1: Stateful Matches ---------------------------------------------------------------'

# Most valid packets match these rules, so we put them first
f_rule "-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT"
f_rule "-A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT"
f_rule "-A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT"

# ________________________________________________________________________________________
# '-- 2: General Protections ------------------------------------------------------------'

# BadTraffic handles INVALID packets and other things that just shouldn't exist
f_rule "-A INPUT -j BadTraffic"
f_rule "-A FORWARD -j BadTraffic"
f_rule "-A OUTPUT -j BadTraffic"

# BanHammer handles things like naughty hosts that we want to get rid of ASAP
f_rule "-A INPUT -j BanHammer"
f_rule "-A FORWARD -j BanHammer"
f_rule "-A OUTPUT -j BanHammer"

# All clients get DNS from our DNS proxy instead of NATing through
f_rule "-A FORWARD -p tcp --dport 53 -j REJECT --reject-with icmp-port-unreachable"
f_rule "-A FORWARD -p udp --dport 53 -j REJECT --reject-with icmp-port-unreachable"

# ________________________________________________________________________________________
# '-- 3: Trust Settings: Allow traffic from trusted hosts -------------------------------'

f_rule "-A INPUT -i lo -j ACCEPT"
f_rule "-A OUTPUT -o lo -j ACCEPT"
if [[ $INTNET_EXISTS ]]; then
  # We won't blindly trust INTNET, but will let them go out on the Internet
  #f_rule "-A INPUT -i $INTDEV -j ACCEPT"
  f_rule "-A OUTPUT -o $INTDEV -j ACCEPT"
  f_rule "-A FORWARD -i $INTDEV -j ACCEPT"
fi

# ________________________________________________________________________________________
# '-- 4: INPUT Default Chain ------------------------------------------------------------'

# ICMP Filters
f_rule "-A INPUT -p icmp -j AllowICMP"

# Allow fail2ban-protected SSH access
f_table ":fail2ban-ssh - [0:0]"
f_table ":fail2ban-sshban - [0:0]"
f_rule "-A INPUT -p tcp --dport 22 -j fail2ban-ssh"
f_rule "-A INPUT -p tcp --dport 22 -j fail2ban-sshban"
f_rule "-A INPUT -s 128.165.0.0/16 -p tcp --dport 22 $TRUSTEDSSHLIMIT -j ACCEPT"
f_rule "-A INPUT -s 192.168.0.0/24 -p tcp --dport 22 $TRUSTEDSSHLIMIT -j ACCEPT"
f_rule "-A INPUT -s 72.14.181.128 -p tcp --dport 22 $TRUSTEDSSHLIMIT -j ACCEPT"
f_rule "-A INPUT -p tcp --dport 22 $SSHLIMIT -j ACCEPT"
f_rule "-A INPUT -p tcp --dport 22 $LOGLIMIT -j LOG --log-prefix \"IPT BadSSH \""
f_rule "-A INPUT -p tcp --dport 22 -j DROP"

# Web stuff
f_rule "-A INPUT -p tcp -m multiport --dports 80,443 -j SynDam"

# Syslog aggregator
f_rule "-A INPUT -p tcp --dport 514 -j ACCEPT"
f_rule "-A INPUT -p udp --dport 514 -j ACCEPT"

# Samba Sharing
f_rule "-A INPUT -p tcp -m multiport --dports 139,445 -j SynDam"
f_rule "-A INPUT -p udp -m multiport --dports 137,138 -j ACCEPT"

# APCUPSD Sharing
f_rule "-A INPUT -s 192.168.0.1 -p tcp --dport 3551 -j SynDam"
f_rule "-A INPUT -s 192.168.0.1 -p udp --dport 3551 -j ACCEPT"

# ________________________________________________________________________________________
# '-- 5: FORWARD Default Chain ----------------------------------------------------------'

# ________________________________________________________________________________________
# '-- 6: OUTPUT Default Chain -----------------------------------------------------------'

# When possible, restrict outbound traffic to trusted hosts

# ICMP Filters
f_rule "-A OUTPUT -p icmp -j AllowICMP"

# SSH to anywhere
f_rule "-A OUTPUT -p tcp --dport 22 -j ACCEPT"

# Mail anywhere
f_rule "-A OUTPUT -p tcp --dport 25 -j ACCEPT"

# whois
f_rule "-A OUTPUT -p tcp --dport 43 -j ACCEPT"

# External DNS servers
f_rule "-A OUTPUT -p tcp --dport 53 -j ACCEPT"
f_rule "-A OUTPUT -p udp --dport 53 -j ACCEPT"

# NTP peers
f_rule "-A OUTPUT -p udp --dport 123 -j ACCEPT"

# If we want to allow all outbound web traffic from local host
f_rule "-A OUTPUT -p tcp -m multiport --dports 80,443 -j ACCEPT"

# Allow UDP traceroutes out
f_rule "-A OUTPUT -p udp --dport 33434:33534 -j ACCEPT"

# VNC / Apple Screen Sharing
f_rule "-A OUTPUT -p tcp --dport 5900 -j ACCEPT"
f_rule "-A OUTPUT -p udp --dport 5900 -j ACCEPT"

# IRC
f_rule "-A OUTPUT -p tcp --dport 6667 -j ACCEPT"

# Samba Sharing
f_rule "-A OUTPUT -p tcp -m multiport --dports 139,445 -j ACCEPT"
f_rule "-A OUTPUT -p udp -m multiport --dports 137,138 -j ACCEPT"
# XXX why is the next rule needed?
f_rule "-A OUTPUT -p udp --sport 137 -d $EXTNET -j ACCEPT"

# APCUPSD Sharing
f_rule "-A OUTPUT -d 192.168.0.1 -p tcp --dport 3551 -j ACCEPT"
f_rule "-A OUTPUT -d 192.168.0.1 -p udp --dport 3551 -j ACCEPT"

# ________________________________________________________________________________________
# '-- 7: Source NAT ---------------------------------------------------------------------'

# ________________________________________________________________________________________
# '-- 8: Default Chain Endings ----------------------------------------------------------'

# Silence chatty protocols to reduce log load
# Warning: these may make debugging more difficult by silently dropping traffic
f_table ":LogCleaner - [0:0]"
f_rule "-A INPUT -j LogCleaner"
f_rule "-A FORWARD -j LogCleaner"
f_rule "-A OUTPUT -j LogCleaner"

# Log everything else
f_rule "-A INPUT $LOGLIMIT -j LOG --log-prefix \"IPT EndOfINPUT \""
f_rule "-A FORWARD $LOGLIMIT -j LOG --log-prefix \"IPT EndOfFORWARD \""
f_rule "-A OUTPUT $LOGLIMIT -j LOG --log-prefix \"IPT EndOfOUTPUT \""

# Count everything that doesn't get accepted, silenced, or explicitly dropped
f_rule "-A INPUT -j DROP"
f_rule "-A FORWARD -j DROP"
f_rule "-A OUTPUT -j DROP"

# ________________________________________________________________________________________
# '-- 9a: Custom Chains: Bad Traffic ----------------------------------------------------'

f_table ":BadTraffic - [0:0]"

# Packets marked invalid from connection tracking
#f_rule "-A BadTraffic -m conntrack --ctstate INVALID $LOGLIMIT -j LOG --log-prefix \"IPT INVALID \""
f_rule "-A BadTraffic -m conntrack --ctstate INVALID -j DROP"

# Non-tracked TCP packets without SYN flag
#f_rule "-A BadTraffic -p tcp ! --syn -m conntrack --ctstate NEW $LOGLIMIT -j LOG --log-prefix \"IPT NewNotSYN \""
f_rule "-A BadTraffic -p tcp ! --syn -m conntrack --ctstate NEW -j DROP"

# Packets appearing on the wrong interface
# XXX these shouldn't be necessary with rp_filter on but it still matched packets last time.  keep an eye on it
if [[ "z$EXTDEV_ROUTABLE" == "zYES" ]]; then
  f_rule "-A BadTraffic -i $EXTDEV -s 0.0.0.0 -j MartianDrop"
  f_rule "-A BadTraffic -i $EXTDEV -s 10.0.0.0/8 -j MartianDrop"
  f_rule "-A BadTraffic -i $EXTDEV -s 172.16.0.0/12 -j MartianDrop"
  f_rule "-A BadTraffic -i $EXTDEV -s 192.168.0.0/16 -j MartianDrop"
fi
if [[ "$INTNET_EXISTS" ]]; then
  f_rule "-A BadTraffic -i $INTDEV -s 0.0.0.0 -j RETURN"
  f_rule "-A BadTraffic -i $INTDEV ! -s $INTNET -j MartianDrop"
fi
if [[ "$DMZNET_EXISTS" ]]; then
  f_rule "-A BadTraffic -i $DMZDEV -s 0.0.0.0 -j RETURN"
  f_rule "-A BadTraffic -i $DMZDEV ! -s $DMZNET -j MartianDrop"
fi
f_table ":MartianDrop - [0:0]"
f_rule "-A MartianDrop $LOGLIMIT -j LOG --log-prefix \"IPT Martian \""
f_rule "-A MartianDrop -j DROP"

# ________________________________________________________________________________________
# '-- 9b: Custom Chains: Ban Hammer -----------------------------------------------------'

f_table ":BanHammer - [0:0]"

# Example: Stop all traffic to/from a given host or subnet
#f_rule "-A BanHammer -s 192.246.40.28/32 -j DROP"
#f_rule "-A BanHammer -d 192.246.40.28/32 -j DROP"

# ________________________________________________________________________________________
# '-- 9c: Custom Chains: Allow ICMP -----------------------------------------------------'

f_table ":AllowICMP - [0:0]"

# Allow pings (8)
# Pongs (0) are treated as RELATED traffic and automatically allowed
f_rule "-A AllowICMP -p icmp --icmp-type 8 $PINGLIMIT -j ACCEPT"

# We don't really NEED to log and drop here, but it shortens the path through the chains
f_rule "-A AllowICMP $LOGLIMIT -j LOG --log-prefix \"IPT BadICMP \""
f_rule "-A AllowICMP -j DROP"

# ________________________________________________________________________________________
# '-- 9d: Custom Chains: SYN Flood Protector --------------------------------------------'

f_table ":SynDam - [0:0]"

# Allowed TCP traffic should jump here for SYN flood protection
f_rule "-A SynDam $SYNLIMIT -j ACCEPT"
f_rule "-A SynDam $LOGLIMIT -j LOG --log-prefix \"IPT SynFlood \""
f_rule "-A SynDam -j DROP"

# ________________________________________________________________________________________
# '-- 9z: Custom Chains: Log Cleaner ----------------------------------------------------'

#       25/tcp        SMTP
#       53/udp,tcp    DNS (useless)
#       67/udp        DHCP/BOOTP server (useless)
#       123/udp       NTP (time synchronization)
#       135/tcp       microsoft rpc (useless)
#       137-138/udp   SMB/NetBIOS stuff
#       139,445/tcp   SMB/NetBIOS stuff
#       192/udp       airport broadcasts/communication
#       631/udp,tcp   IPP (printing)
#       1026-1029/udp       Windows Messenger (spam)
#       1080/tcp            mydoom worm
#       1433-1434/udp       MS SQL (worm)
#       2745,5554,9898/tcp  (trojan)
#       6129,4899/tcp        remote admin tools (backdoor)
#       17500/udp            dropbox

f_rule "-A LogCleaner -p tcp -m multiport --dports 25,53,135,139,445,631,1080,1433,2745,4899,5554,6129,9898 -j DROP"
f_rule "-A LogCleaner -p udp -m multiport --dports 53,67,123,137,138,192,631,1026,1027,1028,1029,1433,1434,17500 -j DROP"

finalize_ruleset
}
# end of generate_rules()
