Using DynDNS to solve the problem of keeping a firewall open to remote users at changeable IP addresses

DynDNS

Image by Dutch Glory via Flickr

EDIT (January 15, 2012):  I now recommend the use of No-IP or some other free service in preference to DynDNS for new users — see DynDNS (mostly) discontinues free DNS service for details. If you do use a different service I believe that the method shown here will still work, except of course you’ll be using addresses that don’t have “dyndns” in them.

(Updated July 1, 2011 to include rudimentary test for string returned that doesn’t contain an actual IP address)

One problem faced by Asterisk users (and probably also users of other software PBX’s) is that you want to secure your system by not opening ports up to the entire Internet, but if you have remote users (users not on the same local network as your Asterisk server) you need to make an exception for them to allow them to penetrate your firewall.  If all your external users have fixed IP addresses, it’s not a problem — you simply add a specific rule in your firewall to permit access from each user’s IP address.  However, if their ISP changes their IP address frequently, or if they are using a softphone on a laptop computer, then you can’t just assume they will constantly be at same IP.  And if one of those users happens to be your boss or your mother, they are not going to be happy if they can’t use the phone until they make contact with you, and you enter their new IP address in the firewall.  And they’re probably not going to be real happy if they have to go to a web site or take some other action before they can make and receive calls.

This solution will work for many users in this situation, provided that you are using the iptables firewall. Again, the goal is to keep all your ports closed to outsiders, except for your authorized users. But if you can get each user to set up a DynDNS account and then set their router to do the DynDNS updates (as described here — just read the parts about setting up a DynDNS account and router configuration) OR failing that, you can get them to install a software DynDNS client on their computer (which is a poorer choice because the computer has to be on for updates to occur), then you can run a script on your Asterisk box every five minutes to check to see if their IP address has changed, and if so, update iptables. I have one script that is called as a cron job every five minutes, and looks like this:

#!/bin/bash
/root/firewall-dynhosts.sh someaddress.dyndns-ip.com
/root/firewall-dynhosts.sh someotheraddress.dyndns-ip.com
/root/firewall-dynhosts.sh someaddress.dyndns-home.com

In other words it has one line for each DynDNS host I want to check. For each host it calls a script named firewall-dynhosts.sh which in turn contains this:

#!/bin/bash
# filename: firewall-dynhosts.sh
#
# A script to update iptable records for dynamic dns hosts.
# Written by: Dave Horner (http://dave.thehorners.com)
# Released into public domain.
#
# Run this script in your cron table to update ips.
#
# You might want to put all your dynamic hosts in a sep. chain.
# That way you can easily see what dynamic hosts are trusted.
#
# create the chain in iptables.
# /sbin/iptables -N dynamichosts
# insert the chain into the input chain @ the head of the list.
# /sbin/iptables -I INPUT 1 -j dynamichosts
# flush all the rules in the chain
# /sbin/iptables -F dynamichosts

HOST=$1
HOSTFILE=”/root/dynhosts/host-$HOST”
CHAIN=”dynamichosts” # change this to whatever chain you want.
IPTABLES=”/sbin/iptables”

# check to make sure we have enough args passed.
if [ "${#@}" -ne "1" ]; then
echo “$0 hostname”
echo “You must supply a hostname to update in iptables.”
exit
fi

# lookup host name from dns tables
IP=`/usr/bin/dig +short $HOST | /usr/bin/tail -n 1`
if [ "${#IP}" = "0" ]; then
echo “Couldn’t lookup hostname for $HOST, failed.”
exit
fi

if [ ! `expr "$IP" : '\([1-9]\)’` ]; then
echo “Did not return valid IP address, failed.”
exit
fi

OLDIP=”"
if [ -a $HOSTFILE ]; then
OLDIP=`cat $HOSTFILE`
# echo “CAT returned: $?”
fi

# has address changed?
if [ "$OLDIP" == "$IP" ]; then
echo “Old and new IP addresses match.”
exit
fi

# save off new ip.
echo $IP>$HOSTFILE

echo “Updating $HOST in iptables.”
if [ "${#OLDIP}" != "0" ]; then
echo “Removing old rule ($OLDIP)”
`$IPTABLES -D $CHAIN -s $OLDIP/32 -j ACCEPT`
fi
echo “Inserting new rule ($IP)”
`$IPTABLES -A $CHAIN -s $IP/32 -j ACCEPT`

echo “Changing rule in /etc/sysconfig/iptables”
sed -i “s/-A\sdynamichosts\s-s\s$OLDIP\s-j\sACCEPT/-A dynamichosts -s $IP -j ACCEPT/g” /etc/sysconfig/iptables

