Infrastructure Docs for the NixLearn space
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DNS.md 23KB

DNS Lab

The purpose of this lab is to create a DNS server. A DNS server serves out names for IP addresses, and occasionally, converts IP addresses to reverse lookup domain names. The latter is less common, and not usually needed except for specific kinds of domain verification (for example, email servers).

This lab will teach you the in’s and out’s of setting up the DNS server, but that is not a complete replacement for understanding DNS at the protocol level. That will be covered after you set up the server (TODO: Consider writing that).

Table of Contents

Pre-Lab Setup

There are some important steps that must be carried out before you can stand up your DNS server. Namely, you need to add your DNS server’s records into your DNS hosting provider’s DNS.

If you are using a tool like an external DNS registrar, they often provide you with their root level DNS servers for serving things like mywebsite.com and www.mywebsite.com. These will be A records directly on their nameservers.

There are two ways you can go about setting up for this lab. The easier option is to add some NS and A records, so that their server delegates a subdomain of your domain down to your system. The other method is to add your DNS servers directly as the domain name servers in the glue records, and then do all of the DNS for your domain. Note that if you want to go this route, you must have two DNS servers (or more).

This lab will assume that you are using a subdomain delegation, since that is easier to do, but you can very well repeat all of the steps of this lab with a full domain name. It doesn’t really change much, except for adding the subdomain. A subdomain DNS server can also delegate to any subdomain it wants, so you can have multiple chains of DNS servers.

For instance, the COSI DNS server is delegated the @.cslabs.clarkson.edu and @.cosi.clarkson.edu subdomains from Clarkson’s nameservers. If one wanted, you could even configure your DNS servers in COSI’s DNS so that you could have @.yoursubsubdomain.cslabs.clarkson.edu.

Alright, enough talk. Let’s do this.

Add delegation records to DNS

Add the following records into your DNS:

<dns server 1>.<domain name>.     IN A    <nameserver's IPv4 address>
<dns server 2>.<domain name>.     IN A    <nameserver's IPv4 address>
<service>.<domain name>.        IN NS   <first part of the above A record for dns server 1>
<service>.<domain name>.        IN NS   <first part of the above A record for dns server 2>

So, for instance:

ns1-cam.ja4.org.	IN	A	128.153.145.111
ns2-cam.ja4.org.	IN	A	128.153.145.112
cam.ja4.org.		IN	NS	ns1-cam.ja4.org.
cam.ja4.org.		IN	NS	ns2-cam.ja4.org.

This may look different depending on your DNS registrar, and if you’re doing this on COSI’s infrastructure, do not forget to follow the instructions and update the serial. The serial needs to be updated, because that informs upstream DNS servers that the zone has been changed and to evict the cached name information that it has on that zone. If you don’t change the serial in the zone, the DNS server is likely to use cached information and not bother to update, even if it reads the SOA records of your DNS server to check for updates.

Verify the delegated DNS records

To verify that you have added the records correctly, you can use the dig command. It is recommended that you do not use nslookup, as this program does not work as well as it says on the label. It’s really only good at reading A, AAAA, and CNAME records, and only sometimes.

First, verify that you have added the NS records correctly. Presumably, only one of the DNS servers will show up, but that is OK. You can replace 8.8.8.8 with your favorite upstream DNS server (eg, 1.1, 9.9.9.9, etc.)

dig @8.8.8.8 NS <subdomain>.<domain>.

So, for example:

dig @8.8.8.8 NS cam.ja4.org.

The response should look something like this; it will share with you the DNS at which to find the nameserver(s). It may only show one DNS server, as shown below:

; <<>> DiG 9.16.4 <<>> NS cam.ja4.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47353
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;cam.ja4.org.			IN	NS

;; ANSWER SECTION:
cam.ja4.org.		21599	IN	NS	ns1-cam.ja4.org.

;; Query time: 60 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Sat Jul 18 09:33:40 EDT 2020
;; MSG SIZE  rcvd: 62

You can then verify that your A records are good by doing the following:

dig @8.8.8.8 A <server>.<domain name>.

So, for example:

dig @8.8.8.8 A ns1-cam.ja4.org

The response should look something like this:

; <<>> DiG 9.16.4 <<>> A ns1-cam.ja4.org.
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21306
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;ns1-cam.ja4.org.		IN	A

;; ANSWER SECTION:
ns1-cam.ja4.org.	59	IN	A	128.153.145.111

;; Query time: 26 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Sat Jul 18 09:41:31 EDT 2020
;; MSG SIZE  rcvd: 60

If those records look correct and you get IP addresses back, you have completed this step successfully, and can move onto the next step. If you do not get these results (or something similar), then you will not be able to continue until you have fixed this. Your DNS records would be unavailable on the public internet.

