5. DNSSEC

DNS Security Extensions (DNSSEC) provide reliable protection from cache poisoning attacks. At the same time these extensions also provide other benefits: they limit the impact of random subdomain attacks on resolver caches and authoritative servers, and provide the foundation for modern applications like authenticated and private e-mail transfer.

To achieve this goal, DNSSEC adds digital signatures to DNS records in authoritative DNS zones, and DNS resolvers verify the validity of the signatures on the received records. If the signatures match the received data, the resolver can be sure that the data was not modified in transit.

Note

DNSSEC and transport-level encryption are complementary! Unlike typical transport-level encryption like DNS-over-TLS, DNS-over-HTTPS, or VPN, DNSSEC makes DNS records verifiable at all points of the DNS resolution chain.

This section focuses on ways to deploy DNSSEC using BIND. For a more in-depth discussion of DNSSEC principles (e.g. How Does DNSSEC Change DNS Lookup?) please see DNSSEC Guide.

5.1. Zone Signing

BIND offers several ways to generate signatures and maintain their validity during the lifetime of a DNS zone:

5.1.1. Zone keys

Regardless of the zone-signing method in use, cryptographic keys are stored in files named like Kdnssec.example.+013+12345.key and Kdnssec.example.+013+12345.private. The private key (in the .private file) is used to generate signatures, and the public key (in the .key file) is used for signature verification. Additionally, the Fully Automated (Key and Signing Policy) method creates a third file, Kdnssec.example+013+12345.state, which is used to track DNSSEC key timings and to perform key rollovers safely.

These filenames contain:

  • the key name, which always matches the zone name (dnssec.example.),

  • the algorithm number (013 is ECDSAP256SHA256, 008 is RSASHA256, etc.),

  • and the key tag, i.e. a non-unique key identifier (12345 in this case).

Warning

Private keys are required for full disaster recovery. Back up key files in a safe location and protect them from unauthorized access. Anyone with access to the private key can create fake but seemingly valid DNS data.

5.1.2. Fully Automated (Key and Signing Policy)

Key and Signing Policy (KASP) is a method of configuration that describes how to maintain DNSSEC signing keys and how to sign the zone.

This is the recommended, fully automated way to sign and maintain DNS zones. For most use cases users can simply use the built-in default policy, which applies up-to-date DNSSEC practices:

  zone "dnssec.example" {
      type primary;
      file "dnssec.example.db";
      dnssec-policy default;
  };

The dnssec-policy statement requires dynamic DNS to be set up, or inline-signing to be enabled. In the example above we use the latter, because the default policy uses inline-signing.

This is sufficient to create the necessary signing keys, and generate DNSKEY, RRSIG, and NSEC records for the zone. BIND also takes care of any DNSSEC maintenance for this zone, including replacing signatures that are about to expire and managing Key Rollovers.

Note

dnssec-policy needs write access to the zone. Please see dnssec-policy for more details about implications for zone storage.

The default policy creates one key that is used to sign the complete zone, and uses NSEC to enable authenticated denial of existence (a secure way to tell which records do not exist in a zone). This policy is recommended and typically does not need to be changed.

If needed, a custom policy can be defined by adding a dnssec-policy statement into the configuration:

dnssec-policy "custom" {
    dnskey-ttl 600;
    keys {
        ksk lifetime P1Y algorithm ecdsap384sha384;
        zsk lifetime 60d algorithm ecdsap384sha384;
    };
    nsec3param iterations 0 optout no salt-length 0;
};

This custom policy, for example:

  • uses a very short DNSKEY TTL (600 seconds),

  • uses two keys to sign the zone: a Key Signing Key (KSK) to sign the key related RRsets (DNSKEY, CDS, and CDNSKEY), and a Zone Signing Key (ZSK) to sign the rest of the zone. The KSK is automatically rotated after one year and the ZSK after 60 days.

Also:
  • The configured keys have a lifetime set and use the ECDSAP384SHA384 algorithm.

  • The last line instructs BIND to generate NSEC3 records for Proof of Non-Existence, using zero extra iterations and no salt. NSEC3 opt-out is disabled, meaning insecure delegations also get an NSEC3 record.

For more information about KASP configuration see dnssec-policy.

