Take the 2-minute tour ×
Server Fault is a question and answer site for system and network administrators. It's 100% free, no registration required.

I'm sure Linux sysadmins are quite familiar with iptables, the userland interface to the netfilter packet-filtering framework.

Now, this "Question" is meant to be a Community Wiki for collecting together various bits-n-pieces of iptables wisdom. Nothing is too common or too obscure. Post anything you know that would help others make the most of iptables.

share|improve this question

15 Answers 15

Using whitelist and blacklist with iptables



echo 'Clearing all rules'
iptables -F

## Whitelist

for x in `grep -v ^# $WHITELIST | awk '{print $1}'`; do
        echo "Permitting $x..."
        $IPTABLES -A INPUT -t filter -s $x -j ACCEPT

## Blacklist

for x in `grep -v ^# $BLACKLIST | awk '{print $1}'`; do
        echo "Denying $x..."
        $IPTABLES -A INPUT -t filter -s $x -j DROP

Script to open ports

ALLOWEDTCP="80 3128 3784"
ALLOWEDUDP="3128 3784"

## Permitted Ports

for port in $ALLOWEDTCP; do
       echo "Accepting port TCP $port..."
       $IPTABLES -A INPUT -t filter -p tcp --dport $port -j ACCEPT

for port in $ALLOWEDUDP; do
        echo "Accepting port UDP $port..."
        $IPTABLES -A INPUT -t filter -p udp --dport $port -j ACCEPT

Blocking portscan

# Attempt to block portscans
# Anyone who tried to portscan us is locked out for an entire day.
iptables -A INPUT   -m recent --name portscan --rcheck --seconds 86400 -j DROP
iptables -A FORWARD -m recent --name portscan --rcheck --seconds 86400 -j DROP

# Once the day has passed, remove them from the portscan list
iptables -A INPUT   -m recent --name portscan --remove
iptables -A FORWARD -m recent --name portscan --remove

# These rules add scanners to the portscan list, and log the attempt.
iptables -A INPUT   -p tcp -m tcp --dport 139 -m recent --name portscan --set -j LOG --log-prefix "Portscan:"
iptables -A INPUT   -p tcp -m tcp --dport 139 -m recent --name portscan --set -j DROP

iptables -A FORWARD -p tcp -m tcp --dport 139 -m recent --name portscan --set -j LOG --log-prefix "Portscan:"
iptables -A FORWARD -p tcp -m tcp --dport 139 -m recent --name portscan --set -j DROP

Spoofed/Invalid packets

# Reject spoofed packets
# These adresses are mostly used for LAN's, so if these would come to a WAN-only server, drop them.
iptables -A INPUT -s -j DROP
iptables -A INPUT -s -j DROP
iptables -A INPUT -s -j DROP
iptables -A INPUT -s -j DROP

iptables -A INPUT -s -j DROP
iptables -A INPUT -d -j DROP
iptables -A INPUT -s -j DROP
iptables -A INPUT -d -j DROP
iptables -A INPUT -s -j DROP
iptables -A INPUT -d -j DROP
iptables -A INPUT -d -j DROP
iptables -A INPUT -d -j DROP

# Drop all invalid packets
iptables -A INPUT -m state --state INVALID -j DROP
iptables -A FORWARD -m state --state INVALID -j DROP
iptables -A OUTPUT -m state --state INVALID -j DROP

Block Smurf attacks

# Stop smurf attacks
iptables -A INPUT -p icmp -m icmp --icmp-type address-mask-request -j DROP
iptables -A INPUT -p icmp -m icmp --icmp-type timestamp-request -j DROP
iptables -A INPUT -p icmp -m icmp -j DROP

# Drop excessive RST packets to avoid smurf attacks
iptables -A INPUT -p tcp -m tcp --tcp-flags RST RST -m limit --limit 2/second --limit-burst 2 -j ACCEPT

Block ICMP (aka ping)

# Don't allow pings through
iptables -A INPUT -p icmp -m icmp --icmp-type 8 -j DROP
share|improve this answer
Consider adding a comment to the "spoofed" comments, so that less experienced users know why the source addresses are considered spoofed (...when arriving on a wan interface). –  3molo Mar 10 '11 at 15:07
Good call :-). Done. –  Bart De Vos Mar 10 '11 at 16:11
wait. Isn't Block ICMP (aka ping) line redundant to Block smurf attack line: iptables -A INPUT -p icmp -m icmp -j DROP ? –  Stann Mar 31 '11 at 3:14
also - here is the explanation for many items described here: newartisans.com/2007/09/neat-tricks-with-iptables.html –  Stann Mar 31 '11 at 7:01
Re the whitelist script: Theres iptables on line 8, and then $IPTABLES later on. Is it enough just to use iptables everywhere? Otherwise I suppose you'd need to assign something like IPTABLES=/sbin/iptables right? –  UpTheCreek Apr 25 '13 at 9:34

