#!/bin/bash

# Curby's Netfilter Script - linode.curby.net Configuration
RULESVERSION=1.1.1
DATE=2018-11-16

# Copyright (C) 2018  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                                                      |
#   '------------------------------------------------------------------------------------'

# 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="YES"



#   ,------------------------------------------------------------------------------------,
#   |   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"

# ________________________________________________________________________________________
# '-- 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"

# Fail2ban "perma"ban
f_table ":f2b-banhammer - [0:0]"

# Fail2ban-protected SSH access
f_table ":f2b-sshd - [0:0]"
f_rule "-A INPUT -p tcp --dport 22 -j f2b-sshd"
f_rule "-A INPUT -p tcp --dport 22 -j f2b-banhammer"
f_rule "-A INPUT -s 128.165.0.0/16 -p tcp --dport 22 $TRUSTEDSSHLIMIT -j ACCEPT"
f_rule "-A INPUT -s 173.10.246.233 -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"

# DNS from home (off by default but included in case we need to allow)
#f_rule "-A INPUT -s 173.10.246.233 -p tcp --dport 53 -j SynDam"
#f_rule "-A INPUT -s 173.10.246.233 -p udp --dport 53 -j ACCEPT"

# Fail2ban-protected Web access
f_table ":f2b-apache - [0:0]"
f_rule "-A INPUT -p tcp -m multiport --dports 80,443 -j f2b-apache"
f_rule "-A INPUT -p tcp -m multiport --dports 80,443 -j f2b-banhammer"

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

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

# goaccess
f_rule "-A INPUT -p tcp --dport 7890 -j SynDam"

# VPN - Allow UDP traffic on port 1194.
f_rule "-A INPUT -p udp -m state --state NEW,ESTABLISHED --dport 1194 -j ACCEPT"
f_rule "-A INPUT -i tun0 -j ACCEPT"

# VPN - Allow traffic on the TUN interface so OpenVPN can communicate with eth0.
f_rule "-A OUTPUT -p udp -m state --state ESTABLISHED --sport 1194 -j ACCEPT"
f_rule "-A OUTPUT -o tun0 -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"

# Outbound web traffic from local host
f_rule "-A OUTPUT -p tcp -m multiport --dports 80,443,8000,8080 -j ACCEPT"

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

# Send TLS-encrypted email to gmail
f_rule "-A OUTPUT -p tcp --dport 587 -j ACCEPT"

# Twitch.tv XXX remove if unneeded
#f_rule "-A OUTPUT -p udp --dport 5222 -j ACCEPT"

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

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

# AIM
f_rule "-A OUTPUT -p tcp -m multiport --dports 5190:5193 -j ACCEPT"

# GPG
f_rule "-A OUTPUT -p tcp --dport 11371 -j ACCEPT"

# iCloud (also needs 25,80,443,587
f_rule "-A OUTPUT -p tcp -m multiport --dports 993,5223 -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
#       514/udp       syslog
#       631/udp,tcp   IPP (printing)
#       1026-1029/udp       Windows Messenger (spam)
#       1080/tcp            mydoom worm
#       1433-1434/udp       MS SQL (worm)
#       1947/udp            some sort of license server?
#       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,514,631 -j DROP"
f_rule "-A LogCleaner -p udp -m multiport --dports 1026,1027,1028,1029,1433,1434,1947,17500 -j DROP"

finalize_ruleset
}
# end of generate_rules()
