Iptables (1.4)/firewall.sh

From Hexwiki
Revision as of 05:30, 3 May 2014 by Vekseid (talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
  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. 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.

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. 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. 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.

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 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.

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. Here we set some variables that are not 'user modified'.

export DROPTARGET=DROP

  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
  5. fi
  1. Flush current rules and reset policies.

$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. Give the system some time to rest. Can be important if it's been
  2. tracking a lot.

sleep 3

  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

  1. Logging all dropped packets is 99.9% noise, but left in for debugging
  2. potential.
  3. if [ $USELOG -eq 1 ] ; then
 #####################################################################
 # 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.
 #####################################################################
# $IPT -N DRP
# $IPT -A DRP $HASHLOG -j LOG $LOGLEVEL --log-prefix "IPTables: Dropped: "
# $IPT -A DRP -j DROP
 #####################################################################
  1. 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. Iterate through whitelist entries

for i in $WHITELIST do

 $IPT -A INPUT -p tcp -s $i -j ACCEPT

done

  1. Iterate through blacklist entries

for i in $BLACKLIST do

 $IPT -A INPUT -s $i -j $DROPTARGET

done

  1. Iterate through Spamhaus DROP list, if we're using that.
  2. for i in $(cat $DROPLIST | grep -i SBL | cut -f 1 -d ';' )
  3. do
  4. $IPT -A INPUT -s $i -j DROP
  5. done
  1. Iterate through our own ips - drop spoofed entries.

for i in $SELFIPS do

 $IPT -A INPUT -s $i -j $DROPTARGET

done

  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.

$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

  1. Drop garbage sources and destinations.

$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. 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.

$IPT -N TCPMESS

  1. $IPT -A TCPMESS -p tcp -m statistic --mode random --probability 0.03 -j DELUDE

$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. New TCP Traffic going to valid ports gets sent here.
  2. Check connlimit, check new connection rates, log nonsense.

$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

  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.

$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. 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.

$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

  1. $IPT -A INPUT -p tcp ! -d $SAFEIP -m multiport --dports $TARPORTS -j TARPIT

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

$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

  1. Overall ICMP Rules

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

  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.

$IPT -N UDPFUN

  1. Logging this is mostly noise.
  2. if [ $USELOG -eq 1 ] ; then
  3. $IPT -A UDPFUN $HASHLOG -j LOG $LOGLEVEL --log-prefix "IPTables: UDP: "
  4. fi

$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

  1. UDP rules -must- come after dropping spoofed addresses.

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. This script was developed by Vekseid at
  2. http://hexwiki.com/wiki/Iptables_(1.4)/firewall.sh?action=raw
  3. - vek@vekseid.com