DNSSEC Signing w/ BIND

To solve the chicken-or-egg problem for DNSSEC from the other side, let’s use an authoritative DNS server (BIND) for signing DNS zones. This tutorial describes how to generate the keys and configure the “Berkeley Internet Name Domain” (BIND) server in order to automatically sign zones. I am not explaining many details of DNSSEC at all, but only the configuration and verification steps for a concrete BIND server.

It is really easy to tell BIND to do the inline signing. With this option enabled, the admin can still configure the static database for his zone files without any relation to DNSSEC. Everything with signing and maintaining is fully done by BIND without any user interaction. Great.

This blogpost is part of a series about DNSSEC. Refer to this list for all articles.


I am assuming that there is a working BIND server in place which is the authoritative name server for at least one zone. The BIND server must at least run with version 9.9! I am using an Ubuntu Server 14.04.4 LTS with BIND version 9.9.5-3ubuntu0.8-Ubuntu. A few days age I published a blog post covering the secure installation of BIND. Refer to it to see how. Note that ALL of the following configuration steps are done only on the primary server since the secondary servers will only mirror the completely signed zone file. There is no need to adjust something at the secondaries at all. For this tutorial, I am using my test domain weberdns.de.

Furthermore, please read some general DNSSEC information on other sites, or simply google it and read the Wikipedia, DNSSEC.net, or RFC (4033, 4034, 4035) articles. I am assuming that you are already familiar with KSK & ZSK, RRSIG, DNSKEY, DS, NSEC & NSEC3. If not, come back again in two hours. ;) Ok, in any case, here it is: DNSSEC signes zones with a private key (ZSK and KSK), while the public key is used to verify the signature. The signature for all resource records are stored in additional records, called RRSIG. The “Next SECure record” (NSEC) is used to prove that no entry exists between two other entries (NXDOMAIN).

Design and Keys

One thing to think about is the placement of the keys. The private keys (for both, the KSK as well as for the ZSKs) should NOT be accessible from the Internet. In the best way, a hardware security module (HSM) would be used for them. However, since this is not that trivial, the concept of a hidden primary can be used. With this way, the private keys are only stored on a not-accessible server, while only the secondaries (which must not store the private keys) are queried from the Internet:

Another consideration is the usage of NSEC or NSEC3. For small zones that only store a few well-known names such as ns, www, and mail, NSEC can be used. (Remember the zone walking.) For complex zones with many hidden domain names, the usage of NSEC3 should be considered. However, note that “security through obscurity” is not a good design at all. ;)

BIND Configuration

We will first generate the keys followed by the configuration of BIND. A great (hence: small) tutorial is also available at ISC: Inline Signing in ISC BIND 9.9.0 — Examples. Note that I am using /dev/urandom for my key generation. If you are interested in more details, read this or that. And again, note that you must have at least BIND 9.9 for these features to work.


First, create a new directory for the keys:

And generate both, the KSK and the ZSK, within that newly created directory. I am using the RSASHA256 algorithm and 2048 bit for the KSK, while 1024 bit for the ZSK:

Since BIND must be able to read the private key, change the permissions for the group (bind) to read the *.private files: “sudo chmod g+r *.private” the ownership of the files to “bind:bind”:

Now, the /keys directory should look like that:

For example, my public KSK for weberdns.de looks like that:

[Note that I had some problems on Ubuntu with the keys for BIND. This post helped me to solve it. But I am not fully sure whether this is the best way:

and changing the following line to this:

and a restart of apparmor: sudo service apparmor restart .]

named.conf.options & named.conf.local

It is really simple to tell BIND that it should sign its zones for the proper usage of DNSSEC. The first step is to set the key-directory and to enable dnssec. (Note that dnssec-enable is “yes” per default. However, I am adding the lines anyway.) Open the named.conf.options file: sudo nano named.conf.options and add the following two lines within the options { } section:

The second step is to configure the concrete zone to be signed and maintained. That is: BIND will 1) use the existing zone file and sign it in the background and 2) maintains the signed file in order to update any signatures once they expire.

Open the named.conf.local file in which the zones are declared: sudo nano named.conf.local and add the following two lines to the zone which should be signed:

That’s it!!

Reload the server in order to let BIND sign the zone: sudo service bind9 reload. The following command checks the correct signing, e.g.:

Note that you now have two more files within the /etc/bind/ directory, namely the *.signed and *.signed.jnl files. These are the real zone files that are presented to the world, while the original zone file is the file which is still edited by the admin:


KSK/DS to Parent Zone

In order to have the full DNS tree secured by DNSSEC, you must transfer your KSK or DS record to your registrar. They will sign it with their private key. This is the main part of DNSSEC because afterwards anyone in the world will be able to validate that you really own the private key for your zone.

If the registrar wants the DS record, obtain it from the KSK keys with the tool dnssec-dsfromkey, such as:

If they want the whole public KSK, simply “cat” it from the keyfile, as already shown above (cat Kweberdns.de.+008+63202.key).

After the registrar has received and signed the KSK, it can be queried within the DNS. For the German DENIC, the KSK is also present in the whois query:

The DS record can be queried with a normal DNS request. In the following example, I am using the +trace and +dnssec options in order to see the DS returning from the parent zone (in my case .de) and to see some RRSIG signatures for all answers:

In the same way, the dnskeys (KSK and ZSK) can be queried:

Tests (AD & RRSIG)

Now, lets check the correct signing of the DNSSEC secured zone. You must use a DNSSEC validating name server, such as BIND or Unbound, as I showed in the past two blog posts. If you do not have a validating server yourself, you can use the Google public DNS server at 2001:4860:4860::8888 or because it already does validating DNSSEC answers. (Note that your authoritative server won’t ever reply with the “ad” flag, but only with the “aa” flag. This is by design. The authoritative server must not prove his own records, because he has direct access to the source and trusts it.)

The main part when using dig is the “ad” flag within the DNS answer. This indicates the “Authentic Data” being transferred. (Ref: IANA, DNS Header Flags.)

In order to see the signatures (RRSIG), use dig with the +dnssec and +multi options. Now, the DNSSEC related information are displayed, as well as listed within multiple lines with some explanatory notes. Note that my domain name “mail.weberdns.de” has an A and an AAAA record. Therefore this example shows two answers along with two signatures:

In both cases, note the AD flag.

Congratulations. You’re done. You have correctly secured your DNS zone!

In case of failures such as “no servers could be reached” when checking from the outside you might have a look at intermediary firewalls. For example, blocking IP fragments (such as I did ;)) is a bad idea:

What about the Secondaries?

Good news: You do not have to do anything with the secondary name servers. They only mirror the signed zone files in the same way they mirrored the unsigned ones. So nothing to do here. ;)


There are some great online tools to verify the DNSSEC signatures. Have a look at DNSViz. It reveals and visualizes any DNS name, for example for mail.weberdns.de. Another tool is the DNSSEC Analyzer for iPhone. Here are two screenshots of these tools:

Further Reading

One key word that was not mentioned until now is: key rollover. I have only generated a single ZSK without any further expiration dates or following keys. In an upcoming blogpost I will show how to rollover the ZSK.

Furthermore, there are some cool things to come with DNSSEC: DANE and SSHFP, which I will cover in upcoming posts, too.

And keep in mind that you MUST have accurate date and time settings on your DNSSEC servers. If you’re drifting too much, all signatures will become invalid. Consider the usage of an own NTP server with different sources.

Some more links:

Featured image “vertrag-130407_014.jpg” by Eleleleven is licensed under CC BY-SA 2.0.

