Iptables (1.4)/firewall.sh


 * 1) !/bin/bash
 * 2) This is a generic version of the script I use to protect my servers.
 * 3) It's a bit more powerful, and a bit more intelligent. A lot of stuff,
 * 4) like the INVALID state, does not operate with quite the finesse that
 * 5) we were led to believe. Likewise, overly restricting traffic in
 * 6) general tends to throttle a lot of good - if malformed - traffic, for
 * 7) relatively little gain, and takes resources away from handling more
 * 8) serious issues. It is a bit larger, despite being more efficient.
 * 9) This script has undergone several revisions. I was originally
 * 10) tarpitting telnet attempts to port 25, for example, thus TARPORTS.
 * 11) I no longer do this for several reasons, but haven't bothered with
 * 12) fully purging the code yet.
 * 13) This script was developed by Vekseid, hosted at
 * 14) http://hexwiki.com/wiki/Iptables_(1.4)/firewall.sh?action=raw
 * 15) Discussed at http://hexwiki.com/wiki/Iptables_(1.4)
 * 16) - vek@vekseid.com
 * 17) This script would not have been possible without Oskar Andreasson's
 * 18) IPTables Tutorial, found at:
 * 19) http://www.frozentux.net/iptables-tutorial/iptables-tutorial.html
 * 20) I additionally made use of a good amount of the information in Jan
 * 21) Engelhardt's "Detecting and deceiving network scans", found here:
 * 22) http://jengelh.medozas.de/documents/Chaostables.pdf
 * 1) This script would not have been possible without Oskar Andreasson's
 * 2) IPTables Tutorial, found at:
 * 3) http://www.frozentux.net/iptables-tutorial/iptables-tutorial.html
 * 4) I additionally made use of a good amount of the information in Jan
 * 5) Engelhardt's "Detecting and deceiving network scans", found here:
 * 6) http://jengelh.medozas.de/documents/Chaostables.pdf

export IPT=/sbin/iptables export SELFIPS="198.51.100.80/28 192.0.2.184/30 203.0.113.88" export WHITELIST="203.0.113.32/28 203.0.113.45" export TRUSTEDFACES="lo" export BLACKLIST="" export SSHIP="198.51.100.83" export SSHPORT=23728 export SAFEIPS="198.51.100.82 198.51.100.187" export SERVIPS="198.51.100.82" export TARPORTS=25 export SERVPORTS="587,993" export OPENPORTS="80,443,25565" export UDPIPS="" export UDPPORTS="" export ALLOWPING=1 export USELOG=1
 * 1) Define variables to make for easy tuning.
 * 2) IPT - Location of the iptables binary
 * 3) SELFIPS      - The server's allocated IP addresses
 * 4) WHITELIST    - My own personal IPs, separated by spaces, CIDR style
 * 5)                for address blocks e.g. 127.0.0.0/8
 * 6) TRUSTEDFACES - Interfaces we trust, typically lo but could also have
 * 7)                e.g. eth1 in a two-server setup. Space separated
 * 8) BLACKLIST    - Hated IPs. Functions as whitelist.
 * 9) SSHIP        - IP address for SSH
 * 10) SAFEIPS      - Won't tarpit on this IP/mask
 * 11) SERVIPS      - Running SERVPORTS on this IP/mask
 * 12) SSHPORT      - The port I have SSH set to. This does not provide
 * 13)                much added security, but it makes logs less noisy.
 * 14)                I reccoment picking a number and using it for all of
 * 15)                your servers.
 * 16) TARPORTS     - Ports we are going to tarpit when they get hit
 * 17)                improperly. Default to 25 (SMTP) to trap spambots.
 * 18) SERVPORTS    - Ports only open on the service IP
 * 19) OPENPORTS    - Chosen ports to open on all IPs.
 * 20) UDPIPS       - Space-separated list of IP addresses/masks to permit
 * 21)                UDP on.
 * 22) UDPPORTS     - Comma-separated list of ports to allow UDP.
 * 23) ALLOWPING    - Whether or not to allow public pings. I often have to
 * 24)                diagnose problems for my members so sure, why not : )
 * 25) USELOG       - Whether to use the basic log. Nothing in these logs
 * 26)                are fully reliable so I want to make them easy to
 * 27)                disable.
 * 1)                disable.


 * 1) Ended up dropping this... IMO this is the responsibility of your
 * 2) host. Or if you are your own host, this is not done on your end
 * 3) machines.
 * 4) Retained solely to point out that people do this. However, keep in
 * 5) mind that each additional rule is more computing power for every
 * 6) connection.
 * 7) export DROPLIST="/etc/iptables/spamhausdrop.dat"
 * 1) connection.
 * 2) export DROPLIST="/etc/iptables/spamhausdrop.dat"
 * 1) export DROPLIST="/etc/iptables/spamhausdrop.dat"

