Exam topic 207.3 โ Securing a DNS Server (weight: 2). Covers BIND security strategies, chroot jails, split DNS configuration, TSIG zone transfer authentication, DNSSEC record signing, and DANE certificate binding.
DNS Security Strategies
The “security through obscurity” principle does not provide absolute protection, but increases the time needed to detect an attack.
Hiding the BIND Version
How an attacker checks the version:
dig @target chaos version.bind txt
Hide it in named.conf:
options {
version "hidden";
};
ACLs
acl "trusted" {
localhost;
192.168.1.0/24;
};
ACL labels: update in one place โ applies everywhere.
Restricting Queries (allow-query)
options {
allow-query { trusted; };
};
// Or for a specific zone:
zone "example.com" IN {
allow-query { 192.168.1.0/24; };
};
Restricting Zone Transfers (allow-transfer)
Danger of open zone transfers:
dig axfr @ns.example.com example.com
# Reveals the ENTIRE infrastructure: VPN, offices, internal hosts
host -l example.com ns.example.com
# Same thing, different format
Correct configuration:
// Globally deny
options {
allow-transfer { none; };
};
// Allow only to slaves
acl "my_slave_servers" {
10.0.1.2;
10.0.1.3;
};
zone "example.org" IN {
type master;
allow-transfer { my_slave_servers; };
};
// On slave โ always deny further transfers
zone "example.org" IN {
type slave;
allow-transfer { none; };
};
Warning: Don’t forget to protect the reverse zone the same way!
Restricting Dynamic Updates
zone "example.com" IN {
allow-update { none; }; // deny all
allow-update-forwarding { none; }; // deny via slave
allow-notify { 192.168.0.104; }; // only master sends NOTIFY
};
Running BIND as an Unprivileged User
Problem: BIND may run as root by default. If compromised, the attacker gets full system access.
Why not nobody? Many other services already run as nobody โ undesirable interaction is possible.
Solution: create a dedicated named user.
# Manual launch
named -u named -g named
# Debian (/etc/init.d/bind):
start-stop-daemon ... --exec /usr/sbin/named -- -u named -g named
# -- separates start-stop-daemon options from named options
# Red Hat/CentOS (already configured by default):
systemctl status named
# Process: ExecStart=/usr/sbin/named -u named
# Autostart (Red Hat):
chkconfig named on
Verify permissions on zone directory:
ls -l /var/named
# drwxrwx---. named named data/
# -rw-rw----. root named named.ca
Warning: Make sure
namedhas write permission to the directory specified in thedirectoryoption innamed.conf.
Chroot Jail for BIND
Chroot isolates a process: the specified directory becomes the root /. The process cannot escape beyond it.
Preparing the Chroot Environment
New root: /var/cache/bind
# 1. Create /dev/null
mknod -m 666 /var/cache/bind/dev/null c 1 3
# 2. Copy user files
cd /var/cache/bind/etc
cp -p /etc/{passwd,group} .
cp /etc/ld.so.cache .
cp /etc/localtime .
# 3. Find required libraries
ldd /usr/sbin/named
# libc.so.6 => /lib/libc.so.6
# 4. Copy libraries (symlinks + real files)
cd /var/cache/bind/lib
cp -pd /lib/{libc.so.6,libc-2.1.3.so,ld-linux.so.2,ld-2.1.3.so} .
# 5. Copy BIND binaries
cp -p /usr/sbin/named{,-xfer} /var/cache/bind/usr/sbin
cp -p /usr/sbin/rndc /var/cache/bind/usr/sbin
Chroot environment structure:
/var/cache/bind/ โ new root (/)
โโโ dev/null
โโโ etc/
โ โโโ passwd โ copy with named user
โ โโโ group
โ โโโ ld.so.cache
โ โโโ localtime
โ โโโ bind/
โ โโโ named.conf
โ โโโ example.com.zone
โโโ lib/ โ BIND libraries
โโโ usr/sbin/
โ โโโ named
โ โโโ named-xfer
โ โโโ rndc
โโโ var/cache/bind/
โโโ bind.log
All paths in
named.confare relative to the new root, without the/var/cache/bindprefix.
Starting BIND in chroot
# Command line
named -t /var/cache/bind
# Debian
start-stop-daemon ... --exec /usr/sbin/named -- -t /var/cache/bind
# Red Hat
daemon ... /sbin/named -t /var/cache/bind
# Using the bind-chroot package (CentOS/RHEL โ simpler):
yum install bind-chroot
/usr/libexec/setup-named-chroot.sh /var/named/chroot on
systemctl stop named && systemctl disable named
systemctl start named-chroot && systemctl enable named-chroot
# Zone files are now in: /var/named/chroot/var/named/
BIND enters chroot immediately after parsing arguments, before reading configuration.
Logging in chroot
Standard syslog does not work โ no access to /dev/log. Use a file:
logging {
channel some_log {
file "bind.log" versions 3;
severity info;
};
category default { some_log; };
};
// Log: /var/cache/bind/bind.log
Combining chroot + unprivileged user
named -u named -g named -t /var/cache/bind
BIND startup sequence:
- Parse command-line arguments
- chroot (enter jail)
- Write PID file (still as root)
- Drop to
nameduser
The
nameduser must exist in the copy of/etc/passwdinside the chroot. It need not exist outside the jail.
Split DNS
Used for: different answers to internal vs external clients; isolating internal zones from the internet.
Two Servers on Different Hosts
Internet โ [liongate 192.168.72.1] โ forwarders โ [privdns 192.168.72.2]
visible server internal master
named.conf on privdns (internal master):
// Internal root โ for itself only
zone "." IN {
type master;
file "/etc/bind/internal.root.zone";
allow-transfer { none; };
allow-query { none; };
};
// Internal zone โ only for liongate
zone "exworks" IN {
type master;
file "/etc/bind/exworks.zone";
allow-transfer { none; };
allow-query { 192.168.72.1; };
};
options {
recursion no;
fetch-glue no;
};
// Do NOT include zone "." type hint!
named.conf on liongate (forwarder):
options {
forwarders {
192.168.72.2; // privdns โ first (internal)
224.121.121.99; // ISP DNS โ for external
};
};
Two Servers on the Same Host
When no separate host is available โ both servers on liongate:
[liongate]
โโโ Internal (chroot, port 5353, user inamed)
โ master for ".", "exworks", reverse zone
โโโ Visible (port 53, user vnamed)
slave for "exworks" + forwarders โ ISP
Internal server named.conf:
options {
directory "/var/cache/bind";
listen-on port 5353 { 127.0.0.1; };
recursion no;
fetch-glue no;
};
zone "exworks" IN {
type master;
file "/etc/bind/exworks.zone";
allow-transfer { 127.0.0.1; }; // visible server only
allow-query { 127.0.0.1; };
};
Visible server named.conf:
options {
directory "/var/cache/bindVisible";
forwarders { 224.121.121.99; }; // ISP for external queries
};
zone "exworks" IN {
type slave;
masters port 5353 { 127.0.0.1; }; // master on non-standard port
file "exworks.slave";
allow-transfer { none; };
allow-query { 127.0.0.1; 192.168.72.0/24; };
};
NOTIFY problem:
also-notifydoes not support port numbers. When a zone changes, restart the visible server manually.
TSIG โ Transaction Signatures
TSIG (Transaction SIGnatures) secures the communication channel between servers for zone transfers. Uses a shared secret + HMAC.
Used for: zone transfers, dynamic updates, NOTIFY, recursive queries.
Generating the Key
# On the master server:
dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST -r /dev/urandom mykey
# Creates: Kmykey.+165+XXXXX.key and Kmykey.+165+XXXXX.private
# For HMAC โ both files contain the SAME key
# Do not use HMAC-MD5 โ deprecated!
# Recommended: HMAC-SHA256 or HMAC-SHA512
Configuring the Master Server
Create /etc/bind/tsig.key:
key "TRANSFER" {
algorithm hmac-sha512;
secret "XIQDYlGaIbWfyopYHS1vtFr...KJQ==";
};
# Slave server 1
server 10.0.1.2 {
keys { TRANSFER; };
};
# Slave server 2
server 10.0.1.3 {
keys { TRANSFER; };
};
Include in named.conf:
include "/etc/bind/tsig.key";
Reload and verify:
rndc reload
rndc tsig-list # list active TSIG keys
Restrict zone transfer to key:
zone "example.com" {
type master;
file "example.com.zone";
allow-transfer { key TRANSFER; };
};
Configuring the Slave Server
Same tsig.key file, same secret, but server points to the master:
key "TRANSFER" {
algorithm hmac-sha512;
secret "XIQDYlGaIbWfyopYHS1vtFr...KJQ=="; // same!
};
# Master server
server 10.0.1.1 {
keys { TRANSFER; };
};
Then include in named.conf and rndc reload.
Warning: The key file must only be accessible to the named process. Do not type the key manually โ copy it from the
.privatefile!
DNSSEC โ DNS Security Extensions
DNSSEC protects against DNS response spoofing (cache poisoning) through digital signatures on records.
How It Works
Root servers (.) โ trust anchor (since May 6, 2010)
โ sign child zone's KSK
.com / .ru / .nl servers
โ sign KSK
example.com servers
โ ZSK signs zone records
Client verifies the chain from bottom up
The public key of example.com is published on .com parent zone servers.
DNSSEC Record Types
| Record | Purpose |
|---|---|
DNSKEY | Zone public key (ZSK or KSK) |
RRSIG | Digital signature of a record set |
NSEC | Proof of non-existence of a name |
NSEC3 | Same, but hashed (protects against zone enumeration) |
DS | Hash of DNSKEY in the parent zone |
NSEC vs NSEC3:
NSEC: vulnerable to zone walking โ reveals the next nameNSEC3: returns a hash of the next name โ enumeration requires brute force
dnssec-keygen and dnssec-signzone
dnssec-keygen options:
| Option | Purpose | Values |
|---|---|---|
-a | Algorithm | RSASHA256, RSASHA1, DSA, HMAC-MD5, HMAC-SHA512 |
-b | Key size | RSA: 512โ4096; DSA: 512โ1024 (ร64); HMAC-MD5: 1โ512 |
-n | Name type | ZONE, HOST, ENTITY, USER, OTHER |
-f KSK | Generate KSK | โ |
-r | Randomness source | /dev/urandom (recommended) |
# ZSK for the zone
dnssec-keygen -a RSASHA256 -b 1024 -n ZONE example.com
# Creates: Kexample.com.+008+XXXXX.key and .private
# KSK for the zone
dnssec-keygen -a RSASHA256 -b 2048 -f KSK -n ZONE example.com
# TSIG key (symmetric)
dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST mykey
Filename format: K + name + + + algorithm number + + + footprint + .key/.private
# Sign the zone
dnssec-signzone -K /path/to/keys -o example.com example.com.zone
# Creates: example.com.zone.signed (with RRSIG records)
# Load keys manually
rndc loadkeys example.com
Automatic signing (named.conf):
zone "example.com" {
type master;
file "example.com.zone";
auto-dnssec maintain;
inline-signing yes;
key-directory "/etc/bind/keys";
};
Other DNSSEC Utilities
| Utility | Purpose |
|---|---|
dnssec-verify | Verify a signed zone |
dnssec-checkds | Check DS records |
dnssec-coverage | Check key coverage |
dnssec-dsfromkey | Generate DS from DNSKEY |
dnssec-settime | Manage key timestamps |
dnssec-revoke | Revoke a key |
Warning: DNSSEC does not protect zone transfers and dynamic updates โ use TSIG for those.
DANE โ DNS-Based Authentication of Named Entities
The CA problem: browsers trust any of 1000+ certificate authorities. Compromise of one CA is catastrophic.
Example โ DigiNotar (2011): attackers generated fraudulent certificates for major sites. Browsers accepted them as valid. DigiNotar went bankrupt within weeks.
DANE uses DNSSEC to bind TLS certificates to DNS names via TLSA records.
TLSA record syntax:
_443._tcp.www.example.com. IN TLSA 0 0 1 <hash>
# _port._protocol.server_name
Three fields (read right to left):
| Field | Position | Value | Description |
|---|---|---|---|
| Matching Type | 3rd | 0 | Data not hashed |
1 | SHA-256 โ | ||
2 | SHA-512 | ||
| Selector | 2nd | 0 | Full certificate |
1 | Public key only | ||
| Certificate Usage | 1st | 0 PKIX-TA | Public CA (X.509) |
1 PKIX-EE | End-entity certificate (X.509) | ||
2 DANE-TA | Private CA | ||
3 DANE-EE | End-entity without CA โ maximum benefit |
With value
3, DANE completely eliminates dependency on third-party CAs. Requires working DNSSEC.
Exam Cheat Sheet
Security Directives
| Directive | Where | Purpose |
|---|---|---|
version "hidden" | options | Hide BIND version |
allow-query | options, zone | Who can query |
allow-transfer | options, zone | Who can receive zone transfer |
allow-update | zone | Who can make dynamic updates |
allow-update-forwarding | zone | Updates via slave |
allow-notify | zone | Who sends NOTIFY messages |
allow-recursion | options | Who may use recursion |
recursion no | options | Disable recursion globally |
forwarders | options | Upstream servers |
heartbeat-interval 0 + dialup yes | options | Disable WAN connections |
Key Commands
# Hide BIND version (check by attacker)
dig @server chaos version.bind txt
# Zone transfer (should be blocked)
dig axfr @server zonename
host -l zonename nameserver
# Generate TSIG key
dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST keyname
# Generate ZSK
dnssec-keygen -a RSASHA256 -b 1024 -n ZONE example.com
# Generate KSK
dnssec-keygen -a RSASHA256 -b 2048 -f KSK -n ZONE example.com
# Sign zone
dnssec-signzone -K /keys -o example.com example.com.zone
# Run BIND in chroot as unprivileged user
named -u named -g named -t /var/cache/bind
Protection Methods Comparison
| Method | Protects Against | Files / Utilities |
|---|---|---|
| Unprivileged user | Privilege escalation | -u named, /etc/passwd |
| Chroot jail | System compromise | -t /var/cache/bind |
| ACL + allow-* | Unauthorized access | named.conf |
| TSIG | IP spoofing on transfers | dnssec-keygen, key {} |
| DNSSEC | DNS response spoofing | dnssec-keygen, dnssec-signzone |
| DANE | CA compromise | TLSA records + DNSSEC |
| Split DNS | Internal infrastructure leakage | view {}, forwarders {} |
Algorithms (exam facts)
| Algorithm | Type | Use | Status |
|---|---|---|---|
HMAC-MD5 | Symmetric | TSIG | Deprecated |
HMAC-SHA256 | Symmetric | TSIG | Recommended |
HMAC-SHA512 | Symmetric | TSIG | Recommended |
RSASHA1 | Asymmetric | DNSSEC | Being deprecated |
RSASHA256 | Asymmetric | DNSSEC | Recommended |
DSA | Asymmetric | DNSSEC | Being deprecated |
Quick Q&A
Q: How to run BIND as user named in chroot /var/cache/bind?
A: named -u named -g named -t /var/cache/bind
Q: Which utility generates both TSIG and DNSSEC keys?
A: dnssec-keygen
Q: What must be done on the slave when configuring TSIG?
A: Create tsig.key with the same secret, but server points to the master
Q: How does TSIG differ from DNSSEC?
A: TSIG โ point-to-point between servers (transfers, updates); DNSSEC โ end-to-end to the client (response authenticity)
Q: Which Certificate Usage value in TLSA gives maximum DANE benefit?
A: 3 (DANE-EE) โ no dependency on any CA
Q: Why does the slave need allow-transfer { none; }?
A: The slave should not redistribute data further โ it only receives from master
Q: What is NSEC3 and why is it needed?
A: NSEC3 returns a hash of the next name instead of the name itself โ protects against zone walking (enumeration)