Signing a Delegated Subdomain

If you are already familiar with DNSSEC this is quite easy: How to sign a delegated subdomain zone. For the sake of completeness, I am showing how to generate and use the appropriate DS record in order to preserve the chain of trust for DNSSEC.

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


You already have a DNSSEC signed zone, in my example: Beside hostnames within this domain you have a delegated zone with its own nameservers (= NS records). Now you must not only sign this new zone, in my example:, but you must also preserve the chain of trust. This is done by the delegation signer DS record which is placed in the parent zone. I assume that you already have the delegated zone working, i.e., NS records for it, zone file (SOA), etc.

Signing the Subdomain Itself

(Just a recap from my previous blogposts.) That is: Generating the KSK and ZSK, adjusting its ownership to be readable by BIND, and inserting the NSEC3 parameters in order to use NSEC3 rather than NSEC:


Using the DS Record

Note that this process is exactly the same as for your primary domain. For your domain you already have sent the DS record to your parent zone (such as .de or .com). Now it’s much easier since you’re owning both, the domain (in my case: AND the subdomain ( That is:

1) Generate the DS record from the KSK you created for your subdomain. Use the small  dnssec-dsfromkey program with the -2 option to have only the SHA-256 output (refer to IANA: Delegation Signer (DS) Resource Record (RR) Type Digest Algorithms):

This resource record has an owner name of the delegated subdomain ( and the following four fields (refer to RFC 4034 “Resource Records for the DNS Security Extensions” section 5: The DS Resource Record):

  • the key tag 17463 which identifies the KSK,
  • the algorithm used for the KSK 10 = RSASHA512 as used while creating the KSK above,
  • the digest type 2 = SHA-256 and
  • the digest of the KSK (and some more fields) itself.

2) Place this single DS record into your parent zone. After a reload you’re done. ;)


Note the “ad” flag for all of those queries. (Only when resolved by a DNSSEC validating resolver.) You can now query the NS records for your subdomain:

its DNSKEYs (KSK and ZSK):

as well as the DS record in the parent zone:

Querying a hostname within the delegated subdomain shows the “ad” flag as well (line 7), which proves its authenticity:

Testing NSEC3 with a unvalid name to show up the NSEC3 records works as well:

That’s it! Congrats.

Some more DNSViz

Of course you can use DNSViz again to graph the chain of trust. For my test hostname this looks like that:

During my implementation of this delegated subdomain I queried DNSViz after each step about the SOA record for the the subdomain which showed these graphs:

  1. Delegated zone was neither signed nor did the DS record exist
  2. Delegated zone was signed but the DS record did not exist
  3. Completely signed and secure

Featured image “Vorhängeschlösser mit Zahlenschloss” by Marco Verch is licensed under CC BY 2.0.

