Dynamic DNS

Let’s say you want to access your home network from anywhere but your home IP keeps changing. You can rely on a combination of good memory and good luck or you could use a dynamic DNS service like NoIP or DynDNS, or you can build one for yourself using Amazon Route53.

To build your own dynamic DNS service you’ll need a few things:

  • An AWS account.
  • An Amazon Route53 hosted zone.
  • An IAM user.
  • A domain.
  • Something that keeps the IP updated.

Let’s go step by step.

An AWS account

Not a lot to explain here, just go to AWS and create a new account. It will ask for a credit card but you’ll only be charged for what you use. Everything described here will cost you around $0.5 per month.

Anyway, when using AWS it’s always a good idea to use AWS Budgets where you can set a budget and alarms that will warn you before you exceed it.

An Amazon Route53 hosted zone

A hosted zone is the AWS name for a DNS zone, the place where the records for a DNS name are defined. Go to the AWS documentation to see how to create a public hosted zone.

The hosted zone you have created will have a NS record containing the addresses of 4 DNS servers that will host the hosted zone. You’ll need to configure these servers as DNS servers of your domain.

Each hosted zone has an unique identifier with the format similar to ZXXXXXXXXXXXXX. Find and write down the hosted zone ID for the zone you just created because you’ll need it in the next steps.

An IAM user

An IAM user is an entity you use to interact with AWS. In this case will provide specific permissions to modify a hosted zone record set using a credential pair.

Follow the AWS documentation to create an IAM user and attach the following policy:

{
    "Version": "2012-10-17",
    "Statement": [{
        "Sid": "Stmt1597880289163",
        "Action": [
            "route53:ChangeResourceRecordSets"
        ],
        "Effect": "Allow",
        "Resource": "arn:aws:route53:::hostedzone/hosted_zone_ID"
    }]
}

Write down (in a safe place) the access key and secret key for the IAM user, you’ll need them later.

A domain

You can register a domain in different places, for example Gandi.net, GoDaddy, or Amazon Route53.

For this example will use the domain example.com.

Something that keeps the IP updated

We will use Python to create a little program that retrieves your current public IP and updates the Amazon Route 53 hosted zone.

First thing we need is to retrieve the current IP and for that we can use ifconfig.co:

def get_ip():
    r = requests.get('https://ifconfig.co/ip')
    if r.status_code == 200:
        return r.text.strip()
    else:
        return None

This tiny function will return your current public IP address as a string.

Next we will use boto3, the AWS SDK for Python, to interact with AWS and update the Amazon Route53 hosted zone. A single call to the API ChangeResourceRecordSets is all that is needed. Using boto3 we will need to get a Route53 client and call change_resource_record_sets. There are plenty of documentation but is always good to see working code so here it is:

client = boto3.client('route53')

response = client.change_resource_record_sets(
    HostedZoneId=hosted_zone,
    ChangeBatch={
        "Comment": "Automatic record update",
        "Changes": [{
            "Action": "UPSERT",
            "ResourceRecordSet": {
                "Name": name,
                "Type": "A",
                "TTL": 300,
                "ResourceRecords": [{
                    "Value": <THE IP>
                }]
            }
        }]
    }
)

Now we have all the pieces, we just need to put them together and find a way to call it periodically. You can see the full DNSupdater code in GitHub.

Copy the script to your home server and use cron to run it periodically, once per hour would be enough:

0 * * * * not_root_user_please ./dnsupdater.py --hosted-zone-id ZXXXXXXXXXXXXX --name home.example.com

You’ll find you need to configure the credentials somehow. The easiest way is to configure them in the ~/.aws/credentials file in the home directory of the user that will run the script. Here you have the credentials configuration documentation in case you want to dive deep.

If you configure the credentials under an specific profile you can select the profile the script will use by declaring an environment variable.

0 * * * * not_root_user_please AWS_PROFILE=profile ./dnsupdater.py --hosted-zone-id ZXXXXXXXXXXXXX --name home.example.com