The Advanced Discussions section in the DNSSEC Guide discusses the various policy settings and may be useful for determining values for specific needs.

5.1.2.1. Key Rollover

When using a dnssec-policy, a key lifetime can be set to trigger key rollovers. ZSK rollovers are fully automatic, but for KSK and CSK rollovers a DS record needs to be submitted to the parent. See Secure Delegation for possible ways to do so.

Once the DS is in the parent (and the DS of the predecessor key is withdrawn), BIND needs to be told that this event has happened. This can be done automatically by configuring parental agents:

  zone "dnssec.example" {
      type primary;
      file "dnssec.example.db";
      dnssec-policy default;
      parental-agents { 192.0.2.1; };
      checkds explicit;
  };

Here one server, 192.0.2.1, is configured for BIND to send DS queries to, to check the DS RRset for dnssec-example during key rollovers. This needs to be a trusted server, because BIND does not validate the response. The checkds option makes BIND use the explicitly configured parental agents, rather than looking them up by querying for the parent NS records.

If setting up a parental agent is undesirable, it is also possible to tell BIND that the DS is published in the parent with: rndc dnssec -checkds -key 12345 published dnssec.example.. and the DS for the predecessor key has been removed with: rndc dnssec -checkds -key 54321 withdrawn dnssec.example.. where 12345 and 54321 are the key tags of the successor and predecessor key, respectively.

To roll a key sooner than scheduled, or to roll a key that has an unlimited lifetime, use: rndc dnssec -rollover -key 12345 dnssec.example..

To revert a signed zone back to an insecure zone, change the zone configuration to use the built-in “insecure” policy. Detailed instructions are described in Reverting to Unsigned.

5.1.2.2. Multi-Signer Model

Dynamic zones provide the ability to sign a zone by multiple providers, meaning each provider signs and serves the same zone independently, as is described in RFC 8901. BIND 9 is able to support Model 2, where each provider has their own KSK and ZSK (or CSK). The keys from the other provider can be imported via Dynamic Update. For each active KSK there must be a corresponding DS record in the parent zone. Key rollovers require coordination in order to update the DS and DNSKEY RRset.

5.1.3. Manual Signing

There are several tools available to manually sign a zone.

Warning

Please note manual procedures are available mainly for backwards compatibility and should be used only by expert users with specific needs.

To set up a DNSSEC secure zone manually, a series of steps must be followed. Please see chapter Manual Signing in the DNSSEC Guide for more information.

5.1.4. Monitoring with Private Type Records

The state of the signing process is signaled by private type records (with a default type value of 65534). When signing is complete, those records with a non-zero initial octet have a non-zero value for the final octet.

If the first octet of a private type record is non-zero, the record indicates either that the zone needs to be signed with the key matching the record, or that all signatures that match the record should be removed. Here are the meanings of the different values of the first octet:

  • algorithm (octet 1)

  • key ID in network order (octet 2 and 3)

  • removal flag (octet 4)

  • complete flag (octet 5)

Only records flagged as “complete” can be removed via dynamic update; attempts to remove other private type records are silently ignored.

If the first octet is zero (this is a reserved algorithm number that should never appear in a DNSKEY record), the record indicates that changes to the NSEC3 chains are in progress. The rest of the record contains an NSEC3PARAM record, while the flag field tells what operation to perform based on the flag bits:

0x01 OPTOUT

0x80 CREATE

0x40 REMOVE

0x20 NONSEC

5.2. Secure Delegation

Once a zone is signed on the authoritative servers, the last remaining step is to establish chain of trust [1] between the parent zone (example.) and the local zone (dnssec.example.).

Generally the procedure is:

  • Wait for stale data to expire from caches. The amount of time required is equal to the maximum TTL value used in the zone before signing. This step ensures that unsigned data expire from caches and resolvers do not get confused by missing signatures.

  • Insert/update DS records in the parent zone (dnssec.example. DS record).

There are multiple ways to update DS records in the parent zone. Refer to the documentation for the parent zone to find out which options are applicable to a given case zone. Generally the options are, from most- to least-recommended:

  • Automatically update the DS record in the parent zone using CDS/CDNSKEY records automatically generated by BIND. This requires support for RFC 7344 in either parent zone, registry, or registrar. In that case, configure BIND to monitor DS records in the parent zone and everything will happen automatically at the right time.

  • Query the zone for automatically generated CDS or CDNSKEY records using dig, and then insert these records into the parent zone using the method specified by the parent zone (web form, e-mail, API, …).

  • Generate DS records manually using the dnssec-dsfromkey utility on zone keys, and then insert them into the parent zone.

5.3. DNSSEC Validation

The BIND resolver validates answers from authoritative servers by default. This behavior is controlled by the configuration statement dnssec-validation.

By default a trust anchor for the DNS root zone is used. This trust anchor is provided as part of BIND and is kept up-to-date using Dynamic Trust Anchor Management.

Note

DNSSEC validation works “out of the box” and does not require additional configuration. Additional configuration options are intended only for special cases.

To validate answers, the resolver needs at least one trusted starting point, a “trust anchor.” Essentially, trust anchors are copies of DNSKEY RRs for zones that are used to form the first link in the cryptographic chain of trust. Alternative trust anchors can be specified using trust-anchors, but this setup is very unusual and is recommended only for expert use. For more information, see Trust Anchors in the DNSSEC Guide.

The BIND authoritative server does not verify signatures on load, so zone keys for authoritative zones do not need to be specified in the configuration file.

5.3.1. Validation Failures

When DNSSEC validation is configured, the resolver rejects any answers from signed, secure zones which fail to validate, and returns SERVFAIL to the client.

Responses may fail to validate for any of several reasons, including missing, expired, or invalid signatures; a key which does not match the DS RRset in the parent zone; or an insecure response from a zone which, according to its parent, should have been secure.

For more information see Basic DNSSEC Troubleshooting.

5.3.2. Coexistence With Unsigned (Insecure) Zones

Zones not protected by DNSSEC are called “insecure,” and these zones seamlessly coexist with signed zones.

When the validator receives a response from an unsigned zone that has a signed parent, it must confirm with the parent that the zone was intentionally left unsigned. It does this by verifying, via signed and validated NSEC/NSEC3 records, that the parent zone contains no DS records for the child.

If the validator can prove that the zone is insecure, then the response is accepted. However, if it cannot, the validator must assume an insecure response to be a forgery; it rejects the response and logs an error.

The logged error reads “insecurity proof failed” and “got insecure response; parent indicates it should be secure.”

5.4. Dynamic Trust Anchor Management

BIND is able to maintain DNSSEC trust anchors using RFC 5011 key management. This feature allows named to keep track of changes to critical DNSSEC keys without any need for the operator to make changes to configuration files.

5.4.1. Validating Resolver

To configure a validating resolver to use RFC 5011 to maintain a trust anchor, configure the trust anchor using a trust-anchors statement and the initial-key keyword. Information about this can be found in the trust-anchors statement description.

5.4.2. Authoritative Server

To set up an authoritative zone for RFC 5011 trust anchor maintenance, generate two (or more) key signing keys (KSKs) for the zone. Sign the zone with one of them; this is the “active” KSK. All KSKs which do not sign the zone are “stand-by” keys.

Any validating resolver which is configured to use the active KSK as an RFC 5011-managed trust anchor takes note of the stand-by KSKs in the zone’s DNSKEY RRset, and stores them for future reference. The resolver rechecks the zone periodically; after 30 days, if the new key is still there, the key is accepted by the resolver as a valid trust anchor for the zone. Anytime after this 30-day acceptance timer has completed, the active KSK can be revoked, and the zone can be “rolled over” to the newly accepted key.

The easiest way to place a stand-by key in a zone is to use the “smart signing” features of dnssec-keygen and dnssec-signzone. If a key exists with a publication date in the past, but an activation date which is unset or in the future, dnssec-signzone -S includes the DNSKEY record in the zone but does not sign with it:

$ dnssec-keygen -K keys -f KSK -P now -A now+2y example.net
$ dnssec-signzone -S -K keys example.net

To revoke a key, use the command dnssec-revoke. This adds the REVOKED bit to the key flags and regenerates the K*.key and K*.private files.

After revoking the active key, the zone must be signed with both the revoked KSK and the new active KSK. Smart signing takes care of this automatically.

Once a key has been revoked and used to sign the DNSKEY RRset in which it appears, that key is never again accepted as a valid trust anchor by the resolver. However, validation can proceed using the new active key, which was accepted by the resolver when it was a stand-by key.

See RFC 5011 for more details on key rollover scenarios.

When a key has been revoked, its key ID changes, increasing by 128 and wrapping around at 65535. So, for example, the key “Kexample.com.+005+10000” becomes “Kexample.com.+005+10128”.

If two keys have IDs exactly 128 apart and one is revoked, the two key IDs will collide, causing several problems. To prevent this, dnssec-keygen does not generate a new key if another key which may collide is present. This checking only occurs if the new keys are written to the same directory that holds all other keys in use for that zone.

Older versions of BIND 9 did not have this protection. Exercise caution if using key revocation on keys that were generated by previous releases, or if using keys stored in multiple directories or on multiple machines.

It is expected that a future release of BIND 9 will address this problem in a different way, by storing revoked keys with their original unrevoked key IDs.

5.5. PKCS#11 (Cryptoki) Support

Public Key Cryptography Standard #11 (PKCS#11) defines a platform-independent API for the control of hardware security modules (HSMs) and other cryptographic support devices.

PKCS#11 uses a “provider library”: a dynamically loadable library which provides a low-level PKCS#11 interface to drive the HSM hardware. The PKCS#11 provider library comes from the HSM vendor, and it is specific to the HSM to be controlled.

BIND 9 access PKCS#11 libraries via OpenSSL extensions. The extension for OpenSSL 3 and newer is pkcs11-provider. And for the older OpenSSL versions engine_pkcs11 from the OpenSC project can be used.

In both cases the extension is dynamically loaded into OpenSSL and the HSM is operated indirectly; any cryptographic operations not supported by the HSM can be carried out by OpenSSL instead.

5.5.1. Prerequisites

See the documentation provided by the HSM vendor for information about installing, initializing, testing, and troubleshooting the HSM.

5.5.2. Building SoftHSMv2

SoftHSMv2, the latest development version of SoftHSM, is available from https://github.com/opendnssec/SoftHSMv2. It is a software library developed by the OpenDNSSEC project (https://www.opendnssec.org) which provides a PKCS#11 interface to a virtual HSM, implemented in the form of an SQLite3 database on the local filesystem. It provides less security than a true HSM, but it allows users to experiment with native PKCS#11 when an HSM is not available. SoftHSMv2 can be configured to use either OpenSSL or the Botan library to perform cryptographic functions, but when using it for native PKCS#11 in BIND, OpenSSL is required.

By default, the SoftHSMv2 configuration file is prefix/etc/softhsm2.conf (where prefix is configured at compile time). This location can be overridden by the SOFTHSM2_CONF environment variable. The SoftHSMv2 cryptographic store must be installed and initialized before using it with BIND.

$  cd SoftHSMv2
$  configure --with-crypto-backend=openssl --prefix=/opt/pkcs11/usr
$  make
$  make install
$  /opt/pkcs11/usr/bin/softhsm-util --init-token 0 --slot 0 --label softhsmv2

5.5.3. OpenSSL 1.x.x with engine_pkcs11

OpenSSL engine-based PKCS#11 uses engine_pkcs11 OpenSSL engine from libp11 project.

engine_pkcs11 tries to fit the PKCS#11 API within the engine API of OpenSSL. That is, it provides a gateway between PKCS#11 modules and the OpenSSL engine API. One has to register the engine with OpenSSL and one has to provide the path to the PKCS#11 module which should be gatewayed to. This can be done by editing the OpenSSL configuration file, by engine specific controls, or by using the p11-kit proxy module.

It is recommended, that libp11 >= 0.4.12 is used.

For more detailed howto including the examples, we recommend reading:

https://gitlab.isc.org/isc-projects/bind9/-/wikis/BIND-9-PKCS11

When using engine_pkcs11, all BIND binaries potentially need the keys require ‘-E pkcs11’ argument to activate the engine support.

Even though OpenSSL 3 has compatibility support for Engine API it is not recommended to be used due to bugs in OpenSSL and libp11.

