Arts and Craft of Information Systems

DNS Firewalling with BIND: Performance of Approaches

Part 1: an Anti-Guide

I once received an email from a hosting provider telling me that my DNS server was participating in an amplification attack. BIND is a tricky piece of software which can be configured to cause problems. My server was resolving queries without the necessary safeguards to do so publically, to a global network. I recently decided to self-host a BIND instance again. A simple instance which will

  • Resolve lookups for clients on internal private networks
  • Block ads, malware or any unwanted domains

Solving the right problem the wrong way

It's tempting to escape the complexity of ISC's documentation by looking at finished solutions. There are numerous guides and automation scripts found online aimed at implementing a DNS firewall with BIND and everyone is different.

After introducing some basics, I'll outline two approaches that accomplish DNS firewalling.

  • Using DNS spoofing – similar to hosts blocking – by defining multiple zones.
  • Later: using a Request Policy Zone (RPZ), a security feature designed with access-control in mind.

The first method is more frequent in earlier and dated documents. In BIND's 35 year history, RPZ is a "recent" feature so it makes sense that earlier approaches are there to find. Note that this isn't an exhaustive list by any means.

Primer

Let's walk through a few points about BIND and the structure used to manipulate domains. The basics are always there, informing us how we might solve a higher level problems.

etc
└── bind
    ├── bind.keys
    ├── db.0                     # zone file
    ├── db.127                   # zone file
    ├── db.255                   # zone file
    ├── db.empty                 # zone file
    ├── db.local                 # zone file
    ├── named.conf               # configuration
    ├── named.conf.default-zones # configuration
    ├── named.conf.local         # configuration
    ├── named.conf.options       # configuration
    ├── rndc.key
    └── zones.rfc1918

Zone files

BIND uses zone files to define DNS Resource Records (RR). An RR describe how a domain should resolve to some resource. A zone file can state what IP address will be returned when asking for imdb.com or peach.cool.

$TTL 60
@            IN    SOA  localhost. hostmaster.localhost.  (
                     4   ; serial
                     3H  ; refresh
                     1H  ; retry
                     1W  ; expiry
                     1H) ; minimum

             IN      NS    localhost.
@            IN      A     10.100.100.198
*            IN      A     10.100.100.198
/etc/bind/db.custom

This zone file which will resolve any domain @ and subdomains * to the addresses shown in the A record.

Zone statement

For a domain to point to the records in db.custom we create a zone in BIND's settings. The zone include it's configuration and location of the zone file. Most importantly, the name of the zone will be interpreted by BIND as an actual domain*. For example zone "example.tld" means that example.tld will be used in place of @ found in the zone file.

BIND will do this for us as long as there is no $ORIGIN described in the zone file

There are several named.conf files but we'll add zones to named.conf.local. The new zone entry for example.tld looks like so:

zone "example.tld" {
    type master;
    file "/etc/bind/db.custom";
};
/etc/bind/named.conf.local

BIND can now link the dots and resolve example.tld to the addresses in our zone file. An adig from the client-side can confirm that the domain resolves to the wanted address.

user@mbpro:~|⇒  adig example.tld          
id: 24487
flags: qr aa rd ra 
opcode: QUERY
rcode: NOERROR
Questions:
	example.tld    .		A
Answers:
	example.tld    .	3600	A	10.100.100.198
NS records:
Additional records:

Firewalling with Multiple zones

☠ This method is not recommended

With the ability to serve arbitrary records, we can (1) make a list of unwanted domains and (2) turn them into zones. Each will point to the same zone file.

zone "ads.nytimes.com" { 
    type master; 
    file "/etc/bind/db.blocked"; 
    };
zone "ads.snapchat.com" { 
    type master; 
    file "/etc/bind/db.blocked"; 
    };
zone "zzzrtrcm2.com" {
    type master; 
    file "/etc/bind/db.blocked";
    };
zone "secure.webconnect.net" {
    type master; 
    file "/etc/bind/db.blocked";
    };
/etc/bind/named.conf.local

Building these zones from a blocklist such as hosts yields close to 90,000 entries. The resulting named.conf.local grows to over 6MB after conversion to zone formatting. All zones point to db.blocked which consists of some bare essentials. Note that the A records can be left out.

$TTL	86400
@	IN	SOA	localhost. hostmaster.localhost. (
			      1		; Serial
			 604800		; Refresh
			  86400		; Retry
			2419200		; Expire
			  86400 )	; Negative Cache TTL
;
@	IN	 NS	   localhost.
@       IN       A         0.0.0.0
*       IN       A         0.0.0.0
/etc/bind/db.blocked

The zone file will resolve to an unusable address and should break the domain. Testing from a client again, shows that this domain has been spoofed and is now pointing to a non-routable 0.0.0.0.

user@mbpro:~|⇒  adig secure.webconnect.net
id: 64487
flags: qr aa rd ra 
opcode: QUERY
rcode: NOERROR
Questions:
	secure.webconnect.net.		A
Answers:
	secure.webconnect.net.	3600	A	0.0.0.0
NS records:
Additional records:

Performance issues with Multiple zones

Degraded performance is noticed after implementing this approach.

  • Start and restart times consistently much longer
  • Memory consumption consistently too high

In normal circumstances when starting the service, it's up and answering lookups in a couple of seconds. During this test, BIND startup required close to a minute to complete as it loaded the zones. Memory consumption was high, putting BIND consistently in the top. Running with this configuration became unsustainable as BIND would be die off as it maxed out the system.

 CPU[|                                                       1.3%]   Tasks: 67, 135 thr; 1 running
  Mem[|||||||||||||||||||||||||||||||||||||||||||||||||1.67G/1.94G]   Load average: 0.06 0.01 0.00 
  Swp[|||||||||||||||||||||||||||||||||||||||||         688M/1024M]   Uptime: 24 days, 12:21:54

    PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
 133237 bind       20   0 1821M 1414M  5316 S  0.0 71.2  0:00.19 /usr/sbin/named -f -u bind
 133238 bind       20   0 1821M 1414M  5316 S  0.0 71.2  0:27.43 /usr/sbin/named -f -u bind
 133239 bind       20   0 1821M 1414M  5316 S  0.0 71.2  0:02.19 /usr/sbin/named -f -u bind
 133240 bind       20   0 1821M 1414M  5316 S  0.0 71.2  0:00.02 /usr/sbin/named -f -u bind
 133226 bind       20   0 1821M 1414M  5316 S  0.0 71.2  0:29.88 /usr/sbin/named -f -u bind
 131545 *****      20   0 1879M  101M 16496 S  0.0  5.1  0:00.00 /usr/bin/node current/index.js
htop

None of these issues are experienced when implementing the equivalent blocklist using a Response Policy Zone. Using RPZ also provides other benefits in shaping the DNS service. A later walkthrough will cover RPZ in BIND.


BIND 9
Versatile, classic, complete name server software
--
GitHub - StevenBlack/hosts: 🔒 Consolidating and extending hosts files from several well-curated sources. Optionally pick extensions for porn, social media, and other categories.
🔒 Consolidating and extending hosts files from several well-curated sources. Optionally pick extensions for porn, social media, and other categories. - GitHub - StevenBlack/hosts: 🔒 Consolidating a...