# How I hosted a DNS server on AWS ?

> A hands-on guide to hosting an authoritative BIND9 DNS server on AWS EC2, from setting up glue records to configuring zone files with a bash script.

Date: June 7, 2019
Tags: AWS, DNS, AWS Route53, BIND 9


Wait. I know what you are thinking now.

Who on earth would do such a crazy thing ?

Why would a person even host a DNS server on AWS when one could use Route53 to efficiently manage DNS records.

The answer is simple:

> I’ve been a user of DNS since my first interaction with the internet, but have no clear idea on how DNS works.

So I started learning how DNS works and more on how DNS server works. You can [check out my notes on DNS](). I felt that the best way to learn about DNS is by practically hosting a DNS server.

Trust me this way is far better than learning DNS by using a third party DNS service or reading theoretical blog posts on how DNS works.

I had a few credits left on AWS, that’s when I asked myself this question: “Is it possible to host a DNS server on AWS?”

## Creating the Name Server on AWS

The steps are pretty simple:

1. **Create an EC2 instance.** Free tier version of Ubuntu works fine. (Please read [this article](https://medium.com/@GalarnykMichael/aws-ec2-part-1-creating-ec2-instance-9d7f8368f78a) to learn how to create EC2 instance). Download the SSH key to access the instance.
2. **Create an elastic IP and associate it with the EC2 instance** created in the first step. (If you have never worked with elastic IPs, you can read [this article](https://medium.com/@pablo_ezequiel/setting-an-elastic-ip-on-aws-ec2-739341a1cc65))
3. Now the public IP of the EC2 instance will be replaced by the elastic IP.
4. **Create a Security Group which allows 0.0.0.0/0 access to port 53 (TCP and UDP)** and add this security group to the EC2 instance. This allows DNS interactions from public internet.

## Setting up the domain name

I didn’t really want to spend money for this experiment. So ended up registering a free domain – **machane.ga** – for one month at [FreeNom](https://www.freenom.com/en/index.html?lang=en).

> **Did you know ?** Machane is the malayalam slang word meaning “dude”

FreeNom allows user managed DNS name servers. One could create [Glue records](https://ns1.com/blog/glue-records-and-dedicated-dns) at the beginning of the registration. I entered two nameservers as `ns1.machane.ga` and `ns2.machane.ga` and gave the same AWS elastic IP in both the IP address fields.

![Name servers](image2.webp)

If you observe above, you can see that I am in the process of registering `machane.ga` and provided the nameservers as `ns1.machane.ga` and `ns2.machane.ga`. That’s the trick in [glue records](https://ns1.com/blog/glue-records-and-dedicated-dns). So, basically we are saying the domain registrar that I am going to register machane.ga and I am also going to host the nameservers for it under the DNS paths `ns1.machane.ga` and `ns2.machane.ga`.

Once the registration of the domain is successful and logged in to the FreeNom domain nameserver administration panel, one should be able to see something similar to the following:

![Glue records](image3.webp)

## Setting up the DNS server

I have done the first half of the job. The domain registration is instant but the [propagation of the domain’s DNS](https://www.namecheap.com/support/knowledgebase/article.aspx/9622/10/dns-propagation--explained) would take some time ([upto 72 hours on FreeNom](https://my.freenom.com/knowledgebase.php?action=displayarticle&id=15)). In the mean time I could setup the DNS server.

After much fiddling with the installation and configuration of [BIND9 DNS server on Ubuntu](https://help.ubuntu.com/community/BIND9ServerHowto), I finally created a bash script to automate the setup of DNS zones using BIND server.

If you are interested in setting up a DNS server, please make changes (domain name, subdomains, elastic IP, etc) to the following script. Also don’t forget to run it with **sudo** permission.

```bash
apt install bind9 bind9-doc -y

echo 'include "/etc/bind/named.conf.log";' | tee -a /etc/bind/named.conf

# Store all the logs to /var/log/named/bind.log

tee /etc/bind/named.conf.log << EOF
logging {
 channel bind_log {
   file "/var/log/named/bind.log" versions 3 size 5m;
   severity info;
   print-category yes;
   print-severity yes;
   print-time yes;
 };
 category default { bind_log; };
 category update { bind_log; };
 category update-security { bind_log; };
 category security { bind_log; };
 category queries { bind_log; };
 category lame-servers { null; };
};
EOF

# Create the directory for storing logs

mkdir /var/log/named
chown bind:root /var/log/named
chmod 775 /var/log/named/

service bind9 restart

# Setup log rotation

tee /etc/logrotate.d/bind << EOF
/var/log/named/bind.log
{
    rotate 90
    daily
    dateext
    dateformat _%Y-%m-%d
    missingok
    create 644 bind bind
    delaycompress
    compress
    notifempty
    postrotate
        /bin/systemctl reload bind9
    endscript
}
EOF

# Change the zone to your domain name

tee /etc/bind/named.conf.local << EOF
zone "machane.ga" {
        type master;
        file "/etc/bind/zones/db.machane.ga";
};
EOF

mkdir /etc/bind/zones

# Creating the zone files
# Setting AWS elastic IP

ElasticIP=xx.xx.xx.xx

# Please dont forget . at the end of the DNS entries

tee /etc/bind/zones/db.machane.ga << EOF
\$TTL 900
@       IN      SOA     ns1.machane.ga. admin.machane.ga. (
                                1       ;<serial-number>
                              900       ;<time-to-refresh>
                              900       ;<time-to-retry>
                           604800       ;<time-to-expire>
                              900)      ;<minimum-TTL>
;List Nameservers
        IN      NS      ns1.machane.ga.
        IN      NS      ns2.machane.ga.
;Create A record
        IN      A       127.0.0.1
;address to name mapping
ns1     IN      A       $ElasticIP
ns2     IN      A       $ElasticIP
;wildcard DNS entry
*       IN      A       127.0.0.1
EOF

service bind9 restart
```

In the above script, I have explicitly mentioned that `machane.ga` and `*.machane.ga` (except the subdomains `ns1` and `ns2`) resolve to 127.0.0.1. Once the registered domain has propagated its DNS changes around the world, anyone could query the domain and its subdomains.

## Final thoughts

I have finally created an [authoritative DNS server](https://www.digitalocean.com/community/tutorials/how-to-configure-bind-as-an-authoritative-only-dns-server-on-ubuntu-14-04) which resolves any subdomain of machane.ga. It will not resolve DNS of any other domain.

I did this to experiment and learn more about how DNS works (in real life) and DNS servers. I gained a good experience and had a lot of fun along the learning process.

Until my next experiment, *hasta la vista*.

> **DISCLAIMER:** I have registered the domain for experimental purpose for a single month. Any changes done to the domain’s DNS after a month (from the time of writing this article) will not be me. I am not be responsible for any damage done by reusing the above bash script.
