From 2710d9a11d0602814ff3454e9d319420f1578a0c Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 21 Apr 2021 16:09:06 +0200 Subject: [PATCH 1/6] Add built-in dnssec-policy "insecure" Add a new built-in policy "insecure", to be used to gracefully unsign a zone. Previously you could just remove the 'dnssec-policy' configuration from your zone statement, or remove it. The built-in policy "none" (or not configured) now actually means no DNSSEC maintenance for the corresponding zone. So if you immediately reconfigure your zone from whatever policy to "none", your zone will temporarily be seen as bogus by validating resolvers. This means we can remove the functions 'dns_zone_use_kasp()' and 'dns_zone_secure_to_insecure()' again. We also no longer have to check for the existence of key state files to figure out if a zone is transitioning to insecure. --- bin/named/server.c | 6 +- bin/named/zoneconf.c | 43 +++++++-------- lib/bind9/check.c | 2 + lib/dns/include/dns/zone.h | 26 --------- lib/dns/update.c | 3 +- lib/dns/win32/libdns.def.in | 2 - lib/dns/zone.c | 106 +++++++----------------------------- lib/isccfg/kaspconf.c | 6 +- 8 files changed, 49 insertions(+), 145 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index 136c922384..6342ff31a1 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -9174,7 +9174,7 @@ load_configuration(const char *filename, named_server_t *server, dns_kasp_detach(&kasp); } /* - * Create the built-in kasp policies ("default", "none"). + * Create the built-in kasp policies ("default", "insecure"). */ kasp = NULL; CHECK(cfg_kasp_fromconfig(NULL, "default", named_g_mctx, named_g_lctx, @@ -9184,7 +9184,7 @@ load_configuration(const char *filename, named_server_t *server, dns_kasp_detach(&kasp); kasp = NULL; - CHECK(cfg_kasp_fromconfig(NULL, "none", named_g_mctx, named_g_lctx, + CHECK(cfg_kasp_fromconfig(NULL, "insecure", named_g_mctx, named_g_lctx, &kasplist, &kasp)); INSIST(kasp != NULL); dns_kasp_freeze(kasp); @@ -14827,7 +14827,7 @@ named_server_signing(named_server_t *server, isc_lex_t *lex, CHECK(ISC_R_UNEXPECTEDEND); } - if (dns_zone_use_kasp(zone)) { + if (dns_zone_getkasp(zone) != NULL) { (void)putstr(text, "zone uses dnssec-policy, use rndc dnssec " "command instead"); (void)putnull(text); diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index e0b17e65f5..c82cf76850 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1249,15 +1249,23 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, result = named_config_get(maps, "dnssec-policy", &obj); if (result == ISC_R_SUCCESS) { kaspname = cfg_obj_asstring(obj); - result = dns_kasplist_find(kasplist, kaspname, &kasp); - if (result != ISC_R_SUCCESS) { - cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR, - "'dnssec-policy '%s' not found ", - kaspname); - RETERR(result); + if (strcmp(kaspname, "none") != 0) { + result = dns_kasplist_find(kasplist, kaspname, + &kasp); + if (result != ISC_R_SUCCESS) { + cfg_obj_log( + obj, named_g_lctx, + ISC_LOG_ERROR, + "dnssec-policy '%s' not found ", + kaspname); + RETERR(result); + } + dns_zone_setkasp(zone, kasp); + use_kasp = true; } - dns_zone_setkasp(zone, kasp); - use_kasp = dns_zone_use_kasp(zone); + } + if (!use_kasp) { + dns_zone_setkasp(zone, NULL); } obj = NULL; @@ -1671,10 +1679,11 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, obj = NULL; result = cfg_map_get(zoptions, "auto-dnssec", &obj); - if (kasp != NULL && strcmp(dns_kasp_getname(kasp), "none") != 0) - { + if (kasp != NULL) { + bool s2i = (strcmp(dns_kasp_getname(kasp), + "insecure") != 0); dns_zone_setkeyopt(zone, DNS_ZONEKEY_ALLOW, true); - dns_zone_setkeyopt(zone, DNS_ZONEKEY_CREATE, true); + dns_zone_setkeyopt(zone, DNS_ZONEKEY_CREATE, !s2i); dns_zone_setkeyopt(zone, DNS_ZONEKEY_MAINTAIN, true); } else if (result == ISC_R_SUCCESS) { const char *arg = cfg_obj_asstring(obj); @@ -1691,11 +1700,6 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, dns_zone_setkeyopt(zone, DNS_ZONEKEY_ALLOW, allow); dns_zone_setkeyopt(zone, DNS_ZONEKEY_CREATE, false); dns_zone_setkeyopt(zone, DNS_ZONEKEY_MAINTAIN, maint); - } else { - bool s2i = dns_zone_secure_to_insecure(zone, false); - dns_zone_setkeyopt(zone, DNS_ZONEKEY_ALLOW, s2i); - dns_zone_setkeyopt(zone, DNS_ZONEKEY_CREATE, false); - dns_zone_setkeyopt(zone, DNS_ZONEKEY_MAINTAIN, s2i); } } @@ -2216,13 +2220,6 @@ named_zone_inlinesigning(dns_zone_t *zone, const cfg_obj_t *zconfig, dns_zone_log(zone, ISC_LOG_DEBUG(1), "inline-signing: " "implicitly through dnssec-policy"); - } else { - inline_signing = dns_zone_secure_to_insecure(zone, - true); - dns_zone_log( - zone, ISC_LOG_DEBUG(1), "inline-signing: %s", - inline_signing ? "transitioning to insecure" - : "no"); } } diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 9555ba37f9..13e1da994b 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -2634,6 +2634,8 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, if (strcmp(kaspname, "default") == 0) { has_dnssecpolicy = true; + } else if (strcmp(kaspname, "insecure") == 0) { + has_dnssecpolicy = true; } else if (strcmp(kaspname, "none") == 0) { has_dnssecpolicy = false; } else { diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 822989ab82..48e3ae34b5 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -686,32 +686,6 @@ dns_zone_getkasp(dns_zone_t *zone); *\li 'zone' to be a valid zone. */ -bool -dns_zone_secure_to_insecure(dns_zone_t *zone, bool reconfig); -/*%< - * Returns true if the zone is transitioning to insecure. - * Only can happen if a zone previously used a dnssec-policy, - * but changed the value to "none" (or removed the configuration - * option). If 'reconfig' is true, only check the key files, - * because the zone structure is not yet updated with the - * newest configuration. - * - * Require: - *\li 'zone' to be a valid zone. - */ - -bool -dns_zone_use_kasp(dns_zone_t *zone); -/*%< - * Check if zone needs to use kasp. - * True if there is a policy that is not "none", - * or if there are state files associated with the keys - * related to this zone. - * - * Require: - *\li 'zone' to be a valid zone. - */ - void dns_zone_setkasp(dns_zone_t *zone, dns_kasp_t *kasp); /*%< diff --git a/lib/dns/update.c b/lib/dns/update.c index 1b923f692e..6b8b868b99 100644 --- a/lib/dns/update.c +++ b/lib/dns/update.c @@ -1087,6 +1087,7 @@ add_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, bool keyset_kskonly) { isc_result_t result; dns_dbnode_t *node = NULL; + dns_kasp_t *kasp = dns_zone_getkasp(zone); dns_rdataset_t rdataset; dns_rdata_t sig_rdata = DNS_RDATA_INIT; dns_stats_t *dnssecsignstats = dns_zone_getdnssecsignstats(zone); @@ -1097,7 +1098,7 @@ add_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, bool use_kasp = false; isc_mem_t *mctx = diff->mctx; - if (dns_zone_use_kasp(zone)) { + if (kasp != NULL) { check_ksk = false; keyset_kskonly = true; use_kasp = true; diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index c22be8ee2b..bcf586d02b 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -1292,7 +1292,6 @@ dns_zone_rekey dns_zone_replacedb dns_zone_rpz_enable dns_zone_rpz_enable_db -dns_zone_secure_to_insecure dns_zone_set_parentcatz dns_zone_setadded dns_zone_setalsonotify @@ -1378,7 +1377,6 @@ dns_zone_setzeronosoattl dns_zone_signwithkey dns_zone_synckeyzone dns_zone_unload -dns_zone_use_kasp dns_zone_verifydb dns_zonekey_iszonekey dns_zonemgr_attach diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 00e6dd2f4e..7ccacf6e80 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -5736,82 +5736,6 @@ dns_zone_getkasp(dns_zone_t *zone) { return (zone->kasp); } -static bool -statefile_exist(dns_zone_t *zone) { - isc_result_t ret; - dns_dnsseckeylist_t keys; - dns_dnsseckey_t *key = NULL; - isc_stdtime_t now; - isc_time_t timenow; - bool found = false; - - TIME_NOW(&timenow); - now = isc_time_seconds(&timenow); - - ISC_LIST_INIT(keys); - - ret = dns_dnssec_findmatchingkeys(dns_zone_getorigin(zone), - dns_zone_getkeydirectory(zone), now, - dns_zone_getmctx(zone), &keys); - if (ret == ISC_R_SUCCESS) { - for (key = ISC_LIST_HEAD(keys); key != NULL; - key = ISC_LIST_NEXT(key, link)) { - if (dst_key_haskasp(key->key)) { - found = true; - break; - } - } - } - - /* Clean up keys */ - while (!ISC_LIST_EMPTY(keys)) { - key = ISC_LIST_HEAD(keys); - ISC_LIST_UNLINK(keys, key, link); - dns_dnsseckey_destroy(dns_zone_getmctx(zone), &key); - } - - return (found); -} - -bool -dns_zone_secure_to_insecure(dns_zone_t *zone, bool reconfig) { - REQUIRE(DNS_ZONE_VALID(zone)); - - /* - * If checking during reconfig, the zone is not yet updated - * with the new kasp configuration, so only check the key - * files. - */ - if (reconfig) { - return (statefile_exist(zone)); - } - - if (zone->kasp == NULL) { - return (false); - } - if (strcmp(dns_kasp_getname(zone->kasp), "none") != 0) { - return (false); - } - /* - * "dnssec-policy none", but if there are key state files - * this zone used to be secure but is transitioning back to - * insecure. - */ - return (statefile_exist(zone)); -} - -bool -dns_zone_use_kasp(dns_zone_t *zone) { - dns_kasp_t *kasp = dns_zone_getkasp(zone); - - if (kasp == NULL) { - return (false); - } else if (strcmp(dns_kasp_getname(kasp), "none") != 0) { - return (true); - } - return dns_zone_secure_to_insecure(zone, false); -} - void dns_zone_setoption(dns_zone_t *zone, dns_zoneopt_t option, bool value) { REQUIRE(DNS_ZONE_VALID(zone)); @@ -6793,7 +6717,7 @@ add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, dns_zone_t *zone, unsigned int i, j; bool use_kasp = false; - if (dns_zone_use_kasp(zone)) { + if (dns_zone_getkasp(zone) != NULL) { check_ksk = false; keyset_kskonly = true; use_kasp = true; @@ -7311,7 +7235,7 @@ signed_with_good_key(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node, dns_rdata_reset(&rdata); } - if (dns_zone_use_kasp(zone)) { + if (dns_zone_getkasp(zone) != NULL) { dns_kasp_key_t *kkey; int zsk_count = 0; bool approved; @@ -7523,7 +7447,7 @@ sign_a_node(dns_db_t *db, dns_zone_t *zone, dns_name_t *name, } else if (is_zsk && !dst_key_is_signing(key, DST_BOOL_ZSK, inception, &when)) { /* Only applies to dnssec-policy. */ - if (dns_zone_use_kasp(zone)) { + if (dns_zone_getkasp(zone) != NULL) { goto next_rdataset; } } @@ -9262,7 +9186,7 @@ zone_sign(dns_zone_t *zone) { signing = ISC_LIST_HEAD(zone->signing); first = true; - if (dns_zone_use_kasp(zone)) { + if (dns_zone_getkasp(zone) != NULL) { check_ksk = false; keyset_kskonly = true; use_kasp = true; @@ -16659,7 +16583,7 @@ copy_non_dnssec_records(dns_zone_t *zone, dns_db_t *db, dns_db_t *version, * Allow DNSSEC records with dnssec-policy. * WMM: Perhaps add config option for it. */ - if (!dns_zone_use_kasp(zone)) { + if (dns_zone_getkasp(zone) == NULL) { dns_rdataset_disassociate(&rdataset); continue; } @@ -19961,7 +19885,7 @@ zone_rekey(dns_zone_t *zone) { dns__zonediff_t zonediff; bool commit = false, newactive = false; bool newalg = false; - bool fullsign, use_kasp; + bool fullsign; dns_ttl_t ttl = 3600; const char *dir = NULL; isc_mem_t *mctx = NULL; @@ -20035,7 +19959,6 @@ zone_rekey(dns_zone_t *zone) { fullsign = DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_FULLSIGN); kasp = dns_zone_getkasp(zone); - use_kasp = dns_zone_use_kasp(zone); if (kasp != NULL) { LOCK(&kasp->lock); } @@ -20048,10 +19971,14 @@ zone_rekey(dns_zone_t *zone) { isc_result_totext(result)); } - if (use_kasp && (result == ISC_R_SUCCESS || result == ISC_R_NOTFOUND)) { + if (kasp != NULL && + (result == ISC_R_SUCCESS || result == ISC_R_NOTFOUND)) { result = dns_keymgr_run(&zone->origin, zone->rdclass, dir, mctx, &keys, kasp, now, &nexttime); if (result != ISC_R_SUCCESS) { + if (kasp != NULL) { + UNLOCK(&kasp->lock); + } dnssec_log(zone, ISC_LOG_ERROR, "zone_rekey:dns_dnssec_keymgr failed: %s", isc_result_totext(result)); @@ -20064,12 +19991,17 @@ zone_rekey(dns_zone_t *zone) { } if (result == ISC_R_SUCCESS) { + bool cds_delete = false; + isc_stdtime_t when; + /* * Publish CDS/CDNSKEY DELETE records if the zone is * transitioning from secure to insecure. */ - bool cds_delete = dns_zone_secure_to_insecure(zone, false); - isc_stdtime_t when; + if (kasp != NULL && + strcmp(dns_kasp_getname(kasp), "insecure") == 0) { + cds_delete = true; + } /* * Only update DNSKEY TTL if we have a policy. @@ -20329,7 +20261,7 @@ zone_rekey(dns_zone_t *zone) { /* * If keymgr provided a next time, use the calculated next rekey time. */ - if (use_kasp) { + if (kasp != NULL) { isc_time_t timenext; uint32_t nexttime_seconds; diff --git a/lib/isccfg/kaspconf.c b/lib/isccfg/kaspconf.c index 5b458473ca..8a119fb612 100644 --- a/lib/isccfg/kaspconf.c +++ b/lib/isccfg/kaspconf.c @@ -326,9 +326,9 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, const char *name, isc_mem_t *mctx, } } INSIST(!(dns_kasp_keylist_empty(kasp))); - } else if (strcmp(kaspname, "none") == 0) { - /* "dnssec-policy none": key list must be empty */ - INSIST(strcmp(kaspname, "none") == 0); + } else if (strcmp(kaspname, "insecure") == 0) { + /* "dnssec-policy insecure": key list must be empty */ + INSIST(strcmp(kaspname, "insecure") == 0); INSIST(dns_kasp_keylist_empty(kasp)); } else { /* No keys clause configured, use the "default". */ From 17e3b056c87c912127fe94181108b4df898915f7 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 21 Apr 2021 16:33:04 +0200 Subject: [PATCH 2/6] Update kasp tests to "insecure" policy The tests for going insecure should be changed to use the built-in "insecure" policy. The function that checks dnssec status output should again check for the special case "none". --- bin/tests/system/kasp.sh | 32 +++++++++++++----------- bin/tests/system/kasp/ns6/named2.conf.in | 8 +++--- bin/tests/system/kasp/tests.sh | 8 +++--- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/bin/tests/system/kasp.sh b/bin/tests/system/kasp.sh index a619c0e6a9..7594f90b97 100644 --- a/bin/tests/system/kasp.sh +++ b/bin/tests/system/kasp.sh @@ -196,8 +196,8 @@ set_policy() { CDS_DELETE="no" } # By default policies are considered to be secure. -# If a zone sets its policy to "none", call 'set_cdsdelete' to tell the system -# test to expect a CDS and CDNSKEY Delete record. +# If a zone sets its policy to "insecure", call 'set_cdsdelete' to tell the +# system test to expect a CDS and CDNSKEY Delete record. set_cdsdelete() { CDS_DELETE="yes" } @@ -779,18 +779,22 @@ check_dnssecstatus() { _rndccmd $_server dnssec -status $_zone in $_view > rndc.dnssec.status.out.$_zone.$n || _log_error "rndc dnssec -status zone ${_zone} failed" - grep "dnssec-policy: ${_policy}" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "bad dnssec status for signed zone ${_zone}" - if [ "$(key_get KEY1 EXPECT)" = "yes" ]; then - grep "key: $(key_get KEY1 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "missing key $(key_get KEY1 ID) from dnssec status" - fi - if [ "$(key_get KEY2 EXPECT)" = "yes" ]; then - grep "key: $(key_get KEY2 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "missing key $(key_get KEY2 ID) from dnssec status" - fi - if [ "$(key_get KEY3 EXPECT)" = "yes" ]; then - grep "key: $(key_get KEY3 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "missing key $(key_get KEY3 ID) from dnssec status" - fi - if [ "$(key_get KEY4 EXPECT)" = "yes" ]; then - grep "key: $(key_get KEY4 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "missing key $(key_get KEY4 ID) from dnssec status" + if [ "$_policy" = "none" ]; then + grep "Zone does not have dnssec-policy" rndc.dnssec.status.out.$_zone.$n > /dev/null || log_error "bad dnssec status for unsigned zone ${_zone}" + else + grep "dnssec-policy: ${_policy}" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "bad dnssec status for signed zone ${_zone}" + if [ "$(key_get KEY1 EXPECT)" = "yes" ]; then + grep "key: $(key_get KEY1 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "missing key $(key_get KEY1 ID) from dnssec status" + fi + if [ "$(key_get KEY2 EXPECT)" = "yes" ]; then + grep "key: $(key_get KEY2 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "missing key $(key_get KEY2 ID) from dnssec status" + fi + if [ "$(key_get KEY3 EXPECT)" = "yes" ]; then + grep "key: $(key_get KEY3 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "missing key $(key_get KEY3 ID) from dnssec status" + fi + if [ "$(key_get KEY4 EXPECT)" = "yes" ]; then + grep "key: $(key_get KEY4 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "missing key $(key_get KEY4 ID) from dnssec status" + fi fi test "$ret" -eq 0 || echo_i "failed" diff --git a/bin/tests/system/kasp/ns6/named2.conf.in b/bin/tests/system/kasp/ns6/named2.conf.in index 731cf4ced3..8967c8a44a 100644 --- a/bin/tests/system/kasp/ns6/named2.conf.in +++ b/bin/tests/system/kasp/ns6/named2.conf.in @@ -39,26 +39,26 @@ controls { zone "step1.going-insecure.kasp" { type master; file "step1.going-insecure.kasp.db"; - dnssec-policy "none"; + dnssec-policy "insecure"; }; zone "step2.going-insecure.kasp" { type master; file "step2.going-insecure.kasp.db"; - dnssec-policy "none"; + dnssec-policy "insecure"; }; zone "step1.going-insecure-dynamic.kasp" { type master; file "step1.going-insecure-dynamic.kasp.db"; - dnssec-policy "none"; + dnssec-policy "insecure"; allow-update { any; }; }; zone "step2.going-insecure-dynamic.kasp" { type master; file "step2.going-insecure-dynamic.kasp.db"; - dnssec-policy "none"; + dnssec-policy "insecure"; allow-update { any; }; }; diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index 6e5a906d92..6f184c196b 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -3598,7 +3598,7 @@ wait_for_done_signing() { # Zone: step1.going-insecure.kasp # set_zone "step1.going-insecure.kasp" -set_policy "none" "2" "7200" +set_policy "insecure" "2" "7200" set_server "ns6" "10.53.0.6" # Expect a CDS/CDNSKEY Delete Record. set_cdsdelete @@ -3635,7 +3635,7 @@ check_next_key_event 93600 # Zone: step2.going-insecure.kasp # set_zone "step2.going-insecure.kasp" -set_policy "none" "2" "7200" +set_policy "insecure" "2" "7200" set_server "ns6" "10.53.0.6" # The DS is long enough removed from the zone to be considered HIDDEN. @@ -3665,7 +3665,7 @@ check_next_key_event 7500 # set_zone "step1.going-insecure-dynamic.kasp" set_dynamic -set_policy "none" "2" "7200" +set_policy "insecure" "2" "7200" set_server "ns6" "10.53.0.6" # Expect a CDS/CDNSKEY Delete Record. set_cdsdelete @@ -3703,7 +3703,7 @@ check_next_key_event 93600 # set_zone "step2.going-insecure-dynamic.kasp" set_dynamic -set_policy "none" "2" "7200" +set_policy "insecure" "2" "7200" set_server "ns6" "10.53.0.6" # The DS is long enough removed from the zone to be considered HIDDEN. From 9c6ff463fdd26aab45dd95b68d6c0816da753545 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 21 Apr 2021 16:37:17 +0200 Subject: [PATCH 3/6] Add test for "insecure" policy While it is meant to be used for transitioning a zone to insecure, add a test case where a zone uses the "insecure" policy immediately. The zone will go through DNSSEC maintenance, but the outcome should be the same as 'dnssec-policy none;', that is the zone should be unsigned. --- bin/tests/system/kasp/ns3/named.conf.in | 7 +++++++ bin/tests/system/kasp/ns3/setup.sh | 7 +++++++ bin/tests/system/kasp/tests.sh | 17 +++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/bin/tests/system/kasp/ns3/named.conf.in b/bin/tests/system/kasp/ns3/named.conf.in index 28e9c6aee3..6e6f7bfa06 100644 --- a/bin/tests/system/kasp/ns3/named.conf.in +++ b/bin/tests/system/kasp/ns3/named.conf.in @@ -100,6 +100,13 @@ zone "unsigned.kasp" { dnssec-policy "none"; }; +/* A zone that is initially set to insecure. */ +zone "insecure.kasp" { + type primary; + file "insecure.kasp.db"; + dnssec-policy "insecure"; +}; + /* A master zone with dnssec-policy but keys already created. */ zone "dnssec-keygen.kasp" { type primary; diff --git a/bin/tests/system/kasp/ns3/setup.sh b/bin/tests/system/kasp/ns3/setup.sh index 2ffca051e9..55e862856c 100644 --- a/bin/tests/system/kasp/ns3/setup.sh +++ b/bin/tests/system/kasp/ns3/setup.sh @@ -79,6 +79,13 @@ zonefile="${zone}.db" infile="${zone}.db.infile" cp template.db.in $zonefile +# Set up zone that stays unsigned. +zone="insecure.kasp" +echo_i "setting up zone: $zone" +zonefile="${zone}.db" +infile="${zone}.db.infile" +cp template.db.in $zonefile + # Some of these zones already have keys. zone="dnssec-keygen.kasp" $KEYGEN -k rsasha1 -l policies/kasp.conf $zone > keygen.out.$zone.1 2>&1 diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index 6f184c196b..a2969fd9f9 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -784,6 +784,23 @@ check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" check_apex check_subdomain +# +# Zone: insecure.kasp. +# +set_zone "insecure.kasp" +set_policy "insecure" "0" "0" +set_server "ns3" "10.53.0.3" + +key_clear "KEY1" +key_clear "KEY2" +key_clear "KEY3" +key_clear "KEY4" + +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" +check_apex +check_subdomain + # # Zone: unlimited.kasp. # From fadc57d3d03bb30c036104f7923a164b22db053b Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 21 Apr 2021 16:39:28 +0200 Subject: [PATCH 4/6] Update documentation with "insecure" policy Update the ARM to mention the new built-in "insecure" policy. Update the DNSSEC guide recipe "Revert to unsigned" to add the additional step of reconfiguring the zone to "insecure" (instead of immediately set it to "none"). --- doc/arm/reference.rst | 12 ++++++------ doc/dnssec-guide/recipes.rst | 24 ++++++++++++++++++++---- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 7035bebd78..68748252e2 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -1443,12 +1443,12 @@ default is used. reduced. ``dnssec-policy`` - This specifies which key and signing policy (KASP) should be used for this zone. - This is a string referring to a ``dnssec-policy`` statement. There are two - built-in policies: ``default``, which uses the default policy, and - ``none``, which means no DNSSEC policy and keeps the zone unsigned. The - default is ``none``. See :ref:`dnssec-policy Grammar - ` for more details. + This specifies which key and signing policy (KASP) should be used for this + zone. This is a string referring to a ``dnssec-policy`` statement. There + are three built-in policies: ``default``, which uses the default policy, + ``insecure``, to be used when you want to gracefully unsign your zone, and + ``none``, which means no DNSSEC policy. The default is ``none``. + See :ref:`dnssec-policy Grammar ` for more details. ``dnssec-update-mode`` If this option is set to its default value of ``maintain`` in a zone diff --git a/doc/dnssec-guide/recipes.rst b/doc/dnssec-guide/recipes.rst index 0b98b42b8e..1d45c039f3 100644 --- a/doc/dnssec-guide/recipes.rst +++ b/doc/dnssec-guide/recipes.rst @@ -1069,8 +1069,8 @@ Below is an example showing how to remove DS records using the To be on the safe side, wait a while before actually deleting all signed data from your zone, just in case some validating resolvers have cached information. After you are certain that all cached -information has expired (usually this means one TTL interval has passed), you may -reconfigure your zone. +information has expired (usually this means one TTL interval has passed), +you may reconfigure your zone. Here is what ``named.conf`` looks like when it is signed: @@ -1083,7 +1083,7 @@ Here is what ``named.conf`` looks like when it is signed: dnssec-policy "default"; }; -Remove the ``dnssec-policy`` line so your ``named.conf`` looks like this: +Change your ``dnssec-policy`` line to indicate you want to revert to unsigned: :: @@ -1091,8 +1091,24 @@ Remove the ``dnssec-policy`` line so your ``named.conf`` looks like this: type primary; file "db/example.com.db"; allow-transfer { any; }; + dnssec-policy "insecure"; }; Then use ``rndc reload`` to reload the zone. -Your zone is now reverted back to the traditional, insecure DNS format. +The "insecure" policy is a built-in policy (like "default"). It will make sure +the zone is still DNSSEC maintained, to allow for a graceful transition to +unsigned, + +When the DS records have been removed from the parent zone, use +``rndc dnssec -checkds -key withdrawn example.com`` to tell ``named`` that +the DS is removed, and the remaining DNSSEC records will be removed in a timely +manner. + +After a while, your zone is reverted back to the traditional, insecure DNS +format. You can verify by checking that all DNSKEY and RRSIG records have been +removed from the zone. + +You can then remove the ``dnssec-policy`` line from your ``named.conf`` and +reload the zone. The zone will now no longer be subject to any DNSSEC +maintenance. From 75024736a4b0781dff26dcd6bd000f8ec8971a24 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 21 Apr 2021 16:48:24 +0200 Subject: [PATCH 5/6] Release notes and changes for [#2645] The feature "going insecure gracefully" has been changed. --- CHANGES | 4 ++++ doc/notes/notes-current.rst | 13 ++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index b70b4abcef..4b4d18e57d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +5632. [func] Add built-in dnssec-policy "insecure". This is used to + transition a zone from a signed state to a unsigned + state. [GL #2645] + 5631. [bug] Update ZONEMD to match RFC 8976. [GL #2658] 5630. [func] Treat DNSSEC responses with NSEC3 iterations greater diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 717b1a7540..423ecede42 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -41,7 +41,18 @@ Feature Changes configured in an NSEC3 zones to 150. :gl:`#2642` - Treat DNSSEC responses with NSEC3 iterations greater than 150 as insecure. - [GL #2445] + :gl:`#2445` + +- Implement ``draft-vandijk-dnsop-nsec-ttl``, NSEC(3) TTL values are now set to + the minimum of the SOA MINIMUM value and the SOA TTL. :gl:`#2347` + +- Zones that want to transition from secure to insecure mode without making it + bogus in the process should now first change their ``dnssec-policy`` to + ``insecure`` (as opposed to ``none``). Only after the DNSSEC records have + been removed from the zone (in a timely manner), the ``dnssec-policy`` can + be set to ``none`` (or be removed from the configuration). Setting the + ``dnssec-policy`` to ``insecure`` will cause CDS and CDNSKEY DELETE records + to be published. :gl:`#2645` Bug Fixes ~~~~~~~~~ From 287428e0aa2f426f8b248dd68e4497aea093d19b Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Mon, 26 Apr 2021 14:24:59 +0200 Subject: [PATCH 6/6] Add kasp test policy goes straight to "none" Add a test case when a dnssec-policy is reconfigured to "none", without setting it to "insecure" first. This is unsupported behavior, but we want to make sure the behavior is somewhat expected. The zone should remain signed (but will go bogus once the signatures expire). --- bin/tests/system/kasp/ns6/named.conf.in | 6 ++ bin/tests/system/kasp/ns6/named2.conf.in | 6 ++ bin/tests/system/kasp/ns6/setup.sh | 11 ++++ bin/tests/system/kasp/tests.sh | 74 ++++++++++++++++++++++++ 4 files changed, 97 insertions(+) diff --git a/bin/tests/system/kasp/ns6/named.conf.in b/bin/tests/system/kasp/ns6/named.conf.in index 83d92c0d8a..d1a15cf240 100644 --- a/bin/tests/system/kasp/ns6/named.conf.in +++ b/bin/tests/system/kasp/ns6/named.conf.in @@ -50,6 +50,12 @@ zone "step1.going-insecure-dynamic.kasp" { allow-update { any; }; }; +zone "step1.going-straight-to-none.kasp" { + type master; + file "step1.going-straight-to-none.kasp.db"; + dnssec-policy "default"; +}; + /* These are alorithm rollover test zones. */ zone "step1.algorithm-roll.kasp" { type primary; diff --git a/bin/tests/system/kasp/ns6/named2.conf.in b/bin/tests/system/kasp/ns6/named2.conf.in index 8967c8a44a..38f0e92c96 100644 --- a/bin/tests/system/kasp/ns6/named2.conf.in +++ b/bin/tests/system/kasp/ns6/named2.conf.in @@ -62,6 +62,12 @@ zone "step2.going-insecure-dynamic.kasp" { allow-update { any; }; }; +zone "step1.going-straight-to-none.kasp" { + type master; + file "step1.going-straight-to-none.kasp.db"; + dnssec-policy "none"; +}; + /* * Zones for testing KSK/ZSK algorithm roll. */ diff --git a/bin/tests/system/kasp/ns6/setup.sh b/bin/tests/system/kasp/ns6/setup.sh index 90a15682f6..e8a3c21af1 100644 --- a/bin/tests/system/kasp/ns6/setup.sh +++ b/bin/tests/system/kasp/ns6/setup.sh @@ -78,6 +78,17 @@ do $SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 done +# This zone is going straight to "none" policy. This is undefined behavior. +setup step1.going-straight-to-none.kasp +echo "$zone" >> zones +TactN="now" +csktimes="-P ${TactN} -A ${TactN} -P sync ${TactN}" +CSK=$($KEYGEN -k default $csktimes $zone 2> keygen.out.$zone.1) +$SETTIME -s -g $O -k $O $TactN -z $O $TactN -r $O $TactN -d $O $TactN "$CSK" > settime.out.$zone.1 2>&1 +cat template.db.in "${CSK}.key" > "$infile" +private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >> "$infile" +$SIGNER -S -z -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + # # The zones at algorithm-roll.kasp represent the various steps of a ZSK/KSK # algorithm rollover. diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index a2969fd9f9..be49ac7574 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -3558,6 +3558,44 @@ check_apex check_subdomain dnssec_verify +# +# Zone step1.going-straight-to-none.kasp +# +set_zone "step1.going-straight-to-none.kasp" +set_policy "default" "1" "3600" +set_server "ns6" "10.53.0.6" +# Key properties. +set_keyrole "KEY1" "csk" +set_keylifetime "KEY1" "0" +set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256" +set_keysigning "KEY1" "yes" +set_zonesigning "KEY1" "yes" +# DNSKEY, RRSIG (ksk), RRSIG (zsk) are published. DS needs to wait. +set_keystate "KEY1" "GOAL" "omnipresent" +set_keystate "KEY1" "STATE_DNSKEY" "omnipresent" +set_keystate "KEY1" "STATE_KRRSIG" "omnipresent" +set_keystate "KEY1" "STATE_ZRRSIG" "omnipresent" +set_keystate "KEY1" "STATE_DS" "omnipresent" +# This policy only has one key. +key_clear "KEY2" +key_clear "KEY3" +key_clear "KEY4" + +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" + +# The first key is immediately published and activated. +created=$(key_get KEY1 CREATED) +set_keytime "KEY1" "PUBLISHED" "${created}" +set_keytime "KEY1" "ACTIVE" "${created}" +set_keytime "KEY1" "SYNCPUBLISH" "${created}" +# Key lifetime is unlimited, so not setting RETIRED and REMOVED. +check_keytimes + +check_apex +check_subdomain +dnssec_verify + # Reconfig dnssec-policy (triggering algorithm roll and other dnssec-policy # changes). echo_i "reconfig dnssec-policy to trigger algorithm rollover" @@ -3745,6 +3783,42 @@ check_subdomain # 5m + 2h = 125m = 7500 seconds. check_next_key_event 7500 +# +# Zone: step1.going-straight-to-none.kasp +# +set_zone "step1.going-straight-to-none.kasp" +set_policy "none" "1" "3600" +set_server "ns6" "10.53.0.6" + +# The zone will go bogus after signatures expire, but remains validly signed for now. + +# Key properties. +set_keyrole "KEY1" "csk" +set_keylifetime "KEY1" "0" +set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256" +set_keysigning "KEY1" "yes" +set_zonesigning "KEY1" "yes" +# DNSKEY, RRSIG (ksk), RRSIG (zsk) are published. DS needs to wait. +set_keystate "KEY1" "GOAL" "omnipresent" +set_keystate "KEY1" "STATE_DNSKEY" "omnipresent" +set_keystate "KEY1" "STATE_KRRSIG" "omnipresent" +set_keystate "KEY1" "STATE_ZRRSIG" "omnipresent" +set_keystate "KEY1" "STATE_DS" "omnipresent" +# This policy only has one key. +key_clear "KEY2" +key_clear "KEY3" +key_clear "KEY4" + +# Various signing policy checks. +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" +check_apex +check_subdomain +dnssec_verify + +echo_i "status: $status" +exit $status + # # Testing KSK/ZSK algorithm rollover. #