diff --git a/bin/named/config.c b/bin/named/config.c index 4900e813cf..acdd159da6 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -299,6 +299,7 @@ dnssec-policy \"default\" {\n\ cds-digest-types { 2; };\n\ dnskey-ttl " DNS_KASP_KEY_TTL ";\n\ inline-signing yes;\n\ + offline-ksk no;\n\ publish-safety " DNS_KASP_PUBLISH_SAFETY "; \n\ retire-safety " DNS_KASP_RETIRE_SAFETY "; \n\ purge-keys " DNS_KASP_PURGE_KEYS "; \n\ diff --git a/bin/tests/system/checkconf/good-kasp.conf b/bin/tests/system/checkconf/good-kasp.conf index 42e2478f96..e0a3241368 100644 --- a/bin/tests/system/checkconf/good-kasp.conf +++ b/bin/tests/system/checkconf/good-kasp.conf @@ -30,6 +30,7 @@ dnssec-policy "test" { }; max-zone-ttl 86400; nsec3param iterations 0 optout no salt-length 8; + offline-ksk no; parent-ds-ttl 7200; parent-propagation-delay PT1H; publish-safety PT3600S; diff --git a/bin/tests/system/checkconf/kasp-bad-offline-ksk.conf b/bin/tests/system/checkconf/kasp-bad-offline-ksk.conf new file mode 100644 index 0000000000..4a44d92b0e --- /dev/null +++ b/bin/tests/system/checkconf/kasp-bad-offline-ksk.conf @@ -0,0 +1,31 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Offline KSK is not possible with CSK + * (even if there are other key roles present). + */ +dnssec-policy "bad-offline-ksk" { + offline-ksk yes; + keys { + ksk lifetime P10Y algorithm rsasha256; + zsk lifetime P10Y algorithm rsasha256; + csk lifetime P10Y algorithm rsasha256; + }; +}; + +zone "example.net" { + type primary; + file "example.db"; + dnssec-policy "bad-offline-ksk"; +}; diff --git a/bin/tests/system/checkconf/tests.sh b/bin/tests/system/checkconf/tests.sh index ad7e2fbb47..fc776b1d47 100644 --- a/bin/tests/system/checkconf/tests.sh +++ b/bin/tests/system/checkconf/tests.sh @@ -676,6 +676,14 @@ grep "dnssec-policy: key with algorithm rsasha256 has invalid key length 511" checkconf.out$n 2>&1 && ret=1 +grep "dnssec-policy: csk keys are not allowed when offline-ksk is enabled" /dev/null || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + n=$((n + 1)) echo_i "checking named-checkconf kasp signatures refresh errors ($n)" ret=0 diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 0a3075edc2..f427f6b02f 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -6536,6 +6536,20 @@ The following options can be specified in a :any:`dnssec-policy` statement: ``insecure``. In this specific case you should move the existing key files to the zone's ``key-directory`` from the new configuration. +.. namedconf:statement:: offline-ksk + :tags: dnssec + :short: Specifies whether the DNSKEY, CDS, and CDNSKEY RRsets are being signed offline. + + If enabled, BIND 9 does not generate signatures for the DNSKEY, CDS, and + CDNSKEY RRsets. Instead, the signed DNSKEY, CDS and CDNSKEY RRsets are + looked up from Signed Key Response (SKR) files. + + Any existing DNSKEY, CDS, and CDNSKEY RRsets in the unsigned version of the + zone are filtered and replaced with RRsets from the SKR file. + + This feature is off by default. Configuring ``offline-ksk`` in conjunction + with a CSK is a configuration error. + .. namedconf:statement:: purge-keys :tags: dnssec :short: Specifies the amount of time after which DNSSEC keys that have been deleted from the zone can be removed from disk. diff --git a/doc/misc/dnssec-policy.default.conf b/doc/misc/dnssec-policy.default.conf index a6f526c743..d5571bac04 100644 --- a/doc/misc/dnssec-policy.default.conf +++ b/doc/misc/dnssec-policy.default.conf @@ -13,6 +13,7 @@ dnssec-policy "default" { // Keys + offline-ksk no; keys { csk key-directory lifetime unlimited algorithm 13; }; diff --git a/doc/misc/options b/doc/misc/options index ff3c95ddf6..0785c4d7bf 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -18,6 +18,7 @@ dnssec-policy { keys { ( csk | ksk | zsk ) [ key-directory | key-store ] lifetime algorithm [ ]; ... }; max-zone-ttl ; nsec3param [ iterations ] [ optout ] [ salt-length ]; + offline-ksk ; parent-ds-ttl ; parent-propagation-delay ; publish-safety ; diff --git a/lib/dns/include/dns/kasp.h b/lib/dns/include/dns/kasp.h index d0187f8045..26af469546 100644 --- a/lib/dns/include/dns/kasp.h +++ b/lib/dns/include/dns/kasp.h @@ -90,6 +90,7 @@ struct dns_kasp { uint32_t signatures_validity_dnskey; /* Configuration: Keys */ + bool offlineksk; bool cdnskey; dns_kasp_digestlist_t digests; dns_kasp_keylist_t keys; @@ -809,6 +810,28 @@ dns_kasp_setnsec3param(dns_kasp_t *kasp, uint8_t iter, bool optout, * */ +bool +dns_kasp_offlineksk(dns_kasp_t *kasp); +/*%< + * Should we be using Offline KSK key management? + * + * Requires: + * + *\li 'kasp' is a valid, frozen kasp. + * + */ + +void +dns_kasp_setofflineksk(dns_kasp_t *kasp, bool offlineksk); +/*%< + * Enable/disable Offline KSK. + * + * Requires: + * + *\li 'kasp' is a valid, unfrozen kasp. + * + */ + bool dns_kasp_cdnskey(dns_kasp_t *kasp); /*%< @@ -823,7 +846,7 @@ dns_kasp_cdnskey(dns_kasp_t *kasp); void dns_kasp_setcdnskey(dns_kasp_t *kasp, bool cdnskey); /*%< - * Set to enable publication of CDNSKEY records. + * Enable/disable publication of CDNSKEY records. * * Requires: * diff --git a/lib/dns/kasp.c b/lib/dns/kasp.c index d31af74574..d300e32f7c 100644 --- a/lib/dns/kasp.c +++ b/lib/dns/kasp.c @@ -595,6 +595,22 @@ dns_kasp_setnsec3param(dns_kasp_t *kasp, uint8_t iter, bool optout, kasp->nsec3param.saltlen = saltlen; } +bool +dns_kasp_offlineksk(dns_kasp_t *kasp) { + REQUIRE(kasp != NULL); + REQUIRE(kasp->frozen); + + return kasp->offlineksk; +} + +void +dns_kasp_setofflineksk(dns_kasp_t *kasp, bool offlineksk) { + REQUIRE(kasp != NULL); + REQUIRE(!kasp->frozen); + + kasp->offlineksk = offlineksk; +} + bool dns_kasp_cdnskey(dns_kasp_t *kasp) { REQUIRE(kasp != NULL); diff --git a/lib/isccfg/kaspconf.c b/lib/isccfg/kaspconf.c index 419818f257..34d246f621 100644 --- a/lib/isccfg/kaspconf.c +++ b/lib/isccfg/kaspconf.c @@ -113,7 +113,7 @@ get_string(const cfg_obj_t **maps, const char *option) { static isc_result_t cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp, bool check_algorithms, isc_log_t *logctx, - dns_keystorelist_t *keystorelist, + bool offline_ksk, dns_keystorelist_t *keystorelist, uint32_t ksk_min_lifetime, uint32_t zsk_min_lifetime) { isc_result_t result; dns_kasp_key_t *key = NULL; @@ -126,6 +126,7 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp, if (config == NULL) { /* We are creating a key reference for the default kasp. */ + INSIST(!offline_ksk); key->role |= DNS_KASP_KEY_ROLE_KSK | DNS_KASP_KEY_ROLE_ZSK; key->lifetime = 0; /* unlimited */ key->algorithm = DNS_KEYALG_ECDSA256; @@ -149,6 +150,14 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp, } else if (strcmp(rolestr, "zsk") == 0) { key->role |= DNS_KASP_KEY_ROLE_ZSK; } else if (strcmp(rolestr, "csk") == 0) { + if (offline_ksk) { + cfg_obj_log( + config, logctx, ISC_LOG_ERROR, + "dnssec-policy: csk keys are not " + "allowed when offline-ksk is enabled"); + result = ISC_R_FAILURE; + goto cleanup; + } key->role |= DNS_KASP_KEY_ROLE_KSK; key->role |= DNS_KASP_KEY_ROLE_ZSK; } @@ -418,6 +427,7 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, uint32_t zonepropdelay = 0, parentpropdelay = 0; uint32_t ipub = 0, iret = 0; uint32_t ksk_min_lifetime = 0, zsk_min_lifetime = 0; + bool offline_ksk = false; REQUIRE(config != NULL); REQUIRE(kaspp != NULL && *kaspp == NULL); @@ -539,6 +549,13 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, dns_kasp_setparentpropagationdelay(kasp, parentpropdelay); /* Configuration: Keys */ + obj = NULL; + (void)confget(maps, "offline-ksk", &obj); + if (obj != NULL) { + offline_ksk = cfg_obj_asboolean(obj); + } + dns_kasp_setofflineksk(kasp, offline_ksk); + obj = NULL; (void)confget(maps, "cdnskey", &obj); if (obj != NULL) { @@ -596,7 +613,7 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, cfg_obj_t *kobj = cfg_listelt_value(element); result = cfg_kaspkey_fromconfig( kobj, kasp, check_algorithms, logctx, - keystorelist, ksk_min_lifetime, + offline_ksk, keystorelist, ksk_min_lifetime, zsk_min_lifetime); if (result != ISC_R_SUCCESS) { cfg_obj_log(kobj, logctx, ISC_LOG_ERROR, diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 0ad4eb6755..9adc7dcab6 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2278,6 +2278,7 @@ static cfg_clausedef_t dnssecpolicy_clauses[] = { { "keys", &cfg_type_kaspkeys, 0 }, { "max-zone-ttl", &cfg_type_duration, 0 }, { "nsec3param", &cfg_type_nsec3, 0 }, + { "offline-ksk", &cfg_type_boolean, 0 }, { "parent-ds-ttl", &cfg_type_duration, 0 }, { "parent-propagation-delay", &cfg_type_duration, 0 }, { "parent-registration-delay", &cfg_type_duration,