8 thoughts on “DNSSEC Signing w/ BIND

  1. I have a silly question, but maybe you might update your awesome tutorial to answer it. All tutorials (including yours) just deal with one domain name. That means generating two keys, the KSK and the ZSK. If I have many (dozens, thousands…) of domain names, each will have its own ZSK (obviously) but will they have their own KSK as well? If so, what’s the point of signing the zones twice? On the other hand, I see the advantage of having all zones signed with the same KSK, so that you can invalidate everything by changing just the KSK manually, and let BIND regenerate everything. Cool, of course. However, I can also see that it wouldn’t be that hard to change all ZSKs with a simple script… uh… so I don’t see the point of using a KSK altogether. Ok, I know — I ought to spend the two hours reading manuals :-P I will do so, and hopefully get some light shed into this mysterious issue…

    BTW, for the sake of completeness, you ought also to write a paragraph explaining how to properly configure the secondary nameserver. I know it’s trivial, but it’s always nice to have everything on a single page (most tutorials also ‘forget’ the secondary nameservers, so you’re definitely not alone on this).

    1. Gwyneth! Thanks for your feedback and sorry for the delay in responding.

      1) The differences between the KSK and the ZSK are that the KSK must be brought to the parent zone (which involves a third party). You won’t rollover the KSK that often but only after a breach or every few years. The ZSK is completely under your control and you can simply rollover it every few month. Of course, with some scripts. ;) Please refer to my blog post about the ZSK rollover. That is, it is absolutely recommended to use new and fresh KSKs (and of course ZSKs) for each and every zone. (Note that you must specify the name of the zone while generating the KSK so you don’t have the choice anyway.)

      2) Thanks for the hint. I just added a small paragraph stating that you must not do anything on the slaves. (Assumed that you already have a working primary-slave setup.) The slaves will just mirror the complete (signed) zone file. I did not have any problems with them so far.

  2. Thank you for your detailed explanation. Can we implement the same for a private hierarchial DNS structure without being registered with Domain registery. When I tried to do DNSEC on a private root zone including top level domain and child nodes.My signed zone does not contain signed RR of DS, How can I bring signed RR of DS into my signed zone?

    1. Hey Jeyakumar,

      you can sign whatever you want. ;) But you must put your own trust-anchor at the DNSSEC validating resolver.

      Concerning your DS records: You must place them into your parent zone by yourself. BIND won’t do it in an automatic way. Please have a look at this blogpost: https://weberblog.net/signing-a-delegated-subdomain/
      Those DS records will be signed with RRSIGs by the parent zone. Of course only if you’re signing your parent zone at all. ;)


  3. Hi Johannes, I am implementing Bind9 with DNSSEC in a local environment with an authoritative server on the “lan” domain.

    The Bind version is 9.11, I can’t get the “lan” zone to be validated. which is my local area. I have tried to add trsted-keys but it does not validate (bit ad).

    I don’t find anything clear how to do the validation locally, this is the output of delv

    ; fetch: dns01.lan/A
    ;; validating dns01.lan/A: starting
    ;; validating dns01.lan/A: attempting positive response validation
    ;; fetch:lan/DNSKEY
    ;; validating lan/DNSKEY: starting
    ;; validating lan/DNSKEY: attempting positive response validation
    ;; fetch: lan/DS
    ;; chase DS servers resolving ‘lan/DS/IN’:
    ;; fetch: ./NS
    ;; REFUSED unexpected RCODE resolving ‘./NS/IN’:
    ;; validating lan/DNSKEY: in dsfetched
    ;; validating lan/DNSKEY: falling back to insecurity proof (SERVFAIL)
    ;; validating lan/DNSKEY: checking existence of DS at ‘lan’
    ;; fetch: lan/DS
    ;; chase DS servers resolving ‘lan/DS/IN’:
    ;; fetch: ./NS
    ;; REFUSED unexpected RCODE resolving ‘./NS/IN’:
    ;; validating lan/DNSKEY: in dsfetched2: SERVFAIL
    ;; no valid DS resolving ‘lan/DNSKEY/IN’:
    ;; validating dns01.lan/A: in fetch_callback_validator
    ;; validating dns01.lan/A: fetch_callback_validator: got SERVFAIL
    ;; broken trust chain resolving ‘dns01.lan/A/IN’:
    ;; resolution failed: broken trust chain

    1. Hey Eduardo,

      one question, just to be sure: You’re NOT asking the authoritative server directly, don’t you?
      If you’re querying the authoritative directly, you’ll get the “aa” flag but NEVER the “ad” flag. You’ll only see that “ad” flag if you’re querying a recursive DNS server that does the DNSSEC validation.

Leave a Reply

Your email address will not be published. Required fields are marked *