IPtables whitelist dynamic IP by hostname

Solution 1:

iptables works on IP addresses, not on hostnames. You can use hostnames as arguments, but they will be resolved at the time the command is entered. Doing a DNS lookup for each passing packet would be much too slow.

Your idea to adjust the rules is therefore the only approach. This can be either at a regular schedule, controlled by a program like systemd or cron, or better if you can manage to receive a notification whenever the IP address changes.

You don't have to store the old address, just make an iptables chain for your rule and replace the rule. See the -R option to iptables. To have a rule to replace on the first check, just add a dummy rule so that there will be a rule to replace when the first check runs.

You can also avoid the extra chain and replace a rule at a specific position in INPUT or FORWARD, but that is much more work to maintain, as the position number changes whenever you add or delete rules.

Solution 2:

The way I do this is:

  • Run a script every x minutes from crontab to update an "ipset"
  • Have IPtables use the ipset

Assuming you have only 1 IP address in this ipset, the following script would do:

#!/bin/bash
# Update ipset to let my dynamic IP in

set=whitelist
host=myhost.dynamic.example.com

me=$(basename "$0")

ip=$(dig +short $host)

if [ -z "$ip" ]; then
    logger -t "$me" "IP for '$host' not found"
    exit 1
fi

# make sure the set exists
ipset -exist create $set hash:ip

if ipset -q test $set $host; then
    logger -t "$me" "IP '$ip' already in set '$set'."
else 
    logger -t "$me" "Adding IP '$ip' to set '$set'."
    ipset flush $set
    ipset add $set $host
fi

In crontab I call this script every 5 minutes :

*/5 * * * * root /usr/local/bin/ipset-update-dyn

In iptables, the rule using the ipset looks like this :

-A INPUT -p tcp -m set --match-set whitelist src -m state --state NEW -j ACCEPT