Migrating from OpenDNSSEC to BIND 9

Prev Next

With OpenDNSSEC reaching End-of-Life, this article aims to help with the transition to BIND 9 for those who wish to do so.

In BIND 9 you can configure a DNSSEC Key and Signing Policy (KASP), allowing you to specify all DNSSEC signing behavior in one place. This is the same approach as with OpenDNSSEC.

Requirements

First of all, OpenSSL 3.0 or higher is required. Older versions relied on so-called engines to perform cryptographic operations. The PKCS#11 engine was used to interface with an HSM keystore. Key generation was not supported.

OpenSSL 3 introduces providers instead of engines. You can use the PKC#11 provider to generate keys, and sign with them.

You should use BIND version 9.20 or higher. This version plays well with OpenSSL providers and allows for referencing HSM keystores in the configuration.

Configuration

Let's say we want to migrate the ever-so-popular example.com zone. Let's assume it is signed with the default policy from OpenDNSSEC. Note that this default policy is very different than dnssec-policy default, so we will need to create a custom policy.

The keys are stored inside an HSM, but for this example we assume SoftHSM from OpenDNSSEC's default configuration.

The unsigned zone data is likely stored in /var/lib/opendnssec/unsigned/example.com. Your paths may differ from this example, but we copy the unsigned zone to somewhere named can read it. To preserve the signatures we are also copying the signed zonefile, but we need to convert it to raw format with named-checkzone. Furthermore, we need to store the PIN value somewhere (preferably in a safer place than this example
uses):

$ cp /var/lib/opendnssec/unsigned/example.com zones/example.com.db
$ named-checkzone -f text -F raw -o zones/example.com.db.signed example.com \
    /var/lib/opendnssec/signed/ example.com
$ echo "1234" > /etc/named/pin

Now let's look at the named configuration:

# named.conf

include "kasp.conf";

key-store "softhsm" {
    directory "keys";
    pkcs11-uri "pkcs11:token=OpenDNSSEC;pin-value=1234";
};

zone "example.com" {
    type primary;
    file "zones/example.com.db";
    serial-update-metod unixtime;
    dnssec-policy opendnssec;
};

We have configured the zone example.com with a DNSSEC policy called opendnssec. This policy is stored in kasp.conf. We will take a look at that shortly.

One of the KASP options that cannot be set within the policy is how to update the serial. Instead, we can set it at the options or zone level with serial-update-method. OpenDNSSEC uses unixtime by default so let's stick to that.

SoftHSM is configured as a key-store. The directory option determines where key files consumed by named are stored. And pkcs11-uri contains the token label and PIN value also used by OpenDNSSEC. Again, these values are taken from the default configuration; your token name and PIN code will likely be different.

Let's continue with the DNSSEC policy:

# kasp.conf

dnssec-policy "opendnssec" {
        signatures-refresh 3d;
        signatures-validity 14d;
        signatures-validity-dnskey 14d;
        signatures-jitter 12h;

        // nsec3param iterations 11 optout no salt-length 8;
        nsec3param;

        dnskey-ttl 3600;
        retire-safety 1h;
        publish-safety 1h;
        purge-keys 14d;

        keys {
                ksk key-store "softhsm" lifetime P1Y algorithm 8 2048;
                zsk key-store "softhsm" lifetime P6M algorithm 8 1024;
        };

        inline-signing yes;
        zone-propagation-delay 43200;
        // no specific soa configuration available
        // serial is set with serial-update-method

        parent-propagation-delay 9999;
        ds-ttl 3600;
        // no specific parent soa configuration available

};

This resembles the default policy from OpenDNSSEC as much as possible. The keys section tells us to use a separate KSK and ZSK; both are stored inside the softhsm key-store. Each KASP key can be stored in a different key-store, so later perhaps you'll want to change your policy so that your ZSK just uses files on disk. But for now let's focus on the migration.

The lifetime and key algorithm/size also are derived from OpenDNSSEC's default policy.

Note that OpenDNSSEC's default policy still uses NSEC3 with iterations 11 and salt. BIND 9 no longer accepts these values as they can be used in a denial of service attack. RFC 9276 (BCP) recommends SHA-1, no extra iterations, and empty salt. This can be set with nsec3param iterations 0 optout no salt-length 0; or just nsec3param;. If you have to change the NSEC3 parameters for your zone, be aware that this will require a lot of new signatures to be generated due to the change in the NSEC3 chain.

Also note that re-salting is not implemented. With the current NSEC3 parameter settings recommendations there is no use for this.

There is no specific SOA configuration available. The serial format is set with the zone option serial-update-method.

Importing keys

Now we have set up BIND 9 to sign example.com according to the opendnssec policy, so we need to make the keys available to the name server. BIND 9 maintains keys that are used for signing in key files.

First we need to figure out what keys are in use for a given zone. We can use ods-enforcer for this:

$ ods-enforcer key list -v -z example.com
Keys:
Zone:        Keytype: State:   Size: Algorithm: CKA_ID:    Repository: KeyTag:
example.com  KSK      active   2048  8          ca56..2088 SoftHSM     61375
example.com  ZSK      active   1024  8          1a51..9707 SoftHSM     2959