echo “Sending e-mail notification”
`echo “This is an automated message – please do not reply. The address of dynamic host $HOST has been changed from $OLDIP to $IP. You may need to change the dynamichosts chain in Webmin’s Linux Firewall configuration.” | mail -s “IP address of dynamic host changed on machine name recipient@someaddress.com,anotherrecipient@someaddress.net`

As always, copy and paste the above script, so you can see where the line breaks are really supposed to be (the last line in particular is quite long, and will likely be broken up into four or five lines on the screen).

Note that prior to the first run of the script you will need to run the three commented-out commands shown near the top of the script, right after “create the chain in iptables”, to create the chain. For your convenience here they all are in one place, without the interleaved comment lines:

/sbin/iptables -N dynamichosts
/sbin/iptables -I INPUT 1 -j dynamichosts
/sbin/iptables -F dynamichosts

The lines in blue in firewall-dynhosts.sh are custom additions by me. Just in case something goes wrong, I suggest you make a backup copy of /etc/sysconfig/iptables in a safe place before running this script.  My first addition checks the first character of the string returned in $IP to make sure it is actually a number.  This was a quick and dirty addition to keep it from trying to use a string like ;; connection timed out; no servers could be reached as a valid IP address (yes, it really did that).  I’m sure that the test there could be improved upon (for example, to do a full check for a valid IP address rather than just checking the first digit) but as I say this was a quick and dirty fix.  If you have any suggestions on how to improve it, please leave a comment.  I did find this article, Validating an IP Address in a Bash Script, but it seemed like a bit of overkill considering that in this case what I’m really trying to do is simply weed out error messages.

The second set of additions change the address in the dynamichosts chain of /etc/sysconfig/iptables. This is particularly important if you run both Webmin and fail2ban. If fail2ban is running it will add some lines to the in-memory version of iptables, so you don’t want to do a simple commit to save the in-memory version back to the iptables file. But at the same time, if you use Webmin’s “Linux Firewall” module to maintain iptables, you want any changes in IP addresses to show up the next time you call up Webmin’s Linux Firewall page. So this simply does a search and replace in /etc/sysconfig/iptables on the rule containing the old IP address, and replaces it with the new one. Note there’s still a possibility of missing a change if you are actually working in Webmin when a change occurs (since you’ll already have loaded a copy of iptables, and if you then make changes and save it out it could overwrite any change made by the script). But, the last two lines of the script send you an e-mail to alert you to that possibility. If you don’t use Webmin and don’t need or want an e-mail notification for some other reason, you can omit those last two lines, otherwise change the parts in red text to sane values for your situation. While editing, pay attention to the backtick at the end of the line (it’s easy to accidentally delete it when editing an e-mail address — don’t do that!).

When you’re all finished, make sure both scripts are executable and the permissions are correct, then create a cron job to call the first script every five minutes.

The only slight drawback to this method is that when an IP address changes it can take up to ten minutes to update (five for DynDNS to pick it up, and five more for the cron job to fire that gets it from DynDNS). Fortunately, most ISP’s tend to change IP address assignments in the middle of the night.

One thing some may not like is that this script basically hands the “keys to the kingdom” to your authorized users, by giving them access to all ports, or at least all ports not explicitly denied by rules higher in priority. It would be easy enough to change the rule that is written to iptables, or even add additional ones, in the above script, so that you could specify access to individual ports. The other problem is it works great for those external users at fixed locations that don’t move around a lot. It might not work quite as well as well for softphone users on laptops due to the delay between the time they turn on the laptop and the time your Asterisk server picks up the new address.

This has actually worked the best for me of anything I’ve tried so far because once you get the external user’s router set up to do the DynDNS updates, they don’t have to think about doing anything else prior to making a call.

2 Comments »

  1. Jack said

    Best news I’ve had all month. Even if it’s not semi-regular, glad to see you back. This site has provided so much information since I started working on VoIP 5+ years ago.

  2. [...] Using DynDNS to solve the problem of keeping a firewall open to remote users at changeable IP addres… (michigantelephone.wordpress.com) GA_googleAddAttr("AdOpt", "1"); GA_googleAddAttr("Origin", "other"); GA_googleAddAttr("theme_bg", "ffffff"); GA_googleAddAttr("theme_border", "666666"); GA_googleAddAttr("theme_text", "333333"); GA_googleAddAttr("theme_link", "105CB6"); GA_googleAddAttr("theme_url", "8DAB3B"); GA_googleAddAttr("LangId", "1"); GA_googleAddAttr("Autotag", "technology"); GA_googleAddAttr("Tag", "linux-system-administration"); GA_googleAddAttr("Tag", "system-administration"); GA_googleAddAttr("Tag", "centos"); GA_googleAddAttr("Tag", "firewalls"); GA_googleAddAttr("Tag", "iptables"); GA_googleAddAttr("Tag", "linux"); GA_googleFillSlot("wpcom_below_post"); [...]

RSS feed for comments on this post · TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 120 other followers