Use Tomato Shibby & AWS Route 53 as your DynDNS service

in
../../../_images/dyndns-aws-route-53.png

A guide on installing AWS tools on Tomato Shibby to roll your own dynamic DNS service.

Tomato Shibby is a custom firmware which can be flashed on to routers to provide many after market features and also access to root. For example, you have hook up an external HDD to the router and use it as a media server.

This is a short guide to get up and running with Entware, Python and awscli tools. We’ll install a script which will update the DNS on AWS Route 53 when the IP of the router changes.

In order to follow along you’ll need to have Entware installed on your router. If you’ve come from a fresh tomato install, you can find an install script in /usr/sbin/entware-install.sh, we’ll cover this below, the /usr/bin directory is in your PATH by default on Tomato Shibby so you can just run it using entware-install.sh.

Installing entware & other packages on tomato

First, we need to install Entware (If you haven’t got it already).

# /usr/sbin/ is already in our path, so we can just execute it
entware-install.sh

# If that doesn't work use: (https://github.com/Entware/entware)
wget -O - http://entware.wl500g.info/binaries/mipselsf/installer/entware_install.sh | sh

Log out of your SSH session and log back in (the install updated your PATH).

Next, we need to install all our basic utility packages (if you haven’t already).

These are all the basic packages I install on a fresh tomato install. You can view them all using opkg list, you can pipe the results through grep to filter through them opkg list | grep rsync.

# We particularly need Bash, Python v2.6+ & ca-certificates
# to install aws tools
# By default entware ships with 2.7 as default so just install `python`
opkg install bash bzip2 curl openssl openssl-util ca-certificates \
rsync wget python python-curl python-openssl

Now, Install easy_install for Python.

This will install by default into /opt/bin/easy_install.

/opt/bin/wget -O setup-distribute.py https://svn.apache.org/repos/asf/oodt/tools/oodtsite.publisher/trunk/distribute_setup.py
python setup-distribute.py

Finally, Install awscli (AWS command line tools for python) via easy_install.

Finally we need to install our awscli tools to interact with AWS. The easy_install script is already in your PATH so we just need to run it.

# Install `awscli`
easy_install awscli

You can check everything was installed correct by running aws --version. Now we have have our aws tools installed, we need to configure it, we need to add our AWS access_key and secret_key, you should already how and where to get these (hint: AWS Console).

Run aws configure and follow the prompts to add your keys.


Updating your DNS record sets using a script

You can now run commands using the aws CLI to update and manage your records.

# Create the file `touch /opt/test-batch.json` with contents:
{
  "Comment":"Testing manual update",
  "Changes":[{
      "Action":"UPSERT",
      "ResourceRecordSet": {
        "ResourceRecords": [
          {
            "Value":"192.168.100.100"
          }
        ],
        "Name":"<recordName e.g sub.domain.com>",
        "Type":"A",
        "TTL":300
      }
    }]
}
# Make sure you change the "Name" to your recordset name (e.g: sub.domain.com)

# Run the following command and see your record set get updated!
# (Change <zoneId> with your zoneId from AWS)
aws route53 change-resource-record-sets --hosted-zone-id <zoneId> --change-batch file:///opt/test-batch.json

Now we just need to turn this into a script that is executed whenever the router’s WAN goes up.



AWS route 53 DDNS update script

This is the final script I use, adapted from Will Warren modified to work with Tomato Shibby.

#!/usr/bin/env bash

# AWS Hosted Zone ID e.g. Z5SVKH13CDPEO
ZONEID="enter zone id here"

# The CNAME you want to update e.g. sub.domain.com
RECORDSET="enter cname here"

# The Time-To-Live of this recordset
TTL=300

# Optional comment
COMMENT="Auto updating @ `date`"

# Change to AAAA if using an IPv6 address
TYPE="A"

# Get current dir (from http://stackoverflow.com/a/246128/920350)
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
LOGFILE="$DIR/update-route53.log"
IPFILE="$DIR/update-route53.ip"

# Choose from several options to get your IP:
#IPPROVIDER=http://ifconfig.me/ip
#IPPROVIDER=https://wtfismyip.com/text
IPPROVIDER=https://icanhasip.com/

# Get the external IP address via external provider OR
# IP=`curl -sS $IPPROVIDER`

# Get it from our local ppp0 adapter
IP=`ip -4 -o addr show dev ppp0| awk '{split($4,a,"/");print a[1]}'`

function valid_ip()
{
    local  ip=$1
    local  stat=1

    if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        OIFS=$IFS
        IFS='.'
        ip=($ip)
        IFS=$OIFS
        [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
            && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
        stat=$?
    fi
    return $stat
}

if ! valid_ip $IP; then
    logger Invalid IP address: $IP
    exit 1
fi

# Check if the IP has changed
if [ ! -f "$IPFILE" ]
    then
    touch "$IPFILE"
fi

if grep -Fxq "$IP" "$IPFILE"; then
    logger AWS: IP has not changed, still $IP. Exiting
    exit 0
else
    logger AWS: IP has changed to $IP.. Updating AWS..
    # Fill a temp file with valid JSON
    TMPFILE=/tmp/temp-aws-file.temp
    cat > ${TMPFILE} << EOF
    {
      "Comment":"$COMMENT",
      "Changes":[
        {
          "Action":"UPSERT",
          "ResourceRecordSet":{
            "ResourceRecords":[
              {
                "Value":"$IP"
              }
            ],
            "Name":"$RECORDSET",
            "Type":"$TYPE",
            "TTL":$TTL
          }
        }
      ]
    }
EOF

    # Update the Hosted Zone record
    aws route53 change-resource-record-sets \
        --hosted-zone-id $ZONEID \
        --change-batch file://"$TMPFILE" >> "$LOGFILE"
    echo "" >> "$LOGFILE"

    # Clean up
    rm $TMPFILE
fi

# All Done - cache the IP address for next time
echo "$IP" > "$IPFILE"

Place the above file into /opt/etc/config, you can name it anything but the extension must be wanup e.g: AWS.wanup.

Now whenever your router’s WAN goes up, this script will fire triggering an update to AWS of your IP (if it hasn’t changed since the previous update).


Run the script via a CRON job

You can run this every 30 minutes by adding it to your CRON jobs. You can do this using: crontab -e and adding the following line.

*/30 * * * * /opt/etc/config/AWS.wanup