The CKA_ID value identifies the key inside the HSM. When OpenDNSSEC creates keys, it will also set the Label and Object to the same value. Now that we know this, we can use dnssec-keyfromlabel to create the key files.

$ dnssec-keyfromlabel -K keys -a 8 -l "pkcs11:token=OpenDNSSEC;object=ca56..2088;pin-source=/etc/named/pin" -f K example.com
Kexample.com.+008+61375

$ dnssec-keyfromlabel -K keys -a 8 -l "pkcs11:token=OpenDNSSEC;object=1a51..9707;pin-source=/etc/named/pin" example.com
Kexample.com.+008+02959

This creates the KSK and ZSK key files that BIND 9 can use for signing. Note that we need to set -f K when creating the key files for the KSK.

We specify the algorithm with -a 8. The -K keys tells the program to store the key files inside the directory keys. Alternatively, we could have changed to this directory before running dnssec-keyfromlabel.

A simple list of the directory contents shows that the key files have been created:

$ ls keys
Kexample.com.+008+02959.key      Kexample.com.+008+61375.key
Kexample.com.+008+02959.private  Kexample.com.+008+61375.private

Setting state

With the key files created, BIND 9 is able to sign with the zone with the given keys. The keys are still inside the HSM - they don't contain the cryptographic secrets - but we are able to find them and perform cryptographic operations with them.

The key files also maintain state and timing metadata. Without setting any, named will consider that we have just introduced the keys, effectively as if we are enabling DNSSEC. We probably want to set the state such that the zone is already in a valid DNSSEC state.

$ cd keys
$ export S="omnipresent"
$ export T="-7d"
$ dnssec-settime -s -g $S -d $S $T -k $S $T -r $S $T -P $T -A $T Kexample.com.+008+61375
./Kexample.com.+008+61375.key
./Kexample.com.+008+61375.private
./Kexample.com.+008+61375.state
$ dnssec-settime -s -g $S -k $S $T -z $S $T -P $T -A $T Kexample.com.+008+02959
./Kexample.com.+008+02959.key
./Kexample.com.+008+02959.private
./Kexample.com.+008+02959.state

This sets all key states into omnipresent, meaning that all DNSSEC records currently in the zone are available to the validators. The publication and activation time is set 7 days in the past. There is no good time to find out what the actual key timing metadata must be, but ods-enforcer rollover list gives us an idea when a key rollover is due and so how long the key has been in use:

$ ods-enforcer rollover list -z example.com
Keys:
Zone:            Keytype: Rollover expected:
example.com      KSK      2026-09-17 09:37:28
example.com      ZSK      2025-12-16 09:37:28

Note that it is also possible to set these state and timing options directly on dnssec-keyfromlabel.

Push the button

Now we are ready to start or reconfigure BIND 9 and the zone will be signed by BIND 9. Before doing so, we can use named-checkconf -k to make sure that the created key files match the configured policy.

$ named-checkconf -k named.conf && echo "all good"
all good

It is also good practice to not migrate if the zone is not currently performing a key rollover.

Still feeling a bit insecure? You can set manual-mode yes; inside dnssec-policy. This will block any key transitions; instead they are logged (look for "keymgr-manual-mode"). If the changes look good, perform the next step manually by issuing the rndc command:

$ rndc dnssec -step example.com

Bump-in-the-wire signer

If you are using OpenDNSSEC adapters, i.e. the signer is a bump-in-the-wire, we can set up our BIND 9 signer to act the same. We call them remote servers:

# named.conf

remote-servers "primary" {
    127.0.0.1;
};

remote-servers "secondary" {
    127.0.0.2; 127.0.0.3;
};

zone "example.com" {
    type secondary;
    inline-signing yes;
    dnssec-policy opendnssec;
    allow-notify { "primary"; };
    also-notify { "secondary"; };
    notify explicit;
};

The zone would be configured as a secondary, with a primary server that we allow to notify (allow-notify). The also-notify option in conjunction with notify explicit; ensures that only the real secondary servers are notified if the zone is updated or re-signed.

What's next?

Hopefully by now you have migrated successfully to signing your zones with BIND 9. What's next? Perhaps you want to change your policy, perform an algorithm rollover, use a different key-store for your ZSK, or simply use BIND 9's default policy. Simply editing your configuration file to suit your needs followed by an rndc reconfig should do the trick. BIND 9 treats policy changes the same as a rollover: keys that no longer match the policy are phased out after the new keys have been introduced and properly propagated. And with manual-mode enabled you are able to first examine the transitions in the logs before executing them.

You can also switch to using offline KSK, add BIND 9 as a signer in a multi-signer setup, or start automation of updating the parent-side chain-of-trust records, but these are topics for another article.

We take DNSSEC very seriously. We aim for continuous improvements and we welcome your feedback. In fact, the new manual-mode and the named-checkconf option to check your keys against your DNSSEC policy were feature requests by users. If you have a feature request or find a bug, please let us know by submitting an issue on our GitLab. You can also reach out to our BIND Users Mailing List.

Note:

We are constantly making improvements to BIND's DNSSEC features, so you may need to update to the latest version for some of the features mentioned in this article:

  • The manual-mode configuration option is available since BIND 9.20.13.
  • The -k option for named-checkconf is available since BIND 9.20.14.