Optimize netfilter's Performance Using ipset

If you write a lot of similar rules based on mere IP, port, or both, consider using ipset to optimize netfilter's performance.

For example:

iptables -s -j ACCEPT
iptables -s -j ACCEPT
iptables -s -j ACCEPT
... hundreds of similar rules ...
iptables -s -j ACCEPT

This means that a packet with the source address of must first traverse hundreds of rules before it can get its verdict of ACCEPT.

Of course, experienced sysadmins will split the rules by subnet. But that still means hundreds of rules.

ipset to the rescue!

First, define an IP Set of ipmap type:

ipset -N Allowed_Hosts ipmap --network

Then, populate it with the addresses:

for ip in $LIST_OF_ALLOWED_IP; do ipset -A Allowed_Hosts $ip; done

Finally, replace the hundreds of iptables rules above with one rule:

iptables -m set --match-set Allowed_Hosts src -j ACCEPT

When a packet arrives, netfilter will perform a very quick bitmap search for the packet's source (src) IP against the Allowed_Hosts IP Set. All packets coming from will experience one rule. And do believe me that searching a bitmap is at least two order of magnitudes faster than performing hundreds of iptables rule-checking.

ipset is not limited to IP addresses. It can also match based on ports, IP-port tuple, network/subnet addresses, IP-MAC tuple, and so on and so forth. And it can match those criteria as source or destination or a mix of both (in the case of tuples).

And finally, with ipset you can automatically put IP addresses in blacklists/whitelists. These blacklists/whitelists can also 'age', thus automatically deleting the IP address after a configurable amount of time has passed.

Please refer to ipset's man page for more details.


Some Linux distros do not have 'out-of-the-box' support for ipset. Two distros I know that do not support ipset out-of-the-box are Ubuntu and Debian. Running aptitude on them tantalizingly shows that an ipset package is available, but I strongly recommend you to not install that package for two reasons: (1) It's an old version of ipset, and (2) It won't work anyway.

Instead, download ipset's source from its website: http://ipset.netfilter.org/install.html

Alternatively, if you use xtables-addons, ipset is included in its source: http://xtables-addons.sourceforge.net/

Edit: The above is true for Ubuntu 10.04, the latest LTS at the time of writing. Ubuntu 12.04 has built-in support for ipset and/or xtables-addons, in the sense that installing the relevant package properly enables the features. I don't know the situation on Debian, though, having not used it for quite some time...

share|improve this answer
That's a real pity it's not supported by default on Debian and Ubuntu. I thought you were going to list some obsure distros :/ –  UpTheCreek Apr 25 '13 at 7:44
@UpTheCreek I've edited my answer... the 'special note' was applicable during time of posting the answer, but no longer applicable now. –  pepoluan Mar 10 '14 at 12:40

Add comments to your rules:

-m comment --comment "Comments help to read output of iptables -nvL"
share|improve this answer

Block Well-Known TCP Attacks

Add the following rules, preferably in -t raw -A PREROUTING

-p tcp --tcp-flags FIN,SYN FIN,SYN -j DROP
-p tcp --tcp-flags SYN,RST SYN,RST -j DROP
-p tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,PSH,URG -j DROP
-p tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN -j DROP
-p tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -j DROP

The attacks being blocked are, respectively:

  • SYN-FIN attack
  • SYN-RST attack
  • X-Mas attack
  • nmap FIN scan
  • NULLflags attack
  • ALLflags attack

(feel free to edit the names of the attacks above)

share|improve this answer
The -p tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,RST,PSH,ACK,URG -j DROP could be removed, as the -p tcp --tcp-flags FIN,SYN FIN,SYN -j DROP will catch every packet it could block. –  Jean-Philippe Guérard Nov 19 '12 at 14:26

Enabling NAT

  1. echo 1 > /proc/sys/net/ipv4/ip_forward
  2. /sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Step 1 sets the kernel parameter to allow for ip forwarding, step 2 sets up an iptables rule that enables NAT on interface eth0.

share|improve this answer
That won't be persistent through a reboot, will it? You need to edit /etc/sysctl.conf net.ipv4.ip_forward = 1. (Assuming Red Hat or derivative.) –  Aaron Copley Apr 3 '11 at 14:48

Block ICMP attacks

Add the following rules, preferably in -t raw -A PREROUTING

-p icmp -m u32 ! --u32 "4&0x3FFF=0"   -j DROP
-p icmp -m length --length 1492:65535 -j DROP

The first rule blocks all ICMP packets whose "fragmentation flag" is not 0. (ICMP should never be fragmented; they should be carrying small payloads)