export CONNREPORT="-m connlimit --connlimit-above 256 --connlimit-mask 24" export CONNLIMIT="-m connlimit --connlimit-above 512 --connlimit-mask 24" export LOGLEVEL="--log-level debug --log-ip-options" export LOGCON="--log-level debug --log-ip-options --log-tcp-sequence --log-tcp-options"
 * 1) The following are 'more advanced' variables.
 * 2) CONNREPORT   - While the limit is initially far higher, we want to
 * 3)                be reporting far before then, to see how many
 * 4)                legitimate users a blanket cutoff may restrict in
 * 5)                the event of an attack.
 * 6) CONNLIMIT    - The connlimit match declaration - it declares how
 * 7)                many tcp connections may exist for a given IP block.
 * 8) LOGLEVEL     - Logging line for basic logging.
 * 9) LOGCON       - Logs more serious incidents - connection flooding and
 * 10)                so on, that we want more reliable data for.
 * 1) LOGCON       - Logs more serious incidents - connection flooding and
 * 2)                so on, that we want more reliable data for.

export HASHPING="-m hashlimit --hashlimit-upto 5/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 29 --hashlimit-name icmp --hashlimit-htable-size 8192 --hashlimit-htable-max 8192 --hashlimit-htable-gcinterval 1000 --hashlimit-htable-expire 2000" export HASHSSH="-m hashlimit --hashlimit-upto 3/minute --hashlimit-burst 3 --hashlimit-mode srcip --hashlimit-srcmask 29 --hashlimit-name ssh --hashlimit-htable-size 8192 --hashlimit-htable-max 8192 --hashlimit-htable-gcinterval 3000 --hashlimit-htable-expire 60000" export HASHLOG="-m hashlimit --hashlimit-upto 2/minute --hashlimit-burst 240 --hashlimit-mode srcip --hashlimit-srcmask 24 --hashlimit-name log --hashlimit-htable-size 8192 --hashlimit-htable-max 8192 --hashlimit-htable-gcinterval 3000 --hashlimit-htable-expire 120000" export HASHCON="-m hashlimit --hashlimit-upto 2/minute --hashlimit-burst 240 --hashlimit-mode srcip --hashlimit-srcmask 24 --hashlimit-name con --hashlimit-htable-size 8192 --hashlimit-htable-max 8192 --hashlimit-htable-gcinterval 3000 --hashlimit-htable-expire 120000"
 * 1) The documentation on hashlimit could certainly use refinement. Worse,
 * 2) many examples might trick someone into using limit where a well-tuned
 * 3) hashlimit is really what they want - if they really want to take the
 * 4) performance hit at all.
 * 5) The following comes from reading the kernel module source, not the
 * 6) documentation : ) The man pages are not very helpful and the tutorial
 * 7) is extremely misleading on this match.
 * 8) Note that, this is from reading the source back in 2009. Things may
 * 9) have changed.
 * 10) --hashlimit-htable-gcinterval is always set to three seconds in these
 * 11)   examples, except for ping, but you may wish to slow this down if
 * 12)   you find your system cpu usage getting high under heavy loads.
 * 13)   Your load is going to be based loosely off of htable-size divided
 * 14)   by gcinterval. The default is 1000 (one second).
 * 15) --hashlimit-htable-expire is an easy one to calculate - just pick the
 * 16)   time when the hashlimit-upto/above will fill up your bucket, maybe
 * 17)   add a bit extra, or less if you don't care about occasional
 * 18)   overages. The default is 10000 (ten seconds).
 * 19) --hashlimit-htable-max to quote the current kernel source:
 * 20)   /* FIXME: do something. question is what.. */
 * 21)   It currently fires a kernel warning if the hash table is allowed
 * 22)   to grow beyond this size. It defaults to 8 times the htable-size,
 * 23)   and has a floor of htable-size if set to a low value.
 * 24)   There's no actual limit, however. I'm not convinced that the
 * 25)   hashlimit algorithm is all it could be >_>
 * 26)   I set this to htable-size since if somehow eight thousand
 * 27)   connections are open I rather want to be warned about it.
 * 28) --hashlimit-htable-size is the size of the hash index. A hash of
 * 29)   log(htable-size) is computed for whatever was given in mode/mask.
 * 30)   If two objects end up with the same hash, they get placed in a
 * 31)   chain which is iteratively searched.
 * 32)   DO NOT SET THIS LOW EXPECTING IT WILL THROTTLE A DDOS. In theory,
 * 33)   that is what htable-max is for. But only in theory.
 * 34)   It has a bit of a messy default:
 * 35)   num_physpages * page_size / 16384, with a cap of 8192 and a minimum
 * 36)   of 16. Pretty much anyone with 256mb+ of RAM is going to reach the
 * 37)   cap.
 * 38)   In addition to the basic memory for the table itself, you will
 * 39)   allocate pointer_bytes * 2 * htable-size in bytes for the table.
 * 40)   More memory gets allocated in other functions and I haven't fully
 * 41)   gone through the source, but for an amd64 machine that means 16
 * 42)   bytes per bucket.
 * 43)   Since this is the size of the full index and I have RAM to spare, I
 * 44)   set all of the tables to 8k. The main thing to worry about here is
 * 45)   the garbage collection interval, since this means it's checking
 * 46)   eight thousand -linked lists- every interval.
 * 47) --hashlimit-srcmask is probably best set to /29 for IPv4 in most
 * 48)   situations where srcip is used for the mask. Not only does this
 * 49)   help reduce collision rates, /29's are often the same family or
 * 50)   local organization. It works fine to treat them in this manner.
 * 51)   The default for this and dstmask is 32 for IPv4 and 128 for IPv6.
 * 52)   If using IPv6, if you do not set this to at least as low as /64 you
 * 53)   are insane.
 * 54) --hashlimit-mode is a fairly straightforward setting. I would suggest
 * 55)   using separate hashes for different ports and destination ips for
 * 56)   most scenarios.
 * 57) --hashlimit-burst defaults to five. This determines the maximum size
 * 58)   of the bucket which gets filled by upto/above.
 * 59) HASHLOG    - The hashlimit declaration for basic logging. It's rather
 * 60)              heavily limited in order to keep us from flooding.
 * 61) HASHCON    - Log mass connection attempts
 * 62) HASHSSH    - Not needed with this current setup, it exists more to
 * 63)              limit getting my auth log slammed than anything.
 * 64) HASHPING   - Originally for pings, now for related/established ICMP
 * 65)              messages in general. To permit ICMP traceroutes, we're
 * 66)              fairly generous.
 * 1) HASHSSH    - Not needed with this current setup, it exists more to
 * 2)              limit getting my auth log slammed than anything.
 * 3) HASHPING   - Originally for pings, now for related/established ICMP
 * 4)              messages in general. To permit ICMP traceroutes, we're
 * 5)              fairly generous.

