DNSSEC signing your zones in BIND is surprisingly less about security and more about authenticity.
Let’s watch a zone get signed. We have a simple zone, example.com, with a few records.
# dig @localhost example.com SOA
; <<>> DiG 9.18.12-DEV <<>> @localhost example.com SOA
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12345
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; ADDITIONALS:
;; NS localhost.
;; QUESTION SECTION:
;example.com. IN SOA
;; ANSWER SECTION:
example.com. 3600 IN SOA localhost. admin.example.com. 1 3600 1800 604800 86400
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Tue Feb 20 10:00:00 PST 2024
;; MSG SIZE rcvd: 74
Now, let’s enable DNSSEC signing in named.conf. We’ll add a key-directory and then a dnssec-signzone directive within our zone definition.
zone "example.com" {
type master;
file "db.example.com";
key-directory "/etc/bind/keys/example.com";
dnssec-signzone yes;
};
After reloading BIND, we can check our zone file. You’ll notice new records: RRSIG and NSEC (or NSEC3).
# cat /var/lib/bind/db.example.com.signed
$ORIGIN example.com.
$TTL 3600
@ IN SOA localhost. admin.example.com. 1 3600 1800 604800 86400
IN NS localhost.
localhost. 3600 IN A 127.0.0.1
www 3600 IN A 192.0.2.1
mail 3600 IN A 192.0.2.2
; Zone Signing Keys
$INCLUDE /etc/bind/keys/example.com/dsset-example.com.
$INCLUDE /etc/bind/keys/example.com/ksk-key.example.com.
$INCLUDE /etc/bind/keys/example.com/zsk-key.example.com.
; RRSIG records for SOA
@ 2700 IN RRSIG SOA 8 2 3600 20240301000000 20240215000000 example.com. ABCDEFGHIJKLMNOPQRSTUVWXYZ123456=
; RRSIG records for NS
@ 2700 IN RRSIG NS 8 2 3600 20240301000000 20240215000000 example.com. ABCDEFGHIJKLMNOPQRSTUVWXYZ123456=
; RRSIG records for A (www)
www 2700 IN RRSIG A 8 2 3600 20240301000000 20240215000000 example.com. ABCDEFGHIJKLMNOPQRSTUVWXYZ123456=
; RRSIG records for A (mail)
mail 2700 IN RRSIG A 8 2 3600 20240301000000 20240215000000 example.com. ABCDEFGHIJKLMNOPQRSTUVWXYZ123456=
; NSEC records for authenticated denial of existence
@ 3600 IN NSEC localhost. A AAAA DNSKEY NSEC RRSIG
localhost. 3600 IN NSEC mail. A RRSIG NSEC
www 3600 IN NSEC mail. A RRSIG NSEC
mail 3600 IN NSEC . A RRSIG NSEC
The dnssec-signzone yes; directive tells BIND to automatically manage the signing process. When BIND loads the zone, it uses the keys it finds in the key-directory to generate RRSIG records (which are digital signatures for existing records) and NSEC or NSEC3 records (which prove that a name doesn’t exist).
The core idea is that a DNSSEC-validating resolver can fetch the zone’s public keys (via DNSKEY records) and then verify the RRSIG records. If the signatures match, the resolver trusts that the data hasn’t been tampered with and that it’s coming from the authoritative source.
The keys themselves are managed by BIND. When you enable dnssec-signzone, BIND will generate a Key Signing Key (KSK) and a Zone Signing Key (ZSK) if they don’t exist. The KSK is used to sign the ZSK and other keys, and the ZSK is used to sign the actual zone data. This two-key hierarchy is a standard DNSSEC practice.
When a client queries for a record, say www.example.com, a validating resolver will receive the A record for www, an RRSIG record for that A record, the DNSKEY records for example.com, and potentially an RRSIG for the DNSKEY set. It then uses the public key to verify the RRSIG on the A record. If it also needs to prove that nonexistent.example.com doesn’t exist, it would use the NSEC or NSEC3 records to demonstrate the gap between existing names.
The key-directory is crucial. BIND expects to find your keys there. If you’re starting from scratch, you’ll need to generate these keys first. For example, to generate a KSK and a ZSK:
# dnssec-keygen -a ECDSA256 -b 2048 -n ZONE example.com
# dnssec-keygen -a ECDSA256 -b 2048 -n ZONE -f KSK example.com
This generates .key and .private files. BIND will automatically pick up the .key files and use them for signing. The .private files are sensitive and should be secured. The dsset-example.com. file is generated by BIND and contains the DS record information that you’ll need to upload to your parent zone registrar.
The RRSIG records have a validity period (e.g., 20240301000000 20240215000000 for expiration and inception). BIND automatically re-signs the zone before keys expire, ensuring continuous coverage. The NSEC records are generated based on the sorted order of your zone’s names and record types.
The most subtle aspect of DNSSEC signing is how NSEC records prevent "zone walking" or enumeration. An attacker can’t just query for every possible name and see what comes back. Instead, the NSEC record for www.example.com might point to mail.example.com and list the types of records present at www (like A and RRSIG). If you query for foo.example.com and get back the NSEC record for www.example.com, you know that foo.example.com doesn’t exist because it would fall alphabetically between www and mail, and no NSEC record covers that gap.
The next problem you’ll encounter is setting up the DS record with your registrar.