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.
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 fornamed-checkconf
is available since BIND 9.20.14.