18 thoughts on “Signing a Delegated Subdomain

  1. Hello,
    thank you for this great post.
    I’m owning a domain name and I’m hosting the dns server. It’s configured with DNSSEC. I’m intending to use a delegated subdomain on Google Cloud Plateform with Cloud DNS. I’ve created the subdomain DNS, and I’ve configured the delegation on the parent, as well as taking the DS record from the public key. But now the configuration is stalled to this step ‘Delegated zone was signed but the DS record did not exist’. I’ve written down the DS record myself in the parent record, so it should be there, but any request to my dns server (the parent) just returns the SOA.
    Any idea?

    1. Hello y0m,

      that sounds like your DNS server does not have the DS record. For whatever reason. Maybe you have a syntax error?

      It’s hard for me to troubleshoot you issue, since I don’t have any more information about your servers, used versions, and so on.


      1. Here are informations anonymized.
        I’m using BIND 9.14 on FreeBSD, with DNSSEC for the parent zone.
        And for the child zone, I’m using Google Cloud DNS with DNSSEC.
        Do you need more informations?
        $ORIGIN .
        $TTL 180 IN SOA (
        2019 ; serial
        21600 ; refresh (6 hours)
        1800 ; retry (30 minutes)
        1209600 ; expire (2 week)
        180 ; minimum (3 minutes)
        ) IN NS IN NS IN A IN AAAA dead::beef IN A IN AAAA dead::beef
        $include /usr/local/etc/namedb/keys/
        $include /usr/local/etc/namedb/keys/ IN DS 56789 8 2 sha256token IN NS IN NS IN NS IN NS (Google Cloud DNS with DNSSEC) 21600 IN SOA 3 21600 3600 259200 300 21600 IN NS 21600 IN NS 21600 IN NS 21600 IN NS 300 IN A

        *Google Cloud DNS informations for registrar setup, in this case: the parent zone*
        Type Data
        56789 8 2 sha256token

        1. Well, this looks good at a first glance. Especially this line:
          “ IN DS 56789 8 2 sha256token”

          When you’re on the FreeBSD machine, what’s the output of something like:
          “dig ds @localhost”
          Is it returning the DS record? It should.

          (Just to be sure: You have incremented the serial number and reloaded the zone, did you?)

          1. I’m getting this:

            $ dig ds @localhost

            ; <> DiG 9.14.1 <> ds @localhost
            ;; global options: +cmd
            ;; Got answer:
            ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33018
            ;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
            ;; WARNING: recursion requested but not available

            ;; OPT PSEUDOSECTION:
            ; EDNS: version: 0, flags:; udp: 4096
            ; COOKIE: dafd459e746736d3204b73eb5cd2cbf60645fda2e545ddfc (good)
            ;; QUESTION SECTION:
            ; IN DS

            ;; AUTHORITY SECTION:
   180 IN SOA 2019 21600 1800 1209600 180

            ;; Query time: 0 msec
            ;; SERVER: ::1#53(::1)
            ;; WHEN: mer. mai 08 14:30:46 CEST 2019
            ;; MSG SIZE rcvd: 114

            I've always incremented the serial in the SOA, I've hidden this a bit, but right now with all the tests I've made, it's 2019050714, which is the date + a 2 digits number. I've never had to reconfig 99 times a day.
            As you can see, that's just like I would have not written the DS record in the parent zone.

            1. Hm, that’s strange. Maybe there is a typo in the DS record in your zone? Or any other typo? Is “named-checkconf -z” okay? Is it showing your most current serial number?

              This is what is looks like on my DNS server, when asking for the DS of the child zone:

              weberjoh@vm24-ns0:~$ dig ds @localhost

              ; <<>> DiG 9.11.3-1ubuntu1.7-Ubuntu <<>> ds @localhost
              ;; global options: +cmd
              ;; Got answer:
              ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28558 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ; COOKIE: be250f559c800ce6ec34c9775cd2d8b563a1099a7d76ac04 (good) ;; QUESTION SECTION: ; IN DS ;; ANSWER SECTION: 3600 IN DS 45918 10 2 29E4AE014492DEED9610422977D3AEC6CC8C42CBD08A4E5919E3FCCE 75553B21 ;; Query time: 0 msec ;; SERVER: ::1#53(::1) ;; WHEN: Wed May 08 13:25:09 UTC 2019 ;; MSG SIZE rcvd: 120 ### My DS record in the zone file is: " IN DS 45918 10 2 29E4AE014492DEED9610422977D3AEC6CC8C42CBD08A4E5919E3FCCE75553B21" Sorry, I don't have any more ideas right now...

              1. I’ve generated the DS record from the DNSKEY record this way:

                $ dig dnskey | dnssec-dsfromkey -f –
       IN DS 56789 8 1 40-DIGIT-HASH
       IN DS 56789 8 2 SHA256-TOKEN

                The named-checkconf -z does not report any error, and the line for the shows:

                zone loaded serial 2019050714 (DNSSEC signed)

                I clearly don’t understand :)

  2. Just to let you and readers know, I’ve found the origin of my problem. I’m using DNSSEC since 2015 or so, and at the time I had set up snippets to help me sign zone every time I’m making changes. A specific option in the dnssec-signzone command was removing DS records from the signed zone.

    Generate DS records for child zones from dsset- or keyset- file.
    Existing DS records will be removed.

    That was a terrible idea, I don’t even remember why I first added this options, even though I remember clearly to have taken some time to read options and what they are doing.

    My issue is fixed now. My Google Cloud DNS child zone (subzone, subdomain) is linked to my parent zone.

  3. Is it possible create DNSSEC chain of trust for local root zone
    Say if I have root domain domainX.loc and subdomains : subdomainY. domainX.loc and
    subdomainZ. domainX.loc. How would I create chain of trust without using

    1. Hey Mundile,

      yes, it is. For your subdomains, you simply have to do the same steps as with any other subdomain. However, your local recursive DNS server (which is DNSSEC validating) needs to know the “root” DNSKEY for your local root zone. You’ll have to put this KSK of your zone as a trust anchor into it.

      Note: This only applies if you have 1) an authoritative DNS server which you are using for DNSSEC signing AND 2) a separate recursive DNS server for DNSSEC validation. If you only have one single DNS server for both, you don’t need this setup, because your authoritative DNS server won’t validate DNSSEC, as he already has the zone file itself.


      1. Thanks Johannes. However, what will be dnssec-validation ( yes | auto | no) at the local root DNS server as Chain of Trust starts from there.
        Also in this scenerio (local root zone) should I use managed keys or trusted keys or any like in the case of public root zone.

  4. Hi Johannes,
    I like your posts. They do help me to understand DNSSEC. But I have a stupid question. I have 2 servers hosting the same subdomain. May I sign the subdomain with different ZSK and KSK, and then upload Both DS keys to the parent zone server?

    1. Hi William,

      thanks for your feedback.

      When you’re talking about “two servers hosting the same subdomain”, are they typically in the primary/secondary role, or do you really have two different zone files on both servers? In the first case, you only need a single KSK and ZSK, since the (signed) zone files are transferred from the primary to the secondary.

      If you really have two independent primary servers with their own zone files for the *same* zone (which is kind of weird, but maybe you do split-DNS that way), uh, then I don’t know right off the box. AFAIK, technically it would work if you have two DS records at the parent zone. As long as one correct path exists, the zone is signed correctly. But I am not quite sure, to be honest.


    2. Ok, I asked a few people at Twitter about this question. Here are their answers:

      That is:
      “I believe that is possible by inserting the DS of both independent KSK into the parent zone. I think this corresponds to “Model 2” of
      “Correct — but you also have to cross import ZSKs. This is described in Model 2 also.”

Leave a Reply

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