The Challenge

In this blogpost I would like to show you, how an EC2 instance can register itself automatically on AWS Route 53. This could be important, if you have ie. a clustered group of nodes (like an Apache ZooKeeper or Kafka Cluster) where a list of DNS-Names or IP-Addresses need to be configured and you don’t want to use AWS specific DNS names.It is very easy to start new instances or make a copies of amazon machine images, but if you have a fixed set of nodes with fixed addresses you have to solve the challenge, that the private and even the elastic and public accessible IP’s will be different if you start a new machine generated from an image. You do not want to change the configuration afterwards only because you started a new machine.

In the early days we could achive that through different ways. Sometimes I used ie. DynDNS for self healing DNS entries or a fixed IP, that was assigned through a DNS-Gateway based on the machines MAC address. In AWS this is not that easy, because a newly created machine should have a different physical address. You can still use DynDNS, but then you can not use the other features of AWS Route 53.

The Steps

  1. Create a private hosted zone in AWS Route 53 (you can use whatever you like as domain name)
  2. Create a new AWS IAM Policy (for a restricted Route 53 DNS Access)
  3. Create a new AWS IAM Group
  4. Create a new AWS IAM User
  5. Create a new AWS EC2 instance and add some predefined tags
  6. Installation of some tools and a script to register the instance on startup or manually on AWS Route 53

1) Create the private hosted zone

First login to AWS Console and switch to Route 53 service. Create a new private hosted zone. The same steps are also possible with a public zone. I used test.com as my domain so that we later have several nodes called ie. server1.test.com, server2.test.comserverN.test.com. Remember the ID from the created zone (in my case Z2IZ9V712F87LJ) because we will use this later when we create A-Records in our Route 53 zone. The hosted zone is normaly attached to a AWS Virtual Private Cloud Network, so later instances need to be part of the VPC to change the DNS tables.

Create a private or public hosted zone in AWS Route 53

2) Create a new AWS IAM Policy for a restricted Route 53 DNS Access

Next we need to create a new IAM Policy. Therefore switch to the IAM service. With this custom policy we like to allow access to read or change the record sets and to the hosted zone itself. Here we use the zone ID to specify the Amazon Resource Name (ARN) that this rules will apply to. The policy should also allow us to list all hosted zones.

Create a policy that allow access to the zone

3) Create a new AWS IAM Group

After the policy is ready to use we can create a new group. Assign the new policy to the group as well you can add the default ec2 read only access policy. If you like you can create a custom policy too for specific resources, for my case this policy is restrictive enough.

Create a group with the new policy and read access to EC2

4) Create a new AWS IAM User

The last we need to prepare is a new user that has only programmatic access via command line tools. We need the credentials of this technical user later to enable the access from the instances to AWS Route 53. In step 2 of the wizzard assign the user to the group we created.

Create a new user with programatic access that is assigned to the group

5) Create a new AWS EC2 instance and add some predefined tags

Next we will create some EC2 instances. In the tags view you can see the two additional tags internal-hostname and public-hostname. We will use this two tags later to register the A-Record at Route 53 to that names. If you don’t like to use AWS tags, you can also write the hostname to instance startup as environment variable or into another script. I like the way to solve this with tags because it’s very transparent and easy to use.

Add the internal and public hostname tag to the instance

6) Installation of some tools and a script to register the instance on startup or manually on AWS Route 53

Now connect via ssh to your EC2 machine with your key and start the preparation of the instance. First we install python and the AWS client tools.

Go to your home directory and download a tool that is available on github (here). This allows us to add, change or remove records form Route 53 through command line calls.

Add a new file that contains the credentials for the AWS client tools and also the ID from the hosted zone.

Save the following constants to the config file.

Then create the file that contains the script to update the AWS Route 53 records.

In the following script we load the config file and make the AWS access key and secret key accessible. Then we will discover the AWS region where the instance is hosted in. If you are confused about the public IP address 169.254.169.254 that is used in the curl command, please read here for more information. Then we gather the instance-id from ec2metadata. With aws ec2 describe-tags we can return all tags that are assigne to the instance. Feel free to change the name of the tags. Now we just need the private and public IP-Addresses of the instance. Caution, I configured my VPC, that all new instances get a Elastic IP automatically. If you do not assign a public EIP just delete the three lines that are related to the public IP and hostname.

Add the following line to /etc/rc.local so that the script get’s executed on restart.

To test the script just log in as root user and execute the script.

Now open up Route 53 in AWS Console. You should see that all three of my test instances added two A-Records each. The first (server1.test.com) can be used if a request should be routed over public internet and the second (server1i.test.com) can be used if two instances should communicate between each other without using public internet.

 

You can also try to communicate between the servers if you install tcptraceroute. Call ie. tcptraceroute server2i.test.com 22 from server 1 to prove if the route is solvable and reachable without hops over some public routers.