I have written a shell script to read a file which consists of IP addresses and then block them with the help of iptables. It works fine, But when I run the script for second time, it writes the rule again (duplicated). I want it to check if IP is already blocked then ignore it otherwise block. here is the script:
#!/bin/bash
ipadds="/home/asad/Downloads/blacklist"
dropit=$(grep -Ev "^#" $ipadds)
for i in $dropit; do
iptables -A INPUT -s $i -j DROP
iptables -A FORWARD -s $i -j DROP
done
Output after first time script run:
root@ubuntu:/home/asad/Downloads# iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP all -- 192.168.248.2 anywhere
DROP all -- 192.168.232.20 anywhere
DROP all -- 192.168.232.5 anywhere
DROP all -- 192.168.232.190 anywhere
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DROP all -- 192.168.248.2 anywhere
DROP all -- 192.168.232.20 anywhere
DROP all -- 192.168.232.5 anywhere
DROP all -- 192.168.232.190 anywhere
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Output after second time script run:
root@ubuntu:/home/asad/Downloads# iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP all -- 192.168.248.2 anywhere
DROP all -- 192.168.232.20 anywhere
DROP all -- 192.168.232.5 anywhere
DROP all -- 192.168.232.190 anywhere
DROP all -- 192.168.248.2 anywhere
DROP all -- 192.168.232.20 anywhere
DROP all -- 192.168.232.5 anywhere
DROP all -- 192.168.232.190 anywhere
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DROP all -- 192.168.248.2 anywhere
DROP all -- 192.168.232.20 anywhere
DROP all -- 192.168.232.5 anywhere
DROP all -- 192.168.232.190 anywhere
DROP all -- 192.168.248.2 anywhere
DROP all -- 192.168.232.20 anywhere
DROP all -- 192.168.232.5 anywhere
DROP all -- 192.168.232.190 anywhere
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
how to avoid this duplication? any help please
#!/bin/bash
ipadds=/home/asad/Downloads/blacklist
grep -v "^#" $ipadds | while read i; do
if ! iptables -nL INPUT | grep -Fq "$i" ; then
iptables -A INPUT -s "$i" -j DROP
iptables -A FORWARD -s "$i" -j DROP
fi
done
Or (maybe this makes little difference to you)
#!/bin/bash
ipadds=/home/asad/Downloads/blacklist
while read i; do
if ! iptables -nL INPUT | grep -Fq "$i" ; then
iptables -A INPUT -s "$i" -j DROP
iptables -A FORWARD -s "$i" -j DROP
fi
done < <(grep -v "^#" $ipadds)
Please note that the -E
flag for grep
is not needed in this case. It is crucial to pass the -n
flag to iptables -L
in order to get IP’s and not hostnames; it also improves performance. I assume that your INPUT and FORWARD chains are kept in sync, therefore I only check one of them. If this is not the case, one should check both, of course.
The exact semantics of the above script is: “don’t insert a new candidate IP if it’s already mentioned anywhere in the INPUT chain” which is a little different from “... if it’s already blocked”. A more efficient solution based on iptables -C
would look for a particular rule, but not for other mentions of the IP in the chain. I can think of scenarios where either approach is preferable.
If you have lots of IP’s in your iptables chains, scanning a whole chain to check if a candidate IP is already there might be inefficient. If this becomes a problem, there are various ways to build an external index for more efficient IP lookups, but OTOH the whole system would become more difficult to maintain, so I’d experiment without external indexes for a start.