The second rule blocks oversized unfragmented ICMP packets.

share|improve this answer

(from my iptables_tricks.txt file, recompiled from a lot of places :P)

Makes iptables wait 15 seconds between new connections from the same IP:

 iptables -A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --update --seconds 15 -j DROP
 iptables -A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --set -j ACCEPT
share|improve this answer
Same, but with counting of attempts: -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 5 --rttl --name SSH -j DROP –  alexm Apr 25 '11 at 6:15

using FireHOL - convenient iptables wrapper

I found it much more intuitive than direct iptables commands. Especially for people with past experience with other firewalls:

FireHOL is an iptables firewall generator producing stateful iptables packet filtering firewalls, on Linux hosts and routers with any number of network interfaces, any number of routes, any number of services served, any number of complexity between variations of the services (including positive and negative expressions).

share|improve this answer
I prefer Shorewall which is actively developed, supports IPv4 and IPv6, and can generate firewalls for other iptables systems. –  BillThor Mar 10 '11 at 16:52
@BillThor sounds like a good tool - thanks –  Ophir Yoktan Mar 10 '11 at 17:28

Another GUI that can be used to configure iptables is Firewall Builder. It lets users create rule elements as objects in a database and then drag-and-drop these objects into a rules editor to create the desired firewall policy. The application then generates a script file with all the iptables commands required to implement the rules.

Unlike some other iptables GUI solutions where you can only manage one iptables configuration at a time, with Firewall Builder you can manage a large number of iptables configurations all from a single application. Firewall Builder runs on Linux, Windows and Mac OS X, has been around for over 10 years and has thousands of active users around the world.

Full Disclosure - I'm the co-founder of NetCitadel which is the company that develops Firewall Builder.

share|improve this answer

IP sets revisited

There is already an answer mentioning IP sets. However, it's rather one-dimensional in that it focuses on the performance gains over classic rules and the fact that IP sets mitigate the problem one has with lots of individual IP address that cannot easily be expressed as a subnet in CIDR notation.

Notation used below

For ipset I will use the notation read by ipset restore and written by ipset save.

Correspondingly for iptables (and ip6tables) rules I will use the notation as read by iptables-restore and written by iptables-save. This makes for a shorter notation and it allows me to highlight potential IPv4-only (prefixed -4) or IPv6-only (prefixed -6) rules.

In some examples we'll divert the packet flow into another chain. The chain is assumed to exist at that point, so the lines to create the chains are not produced (nor is the table name mentioned or the commands COMMIT-ted at the end).

Advanced IP sets

IP sets can do a lot more than was mentioned in the other answer and you should definitely read the IP set documentation (ipset(8)) along with iptables-extensions(8) in addition to this brief entry here.

For example I'll mainly focus on three set types: hash:ip, hash:net and list:set, but there are more than those and they all have valid use cases.

You can for example also match port numbers, not just IP addresses.

Saving and restoring IP sets as with iptables-save and iptables-restore

You can create IP set declarations in bulk and import them by piping them into ipset restore. If you want to make your command more resilient against already existing entries, use ipset -exist restore.

If your rules are in a file called default.set you'd use:

ipset -exist restore < default.set

A file like that can contain entries to create sets and to add entries into them. But generally most of the commands from the command line seem to have a corresponding version in the files. Example (creating a set of DNS servers):

create dns4 hash:ip family inet
create dns6 hash:ip family inet6
# Google DNS servers
add dns4
add dns4
add dns6 2001:4860:4860::8888
add dns6 2001:4860:4860::8844

Here one set is created for IPv4 (dns4) and one for IPv6 (dns6).

Timeouts on IP sets

Timeouts in IP sets can be set as a default per set and also per entry. This is very useful for scenarios where you want to block someone temporarily (e.g. for port-scanning or attempting to brute-force your SSH server).

The way this works is as follows (default during creation of IP sets):

create ssh_loggedon4 hash:ip  family inet  timeout 5400
create ssh_loggedon6 hash:ip  family inet6 timeout 5400
create ssh_dynblock4 hash:ip  family inet  timeout 1800
create ssh_dynblock6 hash:ip  family inet6 timeout 1800

We'll get back to these particular sets below and the rationale as to why they're set the way they are.

If you wanted to set your timeout for a particular IP address, you could simply say:

add ssh_dynblock4 timeout 7200

To block IP for two hours instead of the (set) default half hour.

If you were to look at that with ipset save ssh_dynblock4 after a short while, you'd see something along the lines of:

create ssh_dynblock4 hash:ip family inet hashsize 1024 maxelem 65536 timeout 1800
add ssh_dynblock4 timeout 6954

