How can I check if an iptables rule already exists?
There is a new -C --check
option in recent iptables versions.
# iptables -C INPUT -p tcp --dport 8080 --jump ACCEPT
iptables: Bad rule (does a matching rule exist in that chain?).
# echo $?
1
# iptables -A INPUT -p tcp --dport 8080 --jump ACCEPT
# iptables -C INPUT -p tcp --dport 8080 --jump ACCEPT
# echo $?
0
For older iptables versions, I would use Garrett suggestion :
# iptables-save | grep -- "-A INPUT -p tcp -m tcp --dport 8080 -j ACCEPT"
The new -C
option is not satisfactory, because it is open to a time-of-check-to-time-of-use (TOCTTOU) race condition. If two processes try to add the same rule at around the same time, -C
will not protect them from adding it twice.
So, it is really no better than the grep
solution. An accurate text processing job over the output of iptables-save
can work as reliably as -C
, since that output is a reliable snapshot of the state of the tables.
What is needed is an --ensure
option which atomically checks and adds a rule only if it doesn't already exist. Moreover, it would be nice if the rule is moved to the correct position where a new rule would be inserted if it did not exist already (--ensure-move
). For instance if iptables -I 1
is used to create a rule at the head of a chain, but that rule exists already in the seventh position, then the existing rule should move to the
first position.
Without these features, I think a feasible workaround is to write a shell script loop based on this pseudo code:
while true ; do
# delete all copies of the rule first
while copies_of_rule_exist ; do
iptables -D $RULE
done
# now try to add the rule
iptables -A $RULE # or -I
# At this point there may be duplicates due to races.
# Bail out of loop if there is exactly one, otherwise
# start again.
if exactly_one_copy_of_rule_exists ; then
break;
fi
done
This code could spin around; it does not guarantee that two or more racers will be out within a fixed number of iterations. Some randomized exponential backoff sleeps could be added to help with that.
This may seem a bit backwards, but it works for me - Try deleting the rule first.
iptables -D INPUT -s xxx.xxx.xxx.xxx -j DROP;
You should get a message similiar to:
iptables: Bad rule (does a matching rule exist in that chain?)
Then simply add your rule as normal:
iptables -A INPUT -s xxx.xxx.xxx.xxx -j DROP;