export DROPTARGET=DROP
 * 1) Here we set some variables that are not 'user modified'.
 * 1) Here we set some variables that are not 'user modified'.
 * 1) For awhile I logged all dropped packets, thus the following, but it's
 * 2) all noise and no signal. I prefer to log actual incidents.
 * 3) if [ $USELOG -eq 1 ] ; then
 * 4)  export DROPTARGET=DRP
 * fi

$IPT -F $IPT -X $IPT -t raw -F $IPT -t raw -X $IPT -P INPUT DROP $IPT -P FORWARD DROP $IPT -P OUTPUT ACCEPT
 * 1) Flush current rules and reset policies.
 * 1) Flush current rules and reset policies.

sleep 3
 * 1) Give the system some time to rest. Can be important if it's been
 * 2) tracking a lot.
 * 1) tracking a lot.


 * 1) Interfaces we trust get a free pass. Don't even track, just accept.
 * 1) Interfaces we trust get a free pass. Don't even track, just accept.

for i in $TRUSTEDFACES do $IPT -t raw -A PREROUTING -i $i -j NOTRACK $IPT -A INPUT -i $i -j ACCEPT done

##################################################################### # Log dropped packets. Only make the chain if logging is on. # This is only really useful to determine if you are under a serious # attack of some sort. ##################################################################### #####################################################################
 * 1) Logging all dropped packets is 99.9% noise, but left in for debugging
 * 2) potential.
 * 3) if [ $USELOG -eq 1 ] ; then
 * 1) if [ $USELOG -eq 1 ] ; then
 * 1) if [ $USELOG -eq 1 ] ; then
 * 1) $IPT -N DRP
 * 2) $IPT -A DRP $HASHLOG -j LOG $LOGLEVEL --log-prefix "IPTables: Dropped: "
 * 3) $IPT -A DRP -j DROP
 * fi


 * 1) Accept from related connections, non-icmp established connections,
 * 2) related connections, and our chosen whitelist. Drop invalid sources
 * 3) and non-unicast packets/sources outright, as well as killing funny
 * 4) business.
 * 1) business.

for i in $WHITELIST do $IPT -A INPUT -p tcp -s $i -j ACCEPT done
 * 1) Iterate through whitelist entries

for i in $BLACKLIST do $IPT -A INPUT -s $i -j $DROPTARGET done
 * 1) Iterate through blacklist entries


 * 1) Iterate through Spamhaus DROP list, if we're using that.
 * 2) for i in $(cat $DROPLIST | grep -i SBL | cut -f 1 -d ';' )
 * do
 * 1)  $IPT -A INPUT -s $i -j DROP
 * 2) done

for i in $SELFIPS do $IPT -A INPUT -s $i -j $DROPTARGET done
 * 1) Iterate through our own ips - drop spoofed entries.

$IPT -A INPUT -m state --state RELATED -j ACCEPT $IPT -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT $IPT -A INPUT -p udp -m state --state ESTABLISHED -j ACCEPT $IPT -A INPUT -m pkttype ! --pkt-type unicast -j $DROPTARGET $IPT -A INPUT -m addrtype ! --src-type UNICAST -j $DROPTARGET $IPT -A INPUT -m addrtype --dst-type BROADCAST -j $DROPTARGET
 * 1) I used to split out tcp traffic and rate limit that. It really only
 * 2) caught exceptionally bad ISPs with legitimate users and aggressive
 * 3) web spiders. DDOS attempts are best mitigated with connlimit and
 * 4) possibly checking for lots of very small packets.
 * 5) I've found that the INVALID state actually spends most of its time
 * 6) dropping legitimate traffic. I can't really recommend it. You can
 * 7) do just as well by being picky with what you accept for NEW
 * 8) connections.
 * 9) Somewhere along the line, pings make established connections now.
 * 10) Annoying, but only minor changes needed to segregate established
 * 11) ICMP traffic. For sure, permit related traffic.
 * 1) Somewhere along the line, pings make established connections now.
 * 2) Annoying, but only minor changes needed to segregate established
 * 3) ICMP traffic. For sure, permit related traffic.
 * 1) Drop garbage sources and destinations.

