Concept for DNS security with Bind9, Pi-hole and OpenDNS

DNS is one of the oldest services in the internet RfC882 but essential for any communication in a network or in the internet. DNS is resolving hostnames into IP addresses and vice versa (A and PTR records). In my network I have currently two DNS servers running on virtual machines and due to ongoing new threats from the Internet like malware, crypto-miners, phishing, ransomware, tracking and so on I was thinking about a new security concept which includes DNS as part of the defense.

For the concept I split DNS:

  • Enterprise DNS, for DSN queries in my local network

  • Internet DNS, for everything else in the Internet

Enterprise DNS

In Enterprise DNS, I am using three Bind9 name servers. One name server is a so-called hidden primary. This server manages the zone (DNS zone) and corresponding in-addr.arpa (Reverse DNS lookup) zone for my network. All modifications and changes are performed on this server and clients cannot query this server directly. This DNS server replicates then the zones to the two other DNS servers by a DNS zone transfer. The two DNS servers can be queried by clients.

Note: DNS queries work with UDP/53, DNS zone transfers work with TCP/53.

Internet DNS

The secure the DNS queries I’m using for public, Internet DNS Pi-hole in my network and as upstream DNS server OpenDNS. The two internal DNS server (DNS #1 and DNS #2) are using the server with Pi-hole as so-called forwarder. On Pi-hole I’m using various blocklists from The Block List Project to protect my clients from various threats in the internet. As another line of defense I’m using OpenDNS which does a similar thing like Pi-hole.

Note: Internet traffic must pass through my ASA firewalls, therefore I’m adding and ACL for DNS traffic to OpenDNS.

Concept for DNS security with Bind9, Pi-hole and OpenDNS
Concept for DNS security with Bind9, Pi-hole and OpenDNS

Setup on hidden primary DNS

On my hidden primary DNS I’m managing two zones. I’m disabling in the file /etc/bind/named.conf.local on both zones the DNS lookup with allow-query none so that no client can query directly this DNS server. For the zone-transfer, I’m enabling with allow-transfer IP1, IP2 my DNS #1 and DNS #2 server.

  zone "nnn.nnn.in-addr.arpa" {
    type master;
    file "/etc/bind/master/db.nnn.nnn";
    allow-query { none; };                                # Never answer any DNS queries
    allow-transfer { nnn.nnn.nnn.nnn; nnn.nnn.nnn.nnn; }; # Transfer to DNS #1 and DNS #2
  };
  zone "local.domain" {
    type master;
    file "/etc/bind/master/db.local.domain";
    allow-query { none; };                                # Never answer any DNS queries
    allow-transfer { nnn.nnn.nnn.nnn; nnn.nnn.nnn.nnn; }; # Transfer to DNS #1 and DNS #2
  };
Bind9 primary hidden DNS: /etc/bind/named.conf.local

In addition, I’m allowing in the file /etc/bind/named.conf.options the zone-transfer to DNS #1 and DNS #2 and disable any forwarders.

  allow-transfer { nnn.nnn.nnn.nnn; nnn.nnn.nnn.nnn; };   # Transfer to DNS #1 and DNS #2
  notify yes;                                             # Notify when zones have changed
  forwarders {                                            # Do not use any forwarder
  };
Bind9 primary hidden DNS: /etc/bind/named.conf.options

Setup on DNS #1 and DNS #2

On my two DNS servers for clients I’m setting up in the file /etc/bind/named.conf.local the IP address of the hidden primary DNS as master and with allow-query any I’m allowing the DNS queries from clients. With the option allow-transfer none I’m disabling any zone-transfers from both DNS servers.

  zone "nnn.nnn.in-addr.arpa" {
    type slave;
    masters { nnn.nnn.nnn.nnn; };                  # IP of hidden primary DNS
    file "/var/cache/bind/slave/db.nnn.nnn";
    allow-query { any; };                          # Allow DNS queries
    allow-transfer { none; };                      # Never transfer zone files
  };
  zone "local.hacks" {
    type slave;
    masters { none.none.none.none; };              # IP of hidden primary DNS
    file "/var/cache/bind/slave/db.local.domain";
    allow-query { any; };                          # Allow DNS queries
    allow-transfer { none; };                      # Never transfer zone files
  };
Bind9 DNS #1 and DNS #2: /etc/bind/named.conf.local

In the file /etc/bind/named.conf.options I’m allowing queries from my local network and localhost. Zone-transfers are disabled here as well. As forwarders, I’m setting up here the IP address of the Pi-hole server.

  allow-query { nnn.nnn.nnn.nnn/16; 127.0.0.0/24; }; # Allow DNS queries from LAN
  allow-transfer { none; };                          # Never transfer zone files
  forwarders {
      nnn.nnn.nnn.nnn;                               # IP of Pi-hole as forwader
  };
Bind9 DNS #1 and DNS #2: /etc/bind/named.conf.options

Setup Pi-hole

The installation of Pi-hole is easy and can be accomplished with the One-step automated install process as described in the basic install documentation. Screenshots of the install process are available at: Album: Pi-hole installation on Debian Linux.

Pi-hole generates a password for the Web UI, however I would like to change it. This can be accomplished by log in into the server and executing the following command:

  pihole -a -p
Pi-hole: Change Web UI password

In addition I like to secure the Web UI with HTTPS. This means a certificate needs to be generated and configured in lighttpd. The generate and manage my certificates I’m using XCA X-Certificate and Key management. After generating the Certificate Signing Request (CSR) and signing it with my Root-CA, I’m exporting the certificate and key (PEM+KEY) into a .pem file. This .pem file is then moved to the Pi-hole server into /etc/lighttpd/ directory.

To enable HTTPS with lighttpd, I’m editing the file /etc/lighttpd/external.conf and adding a configuration as shown below. Because this is the first time I’m using lighttpd, I am following the configuration at How can I enable HTTPS (SSL/TLS) for my Pi-hole Web Interface?. However I had to enable the module openssl and made a small adjustment because the certificate was signed by my own PKI.

  # Enable mod_openssl
  server.modules += ("mod_openssl")

  # Ensure the Pi-hole Block Page knows that this is not a blocked domain
  $HTTP["host"] == "pihole.local.domain" {
    setenv.add-environment = ("fqdn" => "true")

    # Enable the SSL engine with cert signed by own PKI, only for this specific host
    $SERVER["socket"] == ":443" {
      ssl.engine = "enable"
      ssl.pemfile = "/etc/lighttpd/pihole.local.domain.pem"
      ssl.honor-cipher-order = "enable"
      ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
      ssl.use-sslv2 = "disable"
      ssl.use-sslv3 = "disable"
    }

    # Redirect HTTP to HTTPS
    $HTTP["scheme"] == "http" {
      $HTTP["host"] =~ ".*" {
        url.redirect = (".*" => "https://%0$0")
      }
    }
  }
Pi-hole: Enable HTTPS in file /etc/lighttpd/external.conf

After restarting the web server with systemctl restart lighttpd the Web UI is now secured with HTTPS.

Pi-hole: HTTPS enabled for Web UI
Pi-hole: HTTPS enabled for Web UI

In the next step I’m adding my preferred block lists from The Block List Project into Pi-hole.

Pi-hole: Add blocklists
Pi-hole: Add blocklists

I’m also selecting OpenDNS as Upstream DNS Servers. OpenDNS uses the IP address 208.67.222.222 and 208.67.220.220 which I’m using later in the ASA Firewall ACL.

Pi-hole: Select Upstream DNS
Pi-hole: Select Upstream DNS

Setup Cisco ASA Firewall ACL

On my ASA Firewalls I’m using a simple ACL to pass DNS queries to the OpenDNS servers. I’m grouping both OpenDNS servers together in a grp.opendns-server group and allow UDP/53. This prevents the usage of any other public DNS server which might be used by untrustworthy devices like Smart-TVs or other devices used for home automation, IoT, and so on.

ASA Firewall: ACL for OpenDNS
ASA Firewall: ACL for OpenDNS

Setup OpenDNS

With a free OpenDNS account, I’m using as last line of defense the provided security filtering like basic malware/botnet protection, phishing protecting and blocking of internal IP addresses.

OpenDNS: Security filtering
OpenDNS: Security filtering

As you can see, 29.8% of DNS queries (and therefore traffic) is blocked by the block lists. I’m quite surprised about the 29.8% because this is “normal” web browsing, work-related traffic, news from German public service broadcasts like Tagesschau and some IT/Network/Programming videos on YouTube.

Screenshots of the Pi-hole setup are available here: Album: Pi-hole installation on Debian Linux


Share: