---
title: "Migrating from OpenDNSSEC to BIND 9"
slug: "migrating-from-opendnssec-to-bind-9"
description: "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."
updated: 2025-10-07T06:00:51Z
published: 2025-10-07T06:00:51Z
canonical: "kb.isc.org/migrating-from-opendnssec-to-bind-9"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://kb.isc.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating from OpenDNSSEC to BIND 9

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](https://kb.isc.org/v1/docs/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.

Migrating should be relatively straightforward. It consists of the following steps:

1. updating your configuration, putting the OpenDNSSEC settings into `named.conf`
2. importing keys, generating key files that reference the keys inside an HSM,
3. optionally setting the key state and timing metadata.

## 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 key-store. Key generation was not supported.

OpenSSL 3 introduces providers instead of engines. You can use the [PKC#11 provider](https://github.com/latchset/pkcs11-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 key-stores 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](https://kb.isc.org/docs/cve-2023-50868). [RFC 9276](https://datatracker.ietf.org/doc/rfc9276/) (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
```

## Migrating key-stores

Now that we have moved key management and signing from OpenDNSSEC to BIND 9, possibly the next step is to migrate from SoftHSM to a different key-store. Perhaps you want to change to an actual HSM, or you want to have software-based key management. In the example below we change to KSK to be in a different HSM, and the ZSK to be software-based.

```
key-store "sca6000" {
    directory "keys";
    pkcs11-uri "pkcs11:token=Sun Metaslot;pin-value=1234";
};

key-store "zsk" {
    directory "keys";
};

dnssec-policy "example" {
    keys {
        ksk key-store "sca6000" lifetime P1Y algorithm 8 2048;
        zsk key-store "zsk" lifetime P6M algorithm 8 1024;
    };
};

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

We have introduced two new key-stores, `sca6000` and `zsk`. The first is a reference to our HSM and the second is just defining `keys` as the key directory to store the key files for ZSKs.

We have created a new policy `example`. Both the KSK and the ZSK uses a new key-store. We have omitted the other configuration options, just for example purposes.

We reconfigure the zone to use the new policy. Since no keys exist in the HSM and in the key directory, once we will reconfigure the server, key rollovers for `example.com` will be initiated.

When the key rollovers are complete, you can clean up the OpenDNSSEC and SoftHSM binaries and configuration.

## 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? If you want to change your policy, for example change the algorithm, or start using 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](https://gitlab.isc.org/isc-projects/bind9/-/issues/new). You can also reach out to our [BIND Users Mailing List](https://lists.isc.org/mailman/listinfo/bind-users).

          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.
