Home Blog Certs Knowledge Base About

LPIC-2 207.3 โ€” DNS Server Security

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 named has write permission to the directory specified in the directory option in named.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.conf are relative to the new root, without the /var/cache/bind prefix.

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:

  1. Parse command-line arguments
  2. chroot (enter jail)
  3. Write PID file (still as root)
  4. Drop to named user

The named user must exist in the copy of /etc/passwd inside 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-notify does 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 .private file!


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

RecordPurpose
DNSKEYZone public key (ZSK or KSK)
RRSIGDigital signature of a record set
NSECProof of non-existence of a name
NSEC3Same, but hashed (protects against zone enumeration)
DSHash of DNSKEY in the parent zone

NSEC vs NSEC3:

  • NSEC: vulnerable to zone walking โ€” reveals the next name
  • NSEC3: returns a hash of the next name โ€” enumeration requires brute force

dnssec-keygen and dnssec-signzone

dnssec-keygen options:

OptionPurposeValues
-aAlgorithmRSASHA256, RSASHA1, DSA, HMAC-MD5, HMAC-SHA512
-bKey sizeRSA: 512โ€“4096; DSA: 512โ€“1024 (ร—64); HMAC-MD5: 1โ€“512
-nName typeZONE, HOST, ENTITY, USER, OTHER
-f KSKGenerate KSKโ€”
-rRandomness 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

UtilityPurpose
dnssec-verifyVerify a signed zone
dnssec-checkdsCheck DS records
dnssec-coverageCheck key coverage
dnssec-dsfromkeyGenerate DS from DNSKEY
dnssec-settimeManage key timestamps
dnssec-revokeRevoke 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):

FieldPositionValueDescription
Matching Type3rd0Data not hashed
1SHA-256 โœ“
2SHA-512
Selector2nd0Full certificate
1Public key only
Certificate Usage1st0 PKIX-TAPublic CA (X.509)
1 PKIX-EEEnd-entity certificate (X.509)
2 DANE-TAPrivate CA
3 DANE-EEEnd-entity without CA โ† maximum benefit

With value 3, DANE completely eliminates dependency on third-party CAs. Requires working DNSSEC.


Exam Cheat Sheet

Security Directives

DirectiveWherePurpose
version "hidden"optionsHide BIND version
allow-queryoptions, zoneWho can query
allow-transferoptions, zoneWho can receive zone transfer
allow-updatezoneWho can make dynamic updates
allow-update-forwardingzoneUpdates via slave
allow-notifyzoneWho sends NOTIFY messages
allow-recursionoptionsWho may use recursion
recursion nooptionsDisable recursion globally
forwardersoptionsUpstream servers
heartbeat-interval 0 + dialup yesoptionsDisable 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

MethodProtects AgainstFiles / Utilities
Unprivileged userPrivilege escalation-u named, /etc/passwd
Chroot jailSystem compromise-t /var/cache/bind
ACL + allow-*Unauthorized accessnamed.conf
TSIGIP spoofing on transfersdnssec-keygen, key {}
DNSSECDNS response spoofingdnssec-keygen, dnssec-signzone
DANECA compromiseTLSA records + DNSSEC
Split DNSInternal infrastructure leakageview {}, forwarders {}

Algorithms (exam facts)

AlgorithmTypeUseStatus
HMAC-MD5SymmetricTSIGDeprecated
HMAC-SHA256SymmetricTSIGRecommended
HMAC-SHA512SymmetricTSIGRecommended
RSASHA1AsymmetricDNSSECBeing deprecated
RSASHA256AsymmetricDNSSECRecommended
DSAAsymmetricDNSSECBeing 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)