$IPT -N TCPMESS $IPT -A TCPMESS -p tcp -m statistic --mode random --probability 0.0208 -j REJECT --reject-with tcp-reset $IPT -A TCPMESS -p tcp -m statistic --mode random --probability 0.0211 -j REJECT --reject-with host-unreach $IPT -A TCPMESS -j DROP
 * 1) TCPMESS is our version of the CHAOS target.
 * 2) The primary purpose of this is not even to deceive, but simply to
 * 3) increase the cost of portscanning. This is more of a nuisance than
 * 4) actual security.
 * 1) actual security.
 * 1) $IPT -A TCPMESS -p tcp -m statistic --mode random --probability 0.03 -j DELUDE

$IPT -N TCPIN $IPT -A TCPIN -m recent --update --seconds 900 --hitcount 1 --name flooders -j DROP $IPT -A TCPIN -p tcp $CONNREPORT $HASHCON -j LOG $LOGCON --log-prefix "Hackers: Many Connections: " $IPT -A TCPIN -p tcp $CONNREPORT $HASHCON -m state --state INVALID -j $DROPTARGET $IPT -A TCPIN -p tcp $CONNLIMIT -m recent --set --name flooders $IPT -A TCPIN -p tcp $CONNLIMIT -j LOG $LOGCON --log-prefix "Hackers: Connection Overlimit: " $IPT -A TCPIN -p tcp $CONNLIMIT -j REJECT --reject-with tcp-reset $IPT -A TCPIN -p tcp --tcp-flags SYN,FIN,RST,PSH,URG SYN -j ACCEPT $IPT -A TCPIN -p tcp --tcp-flags SYN,ACK,RST,URG ACK -j ACCEPT $IPT -A TCPIN -p tcp --tcp-flags SYN,FIN,RST,PSH,URG RST -j ACCEPT if [ $USELOG -eq 1 ] ; then $IPT -A TCPIN $HASHLOG -j LOG $LOGLEVEL --log-prefix "IPTables: Invalid Connect: " fi $IPT -A TCPIN -j DROP
 * 1) New TCP Traffic going to valid ports gets sent here.
 * 2) Check connlimit, check new connection rates, log nonsense.
 * 1) Check connlimit, check new connection rates, log nonsense.
 * 1) Sometimes we see new connections from legitimate peoples that
 * 2) somehow escaped proper connection tracking. These are most
 * 3) frequently ACK, followed by RST, followed distantly by ACK PSH.
 * 1) frequently ACK, followed by RST, followed distantly by ACK PSH.

$IPT -A INPUT -p tcp -m multiport --dports $OPENPORTS -j TCPIN for i in $SERVIPS do $IPT -A INPUT -p tcp -d $i -m multiport --dports $SERVPORTS -j TCPIN done for i in $SAFEIPS do $IPT -A INPUT -p tcp -d $i -m multiport --dports $TARPORTS -j TCPIN done $IPT -A INPUT -p tcp -m multiport --dports $TARPORTS -j DROP
 * 1) TCP traffic for our standard ports are not hindered for the purposes
 * 2) of automated blocking of general hijinks.
 * 3) We accept things on open ports, except for our secret (in this case,
 * 4) our SSH port), and we shut people poking around for a few hours.
 * 5) $TARPORTS still gets used here to drop spammers, regardless.
 * 1) our SSH port), and we shut people poking around for a few hours.
 * 2) $TARPORTS still gets used here to drop spammers, regardless.
 * 1) $TARPORTS still gets used here to drop spammers, regardless.
 * 1) $IPT -A INPUT -p tcp ! -d $SAFEIP -m multiport --dports $TARPORTS -j TARPIT

