I run a VPS which I would like to secure using UFW, allowing connections only to port 80. However, in order to be able to administer it remotely, I need to keep port 22 open and make it reachable from home.

I know that UFW can be configured to allow connections to a port only from specific IP address:

ufw allow proto tcp from to any port 22

But my IP address is dynamic, so this is not yet the solution.

The question is: I have dynamic DNS resolution with DynDNS, so is it possible to create a Rule using the domain instead of the IP?

I already tried this:

ufw allow proto tcp from mydomain.dyndns.org to any port 22

but I got ERROR: Bad source address


I don't believe this is possible with ufw. ufw is just a frontend to iptables which also lacks this feature, so one approach would be to create a crontab entry which would periodically run and check if the IP address has changed. If it has then it will update it.

You might be tempted to do this:

$ iptables -A INPUT -p tcp --src mydomain.dyndns.org --dport 22 -j ACCEPT

But this will resolve the hostname to an IP and use that for the rule, so if the IP later changes this rule will become invalid.

Alternative idea

You could create a script like so, called, iptables_update.bash.

#allow a dyndns name


Current_IP=$(host $HOSTNAME | cut -f4 -d' ')

if [ $LOGFILE = "" ] ; then
  iptables -I INPUT -i eth1 -s $Current_IP -j ACCEPT
  echo $Current_IP > $LOGFILE

  Old_IP=$(cat $LOGFILE)

  if [ "$Current_IP" = "$Old_IP" ] ; then
    echo IP address has not changed
    iptables -D INPUT -i eth1 -s $Old_IP -j ACCEPT
    iptables -I INPUT -i eth1 -s $Current_IP -j ACCEPT
    /etc/init.d/iptables save
    echo $Current_IP > $LOGFILE
    echo iptables have been updated

source: Using IPTables with Dynamic IP hostnames like dyndns.org

With this script saved you could create a crontab entry like so in the file /etc/crontab:

*/5 * * * * root /etc/iptables_update.bash > /dev/null 2>&1

This entry would then run the script every 5 minutes, checking to see if the IP address assigned to the hostname has changed. If so then it will create a new rule allowing it, while deleting the old rule for the old IP address.

  • 2
    How silly that I didn't think about resolving the hostname periodically. I modified your script (added logging, etc.) and it works like a charm. Thank you! – Carles Sala Sep 21 '13 at 14:42
  • @CarlesSala - glad it solved your issue. In addition to accepting you can also upvote 8-). – slm Sep 21 '13 at 14:48
  • 1
    note: on Debian 7 I had to change line Current_IP=$(host $HOSTNAME | cut -f4 -d' ') to Current_IP=$(host $HOSTNAME | head -n1 | cut -f4 -d ' ') – Krystian Jan 6 '16 at 13:47
  • Will I be able to see this when using ufw status verbose ? I mean, the rules? – Freedo Apr 7 '17 at 9:02
  • @Freedo not sure, try it and see what happens. – slm Apr 7 '17 at 11:16

I know this is old but I ran across it and ended up with this solution in the end which seems even better because no log file is needed and it very easy to add additional hosts as needed. Works like a charm!

Source: http://rdstash.blogspot.ch/2013/09/allow-host-with-dynamic-ip-through.html


DYNIP=$(host $DYNHOST | grep -iE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" |cut -f4 -d' '|head -n 1)

# Exit if invalid IP address is returned
case $DYNIP in )
exit 1 ;; )
exit 1 ;;

# Exit if IP address not in proper format
if ! [[ $DYNIP =~ (([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]) ]]; then
exit 1

# If chain for remote doesn't exist, create it
if ! /sbin/iptables -L $DYNHOST -n >/dev/null 2>&1 ; then
/sbin/iptables -N $DYNHOST >/dev/null 2>&1

# Check IP address to see if the chain matches first; skip rest of script if update is not needed
if ! /sbin/iptables -n -L $DYNHOST | grep -iE " $DYNIP " >/dev/null 2>&1 ; then

# Flush old rules, and add new
/sbin/iptables -F $DYNHOST >/dev/null 2>&1
/sbin/iptables -I $DYNHOST -s $DYNIP -j ACCEPT

# Add chain to INPUT filter if it doesn't exist
if ! /sbin/iptables -C INPUT -t filter -j $DYNHOST >/dev/null 2>&1 ; then
/sbin/iptables -t filter -I INPUT -j $DYNHOST

  • sorry I'm a little newbie. Where do I need to store this script and where I change things to reflect my specific case ? – Freedo Apr 7 '17 at 9:52

Based on previous answers I updated the following as bash script that works on Debian Jessie

Current_IP=$(host $HOSTNAME | head -n1 | cut -f4 -d ' ')

if [ ! -f $LOGFILE ]; then
    /usr/sbin/ufw allow from $Current_IP to any port 22 proto tcp
    echo $Current_IP > $LOGFILE

    Old_IP=$(cat $LOGFILE)
    if [ "$Current_IP" = "$Old_IP" ] ; then
        echo IP address has not changed
        /usr/sbin/ufw delete allow from $Old_IP to any port 22 proto tcp
        /usr/sbin/ufw allow from $Current_IP to any port 22 proto tcp
        echo $Current_IP > $LOGFILE
        echo iptables have been updated
  • That could even be added to cron to have it run periodically on it's own. – Tim Kennedy Mar 21 '17 at 15:18
  • That's what I did ;) – Mattias Pettersson Mar 22 '17 at 14:39
  • This script has a small problem: on first usage if you forgot to run as root, it'll create the log file but not add the rules. Then if you run again as root it'll just say 'ip address didn't change'. It has to be run as root the first time! Also, would be good to change LOGFILE=$HOME/ufw.log to LOGFILE=$HOME/ufw.$HOSTNAME.log to permit more than one script run at the same time – Guerlando OCs Apr 25 '18 at 2:38
  • @GuerlandoOCs how do you reset if you run into this issue? – Matthew Dec 28 '18 at 15:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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