Installing your DNS server

These instructions are mostly OS-agnostic, but we will be using NixOS for the purpose of these instructions. There are easy parallels to make to this in RHEL and Debian based systems, but the paths and configuration files may be different depending on the system. Package names you may need may include dnsutils, bind9 or named, and perhaps one or two other programs. dnsutils provides the dig command, and bind9 or named provide the DNS server.

First, you need to select some public IP addresses, and make some firewall exceptions. I’m not going to detail things such as SSH into those boxes, but you may want to consider that as you make the firewall rules.

Add firewall rules for DNS access

The following ports need to be opened to the public internet for the DNS service:

Protocol Port Number Reason
UDP 53 DNS typically uses UDP first to communicate with the server
TCP 53 DNS will fall back on TCP on poor connections or for large records. Not required, but good practice

SECURITY NOTICE:

DNS servers are inadvertently one of the worst DDoS tools out there, so if you are not confident your DNS server prohibits recursive DNS queries, do not run it for an extended period, or do not make firewall exceptions. Please note that if you do not make the firewall exceptions, your DNS will not be publicly resolvable, which may make this lab harder to complete.

Because DNS was designed before malicious actors were a common problem, it is very easy to cause DNS amplification attacks, where a malicious actor sends small packets to the service that cause a larger packet to leave the server.

Install the operating system

The basic process is to install your OS, configure the static IP and the host-based firewall, and then install the BIND DNS server. We will configure it in the next step.

Nothing particularly fancy is required for this step. I’ll even give you a config for NixOS (configuration.nix):

{ config, pkgs, ... }:

{
  imports = [
    ./hardware-configuration.nix
  ];

  boot.loader.grub.enable = true;
  boot.loader.grub.version = 2;
  boot.loader.grub.device = "/dev/vda"; # Set this to your boot disk

  networking = { 
    hostName = "ns1"; # change the DNS name of the server if you want, not particularly important for the server to function correctly.
    useDHCP = false;
    firewall.allowedTCPPorts = [ 53 ];
    firewall.allowedUDPPorts = [ 53 ];

    defaultGateway.address = "YOUR HOST's IPv4 GATEWAY ADDRESS"; # For instance, "128.153.144.1"
    defaultGateway.interface = "YOUR HOST's ETHERNET ADAPTER";
    interfaces.<YOUR HOST's ETHERNET ADAPTER>.ipv4 = { # For instance, enp3s0
      addresses = [{ address = "YOUR HOST IPv4 ADDRESS"; prefixLength = 23; }]; # For instance, "128.153.145.103"
    };
  };

  time.timeZone = "US/New_York";

  environment.systemPackages = with pkgs; [
    wget vim dnsutils inetutils curl
  ];

  services.openssh = {
    enable = true;
    ports = [ 13699 ];
  };

  services.bind = {
    enable = true;
    configFile = "/etc/bind/named.conf";
  };

  users.groups.bind.members = [ "named" ];

  users.users.YOURUSERHERE = {
    isNormalUser = true;
    extraGroups = [ "wheel" ];
    openssh.authorizedKeys.keys = [ "YOUR SSH PUBLIC KEY" ];
  };

  system.stateVersion = "20.03";
}

Configure the Bind service

This is the most important step. There are a few different files you will need, and depending on your OS distribution, you may need to modify these instructions a little, since some files may be provided to you, or import from different locations. But, in the end, just make sure the configuration files pull everything in, and check the service logs.

In case you forgot how to systemd, here’s some handy commands (service files may be different depending on OS, for example, named.service):

# Restart the Bind 9 service
systemctl restart bind9.service
# Read the logs for the Bind 9 service
journalctl -eu bind9.service

There are some basic components to the configuration:

  • Where the DNS server is allowed to accept (recursive) queries from. This should only be a local network IF you intend to allow systems to use it as a resolver (for example, if you put this server’s IP in /etc/resolv.conf on your network)
  • The DNS zone file that the DNS records pull from

Here are some live examples for you to use to configure your service.

Boilerplate DNS configuration files

db.0:

;
; BIND reverse data file for broadcast zone
;
$TTL    604800
@       IN      SOA     localhost. root.localhost. (
                              1         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      localhost.

db.127:

;
; BIND reverse data file for local loopback interface
;
$TTL    604800
@       IN      SOA     localhost. root.localhost. (
                              1         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      localhost.
1.0.0   IN      PTR     localhost.

db.255:

;
; BIND reverse data file for broadcast zone
;
$TTL    604800
@       IN      SOA     localhost. root.localhost. (
                              1         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      localhost.

db.local:

  ;
  ; BIND data file for local loopback interface
  ;
  $TTL    604800
  @       IN      SOA     localhost. root.localhost. (
                                2         ; Serial
                           604800         ; Refresh
                            86400         ; Retry
                          2419200         ; Expire
                           604800 )       ; Negative Cache TTL
  ;
  @       IN      NS      localhost.
  @       IN      A       127.0.0.1
  @       IN      AAAA    ::1

db.root is the root name servers. This file is to be updated regularly if it does not do it by itself; it can be fetched from here: https://www.internic.net/domain/named.cache

;       This file holds the information on root name servers needed to 
;       initialize cache of Internet domain name servers
;       (e.g. reference this file in the "cache  .  <file>"
;       configuration file of BIND domain name servers). 
; 
;       This file is made available by InterNIC 
;       under anonymous FTP as
;           file                /domain/named.cache 
;           on server           FTP.INTERNIC.NET
;       -OR-                    RS.INTERNIC.NET
; 
;       last update:     June 08, 2020 
;       related version of root zone:     2020060801
; 
; FORMERLY NS.INTERNIC.NET 
;
.                        3600000      NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET.      3600000      A     198.41.0.4
A.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:ba3e::2:30
; 
; FORMERLY NS1.ISI.EDU 
;
.                        3600000      NS    B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET.      3600000      A     199.9.14.201
B.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:200::b
; 
; FORMERLY C.PSI.NET 
;
.                        3600000      NS    C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET.      3600000      A     192.33.4.12
C.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2::c
; 
; FORMERLY TERP.UMD.EDU 
;
.                        3600000      NS    D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET.      3600000      A     199.7.91.13
D.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2d::d
; 
; FORMERLY NS.NASA.GOV
;
.                        3600000      NS    E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET.      3600000      A     192.203.230.10
E.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:a8::e
; 
; FORMERLY NS.ISC.ORG
;
.                        3600000      NS    F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET.      3600000      A     192.5.5.241
F.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2f::f
; 
; FORMERLY NS.NIC.DDN.MIL
;
.                        3600000      NS    G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET.      3600000      A     192.112.36.4
G.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:12::d0d
; 
; FORMERLY AOS.ARL.ARMY.MIL
;
.                        3600000      NS    H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET.      3600000      A     198.97.190.53
H.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:1::53
; 
; FORMERLY NIC.NORDU.NET
;
.                        3600000      NS    I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET.      3600000      A     192.36.148.17
I.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fe::53
; 
; OPERATED BY VERISIGN, INC.
;
.                        3600000      NS    J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET.      3600000      A     192.58.128.30
J.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:c27::2:30
; 
; OPERATED BY RIPE NCC
;
.                        3600000      NS    K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET.      3600000      A     193.0.14.129
K.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fd::1
; 
; OPERATED BY ICANN
;
.                        3600000      NS    L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET.      3600000      A     199.7.83.42
L.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:9f::42
; 
; OPERATED BY WIDE
;
.                        3600000      NS    M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET.      3600000      A     202.12.27.33
M.ROOT-SERVERS.NET.      3600000      AAAA  2001:dc3::35

User-configured DNS configuration files

These files are where the magic happens. There are two important files that you have to create; named.conf and db.<zonename>. The zone’s file name is not particularly important, the filename is simply imported from named.conf.

named.conf

I’m going to split this up a bit to add comments, but anything in the code blocks is in the final file once.

This first section sets the local ranges to allow recursive DNS. DO NOT allow external IP addresses in this range!

acl a_trusted {
  128.153.0.0/16;
  localhost;
  localnets;
};
options {
  directory "/var/cache/bind";

The forwarders section has public recursive DNS servers that this DNS server can use to find more DNS information from other servers.

  forwarders {
    1.1.1.1;
  };

This section is very important and tells the server where recursion is allowed. If you do not intend to allow people to use recursive DNS, you can change this to no, and remove the a_trusted sections.

  recursion yes;
  allow-recursion {
    a_trusted;
  };

DNSSEC is a protocol used for DNS verification. If you have it, this will enable it.

  dnssec-validation auto;

  auth-nxdomain no;
  listen-on-v6 {
    any;
  };
};

This is where the zones begin to get ingested. The first sets of records are just standard boilerplate zones.

zone "." {
  type hint;
  file "/etc/bind/db.root";
};

zone "localhost" {
  type master;
  file "/etc/bind/db.local";
};

zone "127.in-addr.arpa" {
  type master;
  file "/etc/bind/db.127";
};

zone "0.in-addr.arpa" {
  type master;
  file "/etc/bind/db.0";
};

zone "255.in-addr.arpa" {
  type master;
  file "/etc/bind/db.255";
};

These last two zones are where the fun begins. I have left them populated, but you will want to configure these names according to your configuration.

The first zone is for what is called Forward DNS, which is what most people use when they are trying to visit a website. The second section, which is less common, is the Reverse DNS. The reverse DNS requires the zones to be passed from your ISP and can be ommited for this lab. If you’re in COSI, adding reverse records will not work (except maybe if you use your resolver for your servers, you can possibly inject these records if your domain is not using DNSSEC).

zone "cam.ja4.org" in {
  type master;
  file "/etc/bind/db.cam";
};

zone "145.153.128.in-addr.arpa" in {
  type master;
  file "/etc/bind/db.cam.rvs.145";
};

Configure the Forward DNS Zones

Inside of the forward DNS zones, the configuration is relatively simple. A sample is provided below:

$TTL 3d
@		IN SOA	cam.ja4.org.	cam.cam.ja4.org. (
			1	; serial
			1h	; refresh
			1h	; retry
			1w	; expire
			300 )	; negative caching-ttl

; Nameservers
@		IN NS	ns1-cam.ja4.org.

; IPv4
@		IN A	128.153.145.110
ns1		IN A	128.153.145.111
ns2		IN A	128.153.145.112
ldap1		IN A	128.153.145.113
ldap2		IN A	128.153.145.114
nfs1		IN A	128.153.145.115

If you have other record types, you can certainly add them, for instance, CNAME records, CAA records, and more!

The general syntax used here is <key> IN <record type> <value>, with an exception for the SOA record.

The SOA record is very important, and defines critical information about the zone. The first part of the SOA declares the subdomain, in this case cam.ja4.org.. After that, it defines the contact for this zone (which should be interpreted as an email address) - so here cam.cam.ja4.org would mean that to contact the zone maintainer, you would email cam@cam.ja4.org. This is more of a legacy thing, and I don’t think it’s used much. Then, the fun starts.

  • Serial: the serial number of the zone file. This can be a simple integer counter you increment manually, epoch time, or similar.
  • Refresh: number of seconds after which secondary name servers should query the master for the SOA record, to detect zone changes
  • Retry: number of seconds after which secondary name servers should retry to request the serial number from the master if the master does not respond
  • Expire: number of seconds after which secondary name servers should stop answering request for this zone if the master does not respond
  • Negative Caching TTL - This is the TTL to use for records that are invalid; for example, if you try to resolve nonexistant.example.com, DNS servers will cache that response for the number of seconds specified here

In DNS syntax, @ has special meaning - if you find it in the cam.ja4.org record (for example, @.cam.ja4.org.), this will resolve the root of the name. There is also *, which is a wildcard that will match any key record.

In this example zonefile, @ points at an IP address, wich means that all servers that contact cam.ja4.org will be directed at that IP address.

If you have made this zone file, you are ready to start up your DNS server and proceed to validation.

Configure the Reverse DNS Zones

This is only necessary if you are creating reverse records and actually get them delegated to your nameserver. Reverse records convert IP addresses back into domain names. Worth noting, you can have as many CNAME and A and AAAA records point at the same IP address as you would like, but only one of them can be it’s reverse.

$TTL 3d
@		IN SOA	cam.ja4.org.	cam.cam.ja4.org. (
			1	; serial
			1h	; refresh
			1h	; retry
			1w	; expire
			300 )	; negative caching-ttl

; Nameservers
@		IN NS	ns1-cam.ja4.org.

; IPv4 Reverse
; Note: the trailing dots are VERY IMPORTANT!
111		IN PTR	ns1.cam.ja4.org.
112		IN PTR	ns2.cam.ja4.org.
113		IN PTR	ldap1.cam.ja4.org.
114		IN PTR	ldap2.cam.ja4.org.
115		IN PTR	nfs1.cam.ja4.org.

Verify your DNS server

Now that you have your DNS server stood up, you probably want to verify it.

The best way to do this is to query it with the dig command. First, you would want to make sure that your records can be resolved from the server itself (by specifying @<Your DNS server IP>), but then later you will want to verify that your records can be recieved from any DNS server on the public internet (such as @8.8.8.8).

Example queries can be the following:

dig @8.8.8.8 google.com A
dig @8.8.8.8 cslabs.clarkson.edu A
dig @1.1.1.1 yahoo.com A
dig @9.9.9.9 talks.cslabs.clarkson.edu CNAME
dig @9.9.9.9 talks.cslabs.clarkson.edu A

You can also query for pretty much any kind of record.

Reverse DNS records are a bit more interesting - you pass the IP address and -x:

dig -x 128.153.145.3 @8.8.8.8

That should be about it.

TODO: Verify that your server is not doing recursive DNS on the public internet.

Further Research

  • recursion.cslabs.clarkson.edu is a custom DNS server that recurses the DNS lookup to add another recursion every few seconds. Hopefully it’s still running.