linkedin tracking
icon-sprite Created with Sketch.
Skip to main content

Mobile & Web


Automated DNS for AWS Instances Using Route 53

Hosting applications on Amazon EC2 is a relatively painless process. Getting instances set up and ready to deploy applications to can literally take minutes. However, once your virtual infrastructure gets larger, you start to run into more complicated issues.

One of those is the limit on the number of static IP addresses (called Elastic IP addresses in Amazon parlance) you can assign to your instances. Amazon will only give you 5, and though you can petition them for more, they want a pretty good reason to do so. Without those static IP addresses, you are limited in what you can do with your new instances, such as allowing them to communicate with one another and making them accessible to the outside world, since the IP addresses can change each time you restart the instance. For those not familiar with Amazon's service, all of the virtual machines are provided with DHCP addresses via NAT inside Amazon's network. This makes sense, as Amazon has thousands and thousands of instances running at any one time, and Amazon only has a limited number of IPv4 addresses in their block.

To deal with this issue, some recommend running a BIND server on another instance. There are also some strategies which write out /etc/hosts on the instances using a cron job. Both of these solutions are good. However, running a BIND server is non-trivial and requires a failover strategy. And writing out /etc/hosts, while simple and relatively foolproof, doesn't address access from the outside world. Luckily, Amazon's new Route 53 service provides a simple, cost effective solution that requires very little work to implement, while solving both of these issues.

The advantage to using Route 53 over running a BIND server on another instance should be obvious. It's cheap, has a high degree of failover, and provides a simple web service for making updates. I have adapted the technique described here to provide comprehensive, step-by-step instructions for how to use Route 53 to provide well-known hostnames for all your instances. Note that these instructions are for a Debian-based instance, but they could be adapted for another distribution or architecture.

Making Route 53 Your DNS Provider

If your organization is small, you may want to use Route 53 for all your DNS services. In that case, you can simply set up Route 53 and use Amazon as the authoritative name servers for your domain. However, large enterprises with existing DNS infrastructure will likely want to delegate a subdomain to Route 53 just for managing the Amazon instances. Either way, you will likely want to create a subdomain specifically for your instances. You will want to do all this first, so the new settings will propagate before you start the next step.

Setting up the Web Service User

To make our system more secure, we will first configure a new IAM user. This new user will allow our instances to access Route 53 and add new resource entries, but only to their own zone. It will not have access to any other AWS services.

To do this, first create a new group in IAM. We use a group so that in the future we can give this set of permissions to another user if needed. I have called the group "dns-editor" but it can really be any name you desire. Next, we will assign permissions to the group that will allow users in it to access just the features in Route 53 which are needed to make resource entries.

  1. Click "Attach Group Policy"
  2. In the dialog, click on "Custom Policy" and click the "Select" button
  3. Enter a name for the policy. I used "change-dns-records"
  4. In the policy document field, copy and paste the following JSON
{
   "Statement":[
      {
         "Action":[
            "route53:ChangeResourceRecordSets",
            "route53:GetHostedZone",
            "route53:ListResourceRecordSets"
         ],
         "Effect":"Allow",
         "Resource":[
            "arn:aws:route53:::hostedzone/<Your zone ID>"
         ]
      },
      {
         "Action":[
            "route53:ListHostedZones"
         ],
         "Effect":"Allow",
         "Resource":[
            "*"
         ]
      }
   ]
}

Where it says <Your zone ID>, above, put the ID of the Route 53 zone ID you made note of earlier. This policy limits access of the user to just the features of Route 53 that it needs and no others. Most importantly, it can't create or delete zones.

Next, create a user. I called this one dns-editor as well. When the user is created, you will be given a chance to download its credentials. It is extremely important that you do this immediately or you will not be able to access them again. They come in the form of a CSV file which you can download to your computer. Put them in a very safe place.

Now that the access to the system is set up, we can configure the instances for Route 53.

Configuring Your Instances

Route 53 has some very rudimentary tools at this time for managing your DNS programatically. Luckily Barnaby Gray has created a nice tool called cli53 which makes adding new entries in Route 53 much simpler. Download this library and install it on the instance:

https://github.com/barnybug/cli53

I chown'd the install directory to root:root and put it /usr/local, but you can put it anywhere it suits you. I also made a symlink to the cli53 script in /usr/bin.

Next create the configuration file for the script. Note that we make the permissions very tight, as the information in this file can be used to modify the DNS entries on Route 53:

  1. sudo mkdir /etc/route53
  2. sudo chmod 700 /etc/route53
  3. sudo touch /etc/route53/config
  4. sudo chmod 600 /etc/route53/config

Copy and paste the following in the configuration file, replacing <Your access key ID> and <Your secret access keyID> with the credentials you downloaded for the user we created before. Likewise, replace with, you guessed it, the name of your subdomain:

AWS_ACCESS_KEY_ID="<Your access key ID>"
AWS_SECRET_ACCESS_KEY="<Your secret access key>"
ZONE="<The name of your subdomain>"
TTL="600"

Next, make a new file /usr/sbin/update-route53-dns and copy and paste the following into it:

#!/bin/sh

# Make sure only root can run our script
if [ "$(id -u)" != "0" ]; then
    echo "This script must be run as root" 1>&2
    exit 1
fi

# Load configuration
. /etc/route53/config

# Export access key ID and secret for cli53
export AWS_ACCESS_KEY_ID
export AWS_SECRET_ACCESS_KEY

# Use command line scripts to get instance ID and public hostname
INSTANCE_ID=$(ec2metadata | grep 'instance-id:' | cut -d ' ' -f 2)
PUBLIC_HOSTNAME=$(ec2metadata | grep 'public-hostname:' | cut -d ' ' -f 2)

# Create a new CNAME record on Route 53, replacing the old entry if nessesary
cli53 rrcreate "$ZONE" "$INSTANCE_ID" CNAME "$PUBLIC_HOSTNAME" --replace --ttl "$TTL"

This script uses the command line tool ec2metadata to get its public DNS hostname and its instance ID, which it uses to make a a CNAME entry in Route 53. Note that on some AWS instances this script may be called "ec2-metadata", so you may need to customize the script for your environment. Because the CNAME resolves to the public hostname Amazon assigned the instance, it will be useful by external users. Also, when used internally, it will resolve to the internal IP address of the instance. This is important because it keeps costs down for internal VM to VM data transfer. We keep the TTL short, but depending on how often you expect to reboot your instances, you could change this in the configuration file.

Finally, make a symlink to /usr/sbin/update-route53-dns in /etc/dhcp3/dhclient-exit-hooks.d/. This will cause the script to be run every time a new DHCP lease is assigned to the instance. UPDATE: This is a distro-specific technique as noted by Rafal Lukawiecki in the comments below. You may want to run it via /etc/rc.local. Or, if you are using Upstart, add a script in /etc/init to run it after networking has started.

Now, reboot the instance you have configured. Wait a few minutes or so and have a look at the recordsets in your new Route 53 zone. If you should see a new CNAME entry for the instance in there. Now you can configure all your instances this way and have well known hostnames for all of them, perhaps even making this part of your base AMIs.

Sign up for our newsletter


Delivered monthly, featuring great content from our blog!