Timeout caveats

  • timeouts are a feature on any given set. If the set was not created with timeout support you'll receive an error (e.g. Kernel error received: Unknown error -1).
  • timeouts are given in seconds. Use Bash arithmetic expressions to get from minutes to seconds, for example. E.g.: sudo ipset add ssh_dynblock4 timeout $((120*60))

Checking whether an entry exists in a given IP set

Inside of your scripts it can be useful to see whether an entry already exists. This can be achieved with ipset test which returns zero if the entry exists and non-zero otherwise. So the usual checks can be applied in a script:

if ipset test dns4; then
  echo "Google DNS is in the set"

However, in many cases you'll rather want to use the -exist switch to ipset in order to direct it not to complain about existing entries.

Populating IP sets from iptables rules

This, in my opinion, is one of the killer features of IP sets. Not only can you match against the entries of an IP set, you can also add new entries to an existing IP set.

For example in this answer to this question you have:

-A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --update --seconds 15 -j DROP
-A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --set -j ACCEPT

... with the intention to rate-limit connection attempts to SSH (TCP port 22). The used module recent keeps track of recent connection attempts. Instead of the state module, I prefer the conntrack module, however.

# Say on your input chain of the filter table you have
   -A INPUT -i eth+ -p tcp --dport ssh -j SSH
# Then inside the SSH chain you can
# 1. create an entry in the recent list on new connections
   -A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
# 2. check whether 3 connection attempts were made within 2 minutes
#    and if so add or update an entry in the ssh_dynblock4 IP set
-4 -A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock4 src --exist
-6 -A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock6 src --exist
# 3. last but not least reject the packets if the source IP is in our
#    IP set
-4 -A SSH -m set --match-set ssh_dynblock4 src -j REJECT
-6 -A SSH -m set --match-set ssh_dynblock6 src -j REJECT

In this case I am redirecting the flow to the SSH chain such that I don't have to repeat myself with -p tcp --dport ssh for every single rule.

To reiterate:

  • -m set makes iptables aware that we're using switches from the set module (which handles IP sets)
  • --match-set ssh_dynblock4 src tells iptables to match the source (src) address against the named set (ssh_dynblock4)
    • this corresponds to sudo ipset test ssh_dynblock4 $IP (where $IP contains the source IP address for the packet)
  • -j SET --add-set ssh_dynblock4 src --exist adds or updates the source (src) address from the packet into the IP set ssh_dynblock4. If an entry exists (--exist) it will simply be updated.
    • this corresponds to sudo ipset -exist add ssh_dynblock4 $IP (where $IP contains the source IP address for the packet)

If you wanted to match the target/destination address instead, you'd use dst instead of src. Consult the manual for more options.

Sets of sets

IP sets can contain other sets. Now if you followed the article up to here you'll have wondered whether it's possible to combine sets. And of course it is. For the IP sets from above we can create two joint sets ssh_dynblock and ssh_loggedon respectively to contain the IPv4-only and IPv6-only sets:

create ssh_loggedon4 hash:ip  family inet  timeout 5400
create ssh_loggedon6 hash:ip  family inet6 timeout 5400
create ssh_dynblock4 hash:ip  family inet  timeout 1800
create ssh_dynblock6 hash:ip  family inet6 timeout 1800
# Sets of sets
create ssh_loggedon  list:set
create ssh_dynblock  list:set
# Populate the sets of sets
add ssh_loggedon ssh_loggedon4
add ssh_loggedon ssh_loggedon6
add ssh_dynblock ssh_dynblock4
add ssh_dynblock ssh_dynblock6

And the next question that should pop up in your mind is whether this allows us to match and and manipulate IP sets in an IP version-agnostic fashion.