It is not possible to generate new keys via the engine_pkcs11 and therefore it is not recommended to use it in a dnssec-policy setup (although it is possible to put previously generated keys in the key-directory and let the key manager select those keys when a key rollover is started.

5.5.4. Configuring engine_pkcs11

The canonical documentation for configuring engine_pkcs11 is in the libp11/README.md, but here’s copy of working configuration for your convenience:

We are going to use our own custom copy of OpenSSL configuration, again it’s driven by an environment variable, this time called OPENSSL_CONF. We are going to copy the global OpenSSL configuration (often found in etc/ssl/openssl.conf) and customize it to use engines_pkcs11.

cp /etc/ssl/openssl.cnf /opt/bind9/etc/openssl.cnf

and export the environment variable:

export OPENSSL_CONF=/opt/bind9/etc/openssl.cnf

Now add the following line at the top of file, before any sections (in square brackets) are defined:

openssl_conf = openssl_init

And make sure there are no other ‘openssl_conf = …’ lines in the file.

Add following lines at the bottom of the file:

[openssl_init]
engines=engine_section

[engine_section]
pkcs11 = pkcs11_section

[pkcs11_section]
engine_id = pkcs11
dynamic_path = <PATHTO>/pkcs11.so
MODULE_PATH = <FULL_PATH_TO_HSM_MODULE>
# if automatic logging to the token is needed, PIN can be specified as below
#PIN = 1234
init = 0

5.5.5. Enabling the OpenSSL Engine in BIND commands

When using OpenSSL Engine-based PKCS#11, the “engine” to be used by OpenSSL can be specified in named and all of the BIND dnssec-* tools by using the -E <engine> command line option. This engine name matches the ‘engine_id’ in the openssl.cnf created in previous section.

The zone signing commences as usual, with only one small difference. We need to provide the name of the OpenSSL engine using the -E command line option.

dnssec-signzone -E pkcs11 -S -o example.net example.net

5.5.6. OpenSSL 3 with pkcs11-provider

OpenSSL provider-based PKCS#11 uses pkcs11-provider project.

pkcs11-provider tries to fit the PKCS#11 API within the Provider API of OpenSSL. That is, it provides a gateway between PKCS#11 modules and the OpenSSL Provider API. One has to register the engine with OpenSSL and one has to provide the path to the PKCS#11 module which should be gatewayed to. This can be done by editing the OpenSSL configuration file, by engine specific controls, or by using the p11-kit proxy module.

It is required to use pkcs11-provider git commit 2e8c26b4157fd21422c66f0b4d7b26cf8c320570 from October 2, 2023 or later.

BIND support for pkcs11-provider is built in and the -E command line option explained above should not be used.

5.5.7. Configuring pkcs11-provider

The canonical documentation for configuring pkcs11-provider is in the provider-pkcs11.7 manual page, but here’s copy of working configuration for your convenience:

We are going to use our own custom copy of OpenSSL configuration, again it’s driven by an environment variable, this time called OPENSSL_CONF. We are going to copy the global OpenSSL configuration (often found in etc/ssl/openssl.conf) and customize it to use pkcs11-provider.

cp /etc/ssl/openssl.cnf /opt/bind9/etc/openssl.cnf

and export the environment variable:

export OPENSSL_CONF=/opt/bind9/etc/openssl.cnf

Now add the following line at the top of file, before any sections (in square brackets) are defined:

openssl_conf = openssl_init

And make sure there are no other ‘openssl_conf = …’ lines in the file.

Add following lines at the bottom of the file:

[openssl_init]
providers = provider_init

[provider_init]
default = default_init
pkcs11 = pkcs11_init

[default_init]
activate = 1

[pkcs11_init]
module = <PATHTO>/pkcs11.so
pkcs11-module-path = <FULL_PATH_TO_HSM_MODULE>
# bind uses the digest+sign api. this is broken with the default load behaviour,
# but works with early load. see: https://github.com/latchset/pkcs11-provider/issues/266
pkcs11-module-load-behavior = early
# no-deinit quirk is needed if you use softhsm2
#pkcs11-module-quirks = no-deinit
# if automatic logging to the token is needed, PIN can be specified as below
# the file referenced should contain just the PIN
#pkcs11-module-token-pin = file:/etc/pki/pin.txt
activate = 1

5.5.8. Key Generation

HSM keys can now be created and used. We are going to assume that you already have a BIND 9 installed, either from a package, or from the sources, and the tools are readily available in the $PATH.

For generating the keys, we are going to use pkcs11-tool available from the OpenSC suite. On both DEB-based and RPM-based distributions, the package is called opensc.

We need to generate at least two RSA keys:

pkcs11-tool --module <FULL_PATH_TO_HSM_MODULE> -l -k --key-type rsa:2048 --label example.net-ksk --pin <PIN>
pkcs11-tool --module <FULL_PATH_TO_HSM_MODULE> -l -k --key-type rsa:2048 --label example.net-zsk --pin <PIN>

Remember that each key should have unique label and we are going to use that label to reference the private key.

Convert the RSA keys stored in the HSM into a format that BIND 9 understands. The dnssec-keyfromlabel tool from BIND 9 can link the raw keys stored in the HSM with the K<zone>+<alg>+<id> files.

You’ll need to provide the OpenSSL engine name (pkcs11) if using the engine and the algorithm (RSASHA256). The key is referenced with the PKCS#11 URI scheme and it can contain the PKCS#11 token label (we asume that it has been initialized as bind9), and the PKCS#11 object label (called label when generating the keys using pkcs11-tool) and the HSM PIN. Refer to RFC7512 for the full PKCS#11 URI specification.

Convert the KSK:

dnssec-keyfromlabel -E pkcs11 -a RSASHA256 -l "pkcs11:token=bind9;object=example.net-ksk;pin-value=0000" -f KSK example.net

and ZSK:

dnssec-keyfromlabel -E pkcs11 -a RSASHA256 -l "pkcs11:token=bind9;object=example.net-zsk;pin-value=0000" example.net

NOTE: you can use PIN stored on disk, by specifying pin-source=<path_to>/<file>, f.e.:

(umask 0700 && echo -n 0000 > /opt/bind9/etc/pin.txt)

and then use in the label specification:

pin-source=/opt/bind9/etc/pin.txt

Confirm that you have one KSK and one ZSK present in the current directory:

ls -l K*

The output should look like this (the second number will be different):

Kexample.net.+008+31729.key
Kexample.net.+008+31729.private
Kexample.net.+008+42231.key
Kexample.net.+008+42231.private

A note on generating ECDSA keys: there is a bug in libp11 when looking up a key, that function compares keys only on their ID, not the label. So when looking up a key it returns the first key, rather than the matching key. The workaround for this is when creating ECDSA keys, you should specify a unique ID:

ksk=$(echo "example.net-ksk" | openssl sha1 -r | awk '{print $1}')
zsk=$(echo "example.net-zsk" | openssl sha1 -r | awk '{print $1}')
pkcs11-tool --module <FULL_PATH_TO_HSM_MODULE> -l -k --key-type EC:prime256v1 --id $ksk --label example.net-ksk --pin <PIN>
pkcs11-tool --module <FULL_PATH_TO_HSM_MODULE> -l -k --key-type EC:prime256v1 --id $zsk --label example.net-zsk --pin <PIN>

5.5.9. Running named With Automatic Zone Re-signing

The zone can also be signed automatically by named. Again, we need to provide the name of the OpenSSL engine using the -E command line option, if using OpenSSL 1.x.x with engine_pkcs11, and this is not needed when using OpenSSL 3.x.x providers.

named -E pkcs11 -c named.conf

and the logs should have lines like:

Fetching example.net/RSASHA256/31729 (KSK) from key repository.
DNSKEY example.net/RSASHA256/31729 (KSK) is now published
DNSKEY example.net/RSA256SHA256/31729 (KSK) is now active
Fetching example.net/RSASHA256/42231 (ZSK) from key repository.
DNSKEY example.net/RSASHA256/42231 (ZSK) is now published
DNSKEY example.net/RSA256SHA256/42231 (ZSK) is now active

For named to dynamically re-sign zones using HSM keys, and/or to sign new records inserted via nsupdate, named must have access to the HSM PIN. In OpenSSL-based PKCS#11, this is accomplished by placing the PIN into the openssl.cnf file (in the above examples, /opt/pkcs11/usr/ssl/openssl.cnf).

See OpenSSL extension specific documentation on how to configure the PIN on global level. Doing so allows the dnssec-\* tools to access the HSM without PIN entry. (The pkcs11-\* tools access the HSM directly, not via OpenSSL, so a PIN is still required to use them.)