$IPT -A INPUT -p tcp -m recent --update --seconds 3600 --hitcount 1 --name scanners -j TCPMESS $IPT -A INPUT -p tcp -d $SSHIP --dport $SSHPORT $HASHSSH -j ACCEPT $IPT -A INPUT -p tcp -d $SSHIP --dport $SSHPORT $HASHCON -j LOG $LOGCON --log-prefix "Hackers: SSH Flood: " $IPT -A INPUT -p tcp -m recent --set --name scanners $IPT -A INPUT -p tcp -d $SSHIP --dport $SSHPORT -j TCPMESS if [ $USELOG -eq 1 ] ; then $IPT -A INPUT -p tcp $HASHLOG -j LOG $LOGLEVEL --log-prefix "IPTables: Scanner: " fi $IPT -A INPUT -p tcp -j TCPMESS

if [ $ALLOWPING -eq 1 ] ; then ##################################################################### # ICMP Incoming Chain. This looks like it's blocking more than it # actually is - a lot of incoming ICMP messages are RELATED. ##################################################################### $IPT -N ICMP if [ $USELOG -eq 1 ] ; then $IPT -A ICMP -p icmp --fragment $HASHLOG -j LOG $LOGLEVEL --log-prefix "IPTables: ICMP Fragment: " fi $IPT -A ICMP -p icmp --fragment -j DROP $IPT -A ICMP -p icmp --icmp-type echo-request -j ACCEPT # These generally get through first via accepting RELATED # connections, this is simply to be certain. $IPT -A ICMP -p icmp --icmp-type 3 -j ACCEPT $IPT -A ICMP -p icmp --icmp-type 4 -j ACCEPT if [ $USELOG -eq 1 ] ; then $IPT -A ICMP -p icmp $HASHLOG -j LOG $LOGLEVEL --log-prefix "IPTables: ICMP Bad: " fi #####################################################################
 * 1) Overall ICMP Rules
 * 1) Overall ICMP Rules

##################################################################### # Allow icmp packets at the rate given in HASHPING. # You tend not to see ping floods these days, but it may be # interesting enough to log. ##################################################################### $IPT -A INPUT -p icmp -m state --state NEW,ESTABLISHED $HASHPING -j ICMP if [ $USELOG -eq 1 ] ; then $IPT -A INPUT -p icmp -m state --state NEW,ESTABLISHED $HASHLOG -j LOG $LOGLEVEL --log-prefix "IPTables: ICMP Flood: " fi $IPT -A INPUT -p icmp -j DROP ##################################################################### fi

$IPT -N UDPFUN $IPT -A UDPFUN -m statistic --mode random --probability 0.02 -j REJECT --reject-with proto-unreach $IPT -A UDPFUN -m statistic --mode random --probability 0.0202 -j REJECT --reject-with host-unreach $IPT -A UDPFUN -j DROP for i in $UDPIPS do $IPT -A INPUT -p udp -d $i -m multiport --dports $UDPPORTS -j ACCEPT done $IPT -A INPUT -p udp -j UDPFUN
 * 1) UDP Rules.
 * 2) Not running DNS, so, let's mess with scanners!
 * 3) Probability in the .03-.04 range is apparently ideal.
 * 4) If you are more concerned about confusing scans of your network as
 * 5) opposed to scans of a single IP, using proto-unreach liberally can
 * 6) confuse protocol scans.
 * 7) Note, this blocks standard traceroutes. ICMP traceroutes work,
 * 8) however, which Windows falls back on and *nix can use via the -I
 * 9) switch.
 * 1) Note, this blocks standard traceroutes. ICMP traceroutes work,
 * 2) however, which Windows falls back on and *nix can use via the -I
 * 3) switch.
 * 1) Logging this is mostly noise.
 * 2) if [ $USELOG -eq 1 ] ; then
 * 3)  $IPT -A UDPFUN $HASHLOG -j LOG $LOGLEVEL --log-prefix "IPTables: UDP: "
 * fi
 * 1) UDP rules -must- come after dropping spoofed addresses.
 * 1) UDP rules -must- come after dropping spoofed addresses.


 * 1) This script was developed by Vekseid at
 * 2) http://hexwiki.com/wiki/Iptables_(1.4)/firewall.sh?action=raw
 * 3) - vek@vekseid.com
 * 1) - vek@vekseid.com