And the answer to that is a resounding: YES! (alas, this wasn't documented explicitly last time I checked)

Consequently the rules from the previous section can be rewritten to read:

-A INPUT -i eth+ -p tcp --dport ssh -j SSH
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
-A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock src --exist
-A SSH -m set --match-set ssh_dynblock src -j REJECT

which is a lot more concise. And yes, this is tried and tested and works like a charm.

Putting it all together: SSH brute-force defense

On my servers I have a script run as a cron job which takes a bunch of host names and resolves those to IP addresses, then feeding it into the IP set for "trusted hosts". The idea is that trusted hosts get more attempts to log into the server and aren't necessarily blocked out for as long as anybody else.

Conversely I have whole countries blocked out from connecting to my SSH server, with the (potential) exception of trusted hosts (i.e. order of rules matters).

However, that is left as an exercise for the reader. Here I'd like to add a neat solution that will use the sets contained in the ssh_loggedon set to allow subsequent connection attempts to be handed through and not be subject to the same scrutiny as the other packets.

It is important to remember the default timeouts of 90 minutes for ssh_loggedon and 30 minutes for ssh_dynblock when looking at the following iptables rules:

-A INPUT -i eth+ -p tcp --dport ssh -j SSH
-A SSH -m set --match-set ssh_loggedon src -j ACCEPT
-A SSH -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
-A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock src --exist
-A SSH -m set --match-set ssh_dynblock src -j REJECT

By now you should ask yourself how the connecting IP address ends up in the ssh_loggedon sub-sets. So read on ...

Bonus: adding the IP you log in with during SSH logon

If you have experimented with sshrc and friends, you'll have learned of its shortcomings. But PAM comes to the rescue. A module named pam_exec.so allows us to invoke a script during SSH logon at a point where we know that the user is admitted in.

In /etc/pam.d/sshd below the pam_env and pam_selinux entries add the following line:

session    optional     pam_exec.so stdout /path/to/your/script

and make sure that your version of the script (/path/to/your/script above) exists and is executable.

PAM uses environment variables to communicate what's going on, so you can use a simple script like this one:

# When called via pam_exec.so ...
if [[ "$PAM_TYPE" == "open_session" ]] && [[ -n "$PAM_RHOST" ]]; then
    [[ "x$PAM_RHOST" != "x${PAM_RHOST//:/}" ]] && SETNAME="${SETNAME}6" || SETNAME="${SETNAME}4"
    ipset -exist add $SETNAME "$PAM_RHOST"

Unfortunately the ipset utility doesn't seem to have the built-in smarts of netfilter. So we need to distinguish between IPv4 and IPv6 IP set when adding our entry. Otherwise ipset will assume we want to add another set to the set of sets, instead of the IP. And of course it's unlikely that there would be a set named after an IP :)

So we check for : in the IP address and append 6 to the set name in such case and 4 otherwise.

The end.

share|improve this answer

Something I do, mainly because of my ignorance of a more elegant solution, is to manually check my Nginx logs every 4 hours and the mail server logs every 2 minutes for excessive access by individual IP's. I run a few scripts together that:

  1. Check the access.log and list off the top 10 IP's organized by how many hits they have to the server
  2. Dump the results into a log file
  3. Have another script look at that log file and ban any IP that has hit the server more than X amount of times over the past X hours
  4. Save my iptables.save

Here's what it looks like:


# This script checks the last 2 minutes of log entries to see if any 
# IP has made over 99 connections

now=$(date +"%m_%d_%Y")

/root/bin/checkBadIPs_mail.sh > /home/ipChecker/ipcheckMAIL_$now.txt
cat /home/ipChecker/ipcheckMAIL_$now.txt | \
    grep " \\(\\([9][9]\\)\\|\\([0-9][0-9][0-9]\\+\\)\\) " | \
    awk '{print $2}' > /home/ipChecker/badMailIPs_$now.sh
sed -i "s/^/\/usr\/local\/sbin\/blockIP /g" /home/ipChecker/badMailIPs_$now.sh
/bin/bash /home/ipChecker/badMailIPs_$now.sh
cat /home/ipChecker/ipcheckMAIL_$now.txt >> /home/ipChecker/ipcheckMAIL_$now.log
rm /home/ipChecker/ipcheckMAIL_$now.txt
rm /home/ipChecker/badMailIPs_$now.sh

One thing that is VERY important to note here is that you NEED to setup a whitelist or you are going to start blocking a lot of authentic IP's from servers that you just receive a lot of email from or in the case of other logs, IP's that just hit your server a lot for legitimate reasons. My whitelist is just built into this script by adding grep pipes right after | grep ']' | that look something like this "grep -v 127.0 |".
You need to take the time to teach your server which high traffic IP's are legit and which aren't. For me this meant that I had to spend the first week or so checking my logs manually every couple of hours, looking up high traffic ip's on iplocation.net and then adding the legit ones like amazon, box.com or even my home/office IP ranges to this whitelist. If you don't you will likely be blocked from your own server or you are going to start blocking legit mail/web servers and cause interruptions in email or traffic.

cat /var/log/mail.log | awk \
    -v d1="$(date --date="-2 min" "+%b %_d %H:%M")" \
    -v d2="$(date "+%b %_d %H:%M")" \
    '$0 > d1 && $0 < d2 || $0 ~ d2' | \
    grep '\[' | grep '\]' | \
    grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | grep -v 127.0 | \
    awk '{print $1}' | sort | uniq -c | sort -n | tail -10
sudo iptables -I INPUT -s $1 -j DROP
sudo bash -c "iptables-save > /etc/network/iptables.save"

