Wednesday, September 18, 2013

Allow host with dynamic ip through iptables firewall

I have a telnet enabled service, which doesn't have any form of authentication. Naturally I don't want to expose this to the public internet - so it's firewall. Now I want to allow my host to access it, but my host have a dynamic ip address. Iptables only support static ips and ip ranges - but for my use case I only wanted a single ip.

My solution is to update rules in iptables using cron, such that only the ip found at my dynamic dns is allowed through the firewall.
HOSTNAME=$1
IP=$(host $HOSTNAME | grep -iE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" |cut -f4 -d' '|head -n 1)

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

# Flush old rules, and add new
/sbin/iptables -F $HOSTNAME
/sbin/iptables -I $HOSTNAME -s $IP -j ACCEPT

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

Example usage: 
./dnsallow.sh my.dynamic.dns.com

5 comments:

  1. hey i had a couple questions:

    1 - does using "$HOSTNAME" in the script mess anything up since that's a system variable too?

    2 - assuming the answer to #1 is "no", i think you can simplify the second line a bit by making it:

    IP=$(dig +short $HOSTNAME)

    ReplyDelete
  2. Thanks for posting this. I have added some of my own tweaks to this script and am sharing in case anyone can find it beneficial. I added a couple of sanity checks to make sure we don't try to update iptables with an invalid IP. I also added a check that prevents iptables from being updated if there hasn't been a change to the dynamic IP address. This way we can run the script in a cron job and it automatically updates iptables as needed. Also, I'm no expert scripter, so any improvements are welcome.

    Script:

    #!/bin/bash

    DYNHOST=$1
    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
    0.0.0.0 )
    exit 1 ;;
    255.255.255.255 )
    exit 1 ;;
    esac

    # 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
    fi

    # 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
    fi

    # 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
    fi

    fi

    ReplyDelete
  3. Also it appears that iptables will not accept chains with names longer than 28 characters. So I also used this to truncate the chain name:

    # Truncate $DYNHOST to 28 characters
    DYNHOST=${DYNHOST:0:28}

    ReplyDelete
  4. Where exactly do you add this part> At the end?

    ReplyDelete
  5. Could I run the script twice with different hostnamrs?

    ReplyDelete