Again I know this is crude as hell and there is probably a nice clean efficient protocol that does all of this but I didn't know about it and this thing has been going for a year or two now and keeping the bad guys at bay. The one thing I would very SERIOUSLY recommend is that you have a proxy or another server in the wings that you can use to access your main server.. The reason being is that if you are doing web development one day out of the blue and you ping your self 2000 times in 5 hours for some testing you could get blocked with no way back in except for a proxy.

You can see that in checkBadIPs.sh I've put grep -v 127.0 and in my actual files I have a ton of ignore rules for my own IP's and other trusted IP ranges but sometimes your IP changes, you forget to update and then you're locked out of your own server.

Anyways, hope that helps.


I have changed things a little bit so that now instead of checking every couple hours I have some logs checked every 2 minutes, mainly my ssh auth log and the mail log as they were getting pounded :(.

I setup specific scripts for each log file although it would be easy enough from the manual script I use myself when wanting to inspect logs. Looks like this:


log=$1 time=$2

cat /var/log/${log} | awk \
    -v d1="$(date --date="-${time} min" "+%b %_d %H:%M")" \
    -v d2="$(date "+%b %_d %H:%M")" \
    '$0 > d1 && $0 < d2 || $0 ~ d2' | \
    grep '\[' | grep '\]' | \
    grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | \
    sort | uniq -c | sort -n | tail -10

This requires 2 inputs when run, the log file you want to scan and how far back into the past you want to scan.

So if I wanted to check mail.log for the ip counts say 75 minutes into the past I would run:

$ sudo script.sh mail.log 75
share|improve this answer
A quick bit of googling found me this, which seems to do what I described above: cyberciti.biz/faq/iptables-connection-limits-howto –  unc0nnected Aug 22 '14 at 16:20
Nice! I'd use ipset rather than elongating the iptables chain, but the idea is great and I think I'll apply them to my production servers. Thanks for sharing! –  pepoluan Aug 23 '14 at 14:48
I just read about ipset for the first time the other day and was gleefully delighted to learn about what it does.. I'm a little scared to implement it just because I'll probably muck it up in the beginning and shut the server down but it's on my list of things to learn here. That being said my iptable chain is probably around 30-40 items and only gets 1 new one every day or two so it's not at a point that I'm overly worried. –  unc0nnected Aug 24 '14 at 19:05
Have you considered fail2ban? –  chicks Dec 12 '14 at 7:55

log outgoing connection with uid

iptables -A OUTPUT -m state --state NEW -m tcp -p tcp -m limit --limit 5/m -j LOG --log-uid --log-prefix="outgoing connection: "

port/connection forwarding:

iptables -A PREROUTING -t nat -i eth1 -p tcp --dport 80 -j DNAT --to
iptables -A INPUT -p tcp -m state --state NEW --dport 80 -i eth1 -j ACCEPT
share|improve this answer
I prefer the NFLOG target. It allows a longer line prefix and the user mode daemon can log to databases as well. –  0xC0000022L Mar 20 at 11:18

Match several interface names with a wildcard

Example: you have eth0 and eth1 and would like to allow any traffic between them?

iptables -A FORWARD -i eth+ -o eth+ -j ACCEPT

I've used this in the past in order to match the veth<something> interfaces dynamically created and named by LXC. So I can match all at once with veth+.

I also intentionally named some interfaces _<something> in order to match against _+.

share|improve this answer
# The following iptables/ip6tables configurations have
# been kindly shared with us from ArckWiki. There are
# a few additions apart from what has been defined.
#=================Flush current definitions==============
    iptables -F
    ip6tables -F
    iptables -X
    ip6tables -X

#----Define chains for opened ports
    iptables -N TCP
    ip6tables -N TCP
    iptables -N UDP
    ip6tables -N UDP

#----Setting up the filter table for NAT
#   iptables -N fw-interfaces
#   ip6tables -N fw-interfaces
#   iptables -N fw-open
#   ip6tables -N fw-open

#================Default Chain reactions=================
#----Default FORWARD reaction
    iptables -P FORWARD DROP
    ip6tables -P FORWARD DROP

#----Default OUTPUT reaction
    iptables -P OUTPUT ACCEPT
    ip6tables -P OUTPUT ACCEPT

    iptables -A INPUT -m string --algo bm --hex-string '|28 29 20 7B|' -j DROP
    ip6tables -A INPUT -m string --algo bm --hex-string '|28 29 20 7B|' -j DROP

#----Default INPUT reaction
    iptables -P INPUT DROP
    ip6tables -P INPUT DROP
#----Drop spoofing packets
    iptables -A INPUT -i eth0 -s -j DROP
    iptables -A INPUT -i wlan0 -s -j DROP
    iptables -A INPUT -i wlan1 -s -j DROP
    iptables -A INPUT -s -j DROP
    iptables -A INPUT -s -j DROP
    iptables -A INPUT -s -j DROP
    iptables -A INPUT -s -j DROP
    iptables -A INPUT -d -j DROP
    iptables -A INPUT -s -j DROP
    iptables -A INPUT -d -j DROP
    iptables -A INPUT -s -j DROP
    iptables -A INPUT -d -j DROP
    iptables -A INPUT -d -j DROP
    iptables -A INPUT -d -j DROP

#================Ping rate limiting globally=============
    iptables -A INPUT -p icmp --icmp-type 8 -m limit --limit 30/min --limit-burst 8 -j ACCEPT
    ip6tables -A INPUT -p icmpv6 --icmpv6-type 8 --match limit --limit-burst 8 -j ACCEPT
    iptables -A INPUT -p icmp --icmp-type 8 -j DROP
    ip6tables -A INPUT -p icmpv6 --icmpv6-type 8 -j DROP

#----flooding RST packets, smurf attack Rejection
    iptables -A INPUT -p tcp -m tcp --tcp-flags RST RST -m limit --limit 2/second --limit-burst 2 -j ACCEPT
    ip6tables -A INPUT -p tcp -m tcp --tcp-flags RST RST -m limit --limit 2/second --limit-burst 2 -j ACCEPT

#----Bogus packet DROP
    iptables -A INPUT -p tcp -m tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
    ip6tables -A INPUT -p tcp -m tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
    iptables -A INPUT -p tcp -m tcp --tcp-flags SYN,RST SYN,RST -j DROP
    ip6tables -A INPUT -p tcp -m tcp --tcp-flags SYN,RST SYN,RST -j DROP

#================RELATED,ESTABLISHED reaction============
    iptables -A INPUT --match conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    ip6tables -A INPUT --match conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

#================unfetered loopback======================
    iptables -A INPUT -i lo -j ACCEPT
    ip6tables -A INPUT -i lo -j ACCEPT

#================INVALID catagory of packets=============
    iptables -A INPUT -p 41 -j ACCEPT
    iptables -A INPUT --match conntrack --ctstate INVALID -j DROP
    ip6tables -A INPUT --match conntrack --ctstate INVALID -j DROP

#================IPv6 reactions and definitions==========
    ip6tables -A INPUT -s fe80::/10 -p icmpv6 -j ACCEPT
    ip6tables -t raw -A PREROUTING -p icmpv6 -s fe80::/10 -j ACCEPT
    ip6tables -t raw -A PREROUTING --match rpfilter -j ACCEPT
    ip6tables -t raw -A PREROUTING -j DROP
#=======Acceptable INVALIDs and a curteous response======
    iptables -A INPUT -p udp --match conntrack --ctstate NEW -j UDP
    ip6tables -A INPUT -p udp --match conntrack --ctstate NEW -j UDP
    iptables -A INPUT -p tcp --syn --match conntrack --ctstate NEW -j TCP
    ip6tables -A INPUT -p tcp --syn --match conntrack --ctstate NEW -j TCP

#================Defining the TCP and UDP chains
#            Notes for port open definitions            #
# It is important to note that this should be config-   #
# ured differently if you're providing any routing      #
# activity for any purpose. it is up to you to actively #
# define what suites your needs to get the job done.    #
# In this example, I'm exempting IPv6 from being able   #
# to interact with SSH protocols for two reasons. The   #
# first is because it is generally easier and more com- #
# for internal networks to be deployed with IPv4. The   #
# second reason is, IPv6 can be deployed globally.      #
#----SSH configured for eth0
    iptables -A TCP -i eth0 -p tcp --dport ssh -j ACCEPT

#!---Blocking SSH interactions in IPv6
    ip6tables -A TCP -p tcp --dport ssh -j DROP

#!---Leave commented for end service device
#   iptables -A TCP -p tcp --dport 80 -j ACCEPT
#   ip6tables -A TCP -p tcp --dport 80 -j ACCEPT
#   iptables -A TCP -p tcp --dport 443 -j ACCEPT
#   ip6tables -A TCP -p tcp --dport 443 -j ACCEPT
#!---Uncomment for remote service to this device
#   iptables -A TCP -p tcp --dport 22 -j ACCEPT
#   ip6tables -A TCP -p tcp --dport 22 -j ACCEPT
#!---Uncomment if you're providing routing services
#   iptables -A UDP -p udp 53 -j ACCEPT
#   ip6tables -A UDP -p udp 53 -j ACCEPT
#=================Tricking port scanners=================
#----SYN scans
    iptables -I TCP -p tcp --match recent --update --seconds 60 --name TCP-PORTSCAN -j DROP
    ip6tables -I TCP -p tcp --match recent --update --seconds 60 --name TCP-PORTSCAN -j DROP
    iptables -A INPUT -p tcp --match recent --set --name TCP-PORTSCAN -j DROP
    ip6tables -A INPUT -p tcp --match recent --set --name TCP-PORTSCAN -j DROP

#----UDP scans
    iptables -I UDP -p udp --match recent --update --seconds 60 --name UDP-PORTSCAN -j DROP
    ip6tables -I UDP -p udp --match recent --update --seconds 60 --name UDP-PORTSCAN -j DROP
    iptables -A INPUT -p udp --match recent --set --name UDP-PORTSCAN -j DROP
    ip6tables -A INPUT -p udp --match recent --set --name UDP-PORTSCAN -j DROP

#----For SMURF attack protection
    iptables -A INPUT -p icmp -m icmp --icmp-type address-mask-request -j DROP
    iptables -A INPUT -p icmp -m icmp --icmp-type timestamp-request -j DROP
    iptables -A INPUT -p icmp -m limit --limit 2/second --limit-burst 2 -j ACCEPT
    ip6tables -A INPUT -p icmpv6 -m limit --limit 2/second --limit-burst 2 -j ACCEPT

#----Ending all other undefined connections
    iptables -A INPUT -j DROP
    ip6tables -A INPUT -j DROP

#=======Defining the IN_SSH chain for bruteforce of SSH==
#!---I've elected to keep IPv6 out of this realm for
#!---ease of use
    iptables -N IN_SSH
    iptables -A INPUT -p tcp --dport ssh --match conntrack --ctstate NEW -j IN_SSH
    iptables -A IN_SSH --match recent --name sshbf --rttl --rcheck --hitcount 3 --seconds 10 -j DROP
    iptables -A IN_SSH --match recent --name sshbf --rttl --rcheck --hitcount 4 --seconds 1800 -j DROP
    iptables -A IN_SSH --match recent --name sshbf --set -j ACCEPT
    iptables -A INPUT -p tcp --dport ssh -m conntrack --ctstate NEW -j IN_SSH

#==================Setting up a NAT gateway==============
#                                                       #
# I commented this half out because it's not something  #
# that will apply to all setups. Make note of all par-  #
# tinate interfaces and what exactly is going on.       #
#                                                       #
#----Setting up the FORWARD chain
#   iptables -A FORWARD --match conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
#   ip6tables -A FORWARD --match conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
#----Defining the fw-interfaces/open chains for FORWARD
#   iptables -A FORWARD -j fw-interfaces
#   ip6tables -A FORWARD -j fw-interfaces
#   iptables -A FORWARD -j fw-open
#   ip6tables -A FORWARD -j fw-open
#   iptables -A FORWARD -j DROP # Should be REJECT. But, fuck them
#   ip6tables -A FORWARD -j DROP
#   iptables -P FORWARD DROP
#   ip6tables -P FORWARD DROP
#----Setting up the nat table
#   iptables -A fw-interfaces -i ### -j ACCEPT
#   ip6tables -A fw-interfaces -i ### -j ACCEPT
#   iptables -t nat -A POSTROUTING -s w.x.y.z/S -o ppp0 -j MASQUERADE
#   ip6tables -t nat -A POSTROUTING -s fe::/10 -o ppp0 -j MASQUERADE
#----The above lines should be repeated specifically for EACH interface
#----Setting up the PREROUTING chain
#                             #
# The PREROUTING chain will redirect either port      #
# targets to be redirected. This can also redirect    #
# traffic inbound to your network from the gateway    #
# to this machine. This can be useful if you're using #
# a honeypot or have any service within your network  #
# that you want to be pointed to a specific device.   #
#                             #
#----SSH honeypot server
#   iptables -A fw-open -d HONEYPOT_IP -p tcp --dport 22 -j ACCEPT
#   ip6tables -A fw-open -d HONEYPOT_IP -p tcp --dport 22 -j ACCEPT
#----With intuition, you can configure the above to also direct specific
#----requests to other devices providing those services. The bellow will
#----be for a squid server
#   iptables -A fw-open -d SQUID_IP -p tcp --dport 80 -j ACCEPT
#   ip6tables -A fw-open -d SQUID_IP -p tcp --dport 80 -j ACCEPT
#   iptables -t nat -A PREROUTING -i ppp0 -p tcp --dport 8000 -j DNAT --to SQUID_IP
#   ip6tables -t nat -A PREROUTING -i ppp0 -p tcp --dport 8000 -j DNAT --to SQUID_IP
#===============Declare configurations=================
    iptables -nvL
    ip6tables -nvL
share|improve this answer
With all the shouting, I can't grasp the message of your post. –  Deer Hunter Oct 29 '14 at 7:27

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.