From 644f0d958a1ba0dc54fca740e9d113900d6882b6 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 8 Apr 2020 13:08:20 +0200 Subject: [PATCH 1/3] dnssec-policy: to sign inline or not When dnssec-policy was introduced, it implicitly set inline-signing. But DNSSEC maintenance required either inline-signing to be enabled, or a dynamic zone. In other words, not in all cases you want to DNSSEC maintain your zone with inline-signing. Change the behavior and determine whether inline-signing is required: if the zone is dynamic, don't use inline-signing, otherwise implicitly set it. You can also explicitly set inline-signing to yes with dnssec-policy, the restriction that both inline-signing and dnssec-policy cannot be set at the same time is now lifted. However, 'inline-signing no;' on a non-dynamic zone with a dnssec-policy is not possible. --- bin/named/server.c | 80 ++++- .../kasp-and-other-dnssec-options.conf | 3 +- bin/tests/system/checkconf/tests.sh | 2 +- bin/tests/system/kasp/ns3/named.conf.in | 25 ++ bin/tests/system/kasp/ns3/setup.sh | 1 + bin/tests/system/kasp/tests.sh | 316 +++++++++--------- lib/bind9/check.c | 27 +- lib/dns/zone.c | 18 +- 8 files changed, 279 insertions(+), 193 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index 3a927b01bd..3c4f8cf9a6 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -6053,6 +6053,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, dns_zone_t *raw = NULL; /* New or reused raw zone */ dns_zone_t *dupzone = NULL; const cfg_obj_t *options = NULL; + const cfg_obj_t *voptions = NULL; const cfg_obj_t *zoptions = NULL; const cfg_obj_t *typeobj = NULL; const cfg_obj_t *forwarders = NULL; @@ -6071,11 +6072,16 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, const char *ztypestr; dns_rpz_num_t rpz_num; bool zone_is_catz = false; + bool zone_maybe_inline = false; + bool inline_signing = false; options = NULL; (void)cfg_map_get(config, "options", &options); zoptions = cfg_tuple_get(zconfig, "options"); + if (vconfig != NULL) { + voptions = cfg_tuple_get(vconfig, "options"); + } /* * Get the zone origin as a dns_name_t. @@ -6408,19 +6414,71 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, */ dns_zone_setadded(zone, added); + /* + * Determine if we need to set up inline signing. + */ + zone_maybe_inline = ((strcasecmp(ztypestr, "primary") == 0 || + strcasecmp(ztypestr, "master") == 0 || + strcasecmp(ztypestr, "secondary") == 0 || + strcasecmp(ztypestr, "slave") == 0)); + signing = NULL; - if ((strcasecmp(ztypestr, "primary") == 0 || - strcasecmp(ztypestr, "master") == 0 || - strcasecmp(ztypestr, "secondary") == 0 || - strcasecmp(ztypestr, "slave") == 0) && - ((cfg_map_get(zoptions, "inline-signing", &signing) == - ISC_R_SUCCESS && - cfg_obj_asboolean(signing)) || - (cfg_map_get(zoptions, "dnssec-policy", &signing) == - ISC_R_SUCCESS && - signing != NULL && - strcmp(cfg_obj_asstring(signing), "none") != 0))) + inline_signing = (zone_maybe_inline && + ((cfg_map_get(zoptions, "inline-signing", &signing) == + ISC_R_SUCCESS && + cfg_obj_asboolean(signing)))); + + /* + * If inline-signing is not set, perhaps implictly through a + * dnssec-policy. Since automated DNSSEC maintenance requires + * a dynamic zone, or inline-siging to be enabled, check if + * the zone with dnssec-policy allows updates. If not, enable + * inline-signing. + */ + signing = NULL; + if (zone_maybe_inline && !inline_signing && + cfg_map_get(zoptions, "dnssec-policy", &signing) == ISC_R_SUCCESS && + signing != NULL && strcmp(cfg_obj_asstring(signing), "none") != 0) { + isc_result_t res; + bool zone_is_dynamic = false; + const cfg_obj_t *au = NULL; + const cfg_obj_t *up = NULL; + + if (cfg_map_get(zoptions, "update-policy", &up) == + ISC_R_SUCCESS) { + zone_is_dynamic = true; + } else { + res = cfg_map_get(zoptions, "allow-update", &au); + if (res != ISC_R_SUCCESS && voptions != NULL) { + res = cfg_map_get(voptions, "allow-update", + &au); + } + if (res != ISC_R_SUCCESS && options != NULL) { + res = cfg_map_get(options, "allow-update", &au); + } + if (res == ISC_R_SUCCESS) { + dns_acl_t *acl = NULL; + cfg_aclconfctx_t *actx = NULL; + res = cfg_acl_fromconfig(au, config, + named_g_lctx, actx, + mctx, 0, &acl); + if (res == ISC_R_SUCCESS && acl != NULL && + !dns_acl_isnone(acl)) { + zone_is_dynamic = true; + } + if (acl != NULL) { + dns_acl_detach(&acl); + } + } + } + + if (!zone_is_dynamic) { + inline_signing = true; + } + } + + if (inline_signing) { dns_zone_getraw(zone, &raw); if (raw == NULL) { CHECK(dns_zone_create(&raw, mctx)); diff --git a/bin/tests/system/checkconf/kasp-and-other-dnssec-options.conf b/bin/tests/system/checkconf/kasp-and-other-dnssec-options.conf index d7c1bf8123..a3eae44f73 100644 --- a/bin/tests/system/checkconf/kasp-and-other-dnssec-options.conf +++ b/bin/tests/system/checkconf/kasp-and-other-dnssec-options.conf @@ -20,9 +20,8 @@ zone "nsec3.net" { dnssec-dnskey-kskonly yes; dnssec-secure-to-insecure yes; dnssec-update-mode maintain; - inline-signing yes; + inline-signing no; sig-validity-interval 3600; update-check-ksk yes; - allow-update { any; }; }; diff --git a/bin/tests/system/checkconf/tests.sh b/bin/tests/system/checkconf/tests.sh index ba8b314cb6..270975bfe6 100644 --- a/bin/tests/system/checkconf/tests.sh +++ b/bin/tests/system/checkconf/tests.sh @@ -478,12 +478,12 @@ n=`expr $n + 1` echo_i "checking named-checkconf kasp errors ($n)" ret=0 $CHECKCONF kasp-and-other-dnssec-options.conf > checkconf.out$n 2>&1 && ret=1 +grep "'inline-signing;' cannot be set to 'no' if dnssec-policy is also set on a non-dynamic DNS zone" < checkconf.out$n > /dev/null || ret=1 grep "'auto-dnssec maintain;' cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 grep "dnskey-sig-validity: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 grep "dnssec-dnskey-kskonly: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 grep "dnssec-secure-to-insecure: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 grep "dnssec-update-mode: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 -grep "inline-signing: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 grep "sig-validity-interval: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 grep "update-check-ksk: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi diff --git a/bin/tests/system/kasp/ns3/named.conf.in b/bin/tests/system/kasp/ns3/named.conf.in index da7c04734e..8b71cab1f0 100644 --- a/bin/tests/system/kasp/ns3/named.conf.in +++ b/bin/tests/system/kasp/ns3/named.conf.in @@ -87,6 +87,31 @@ zone "secondary.kasp" { dnssec-policy "rsasha1"; }; +/* A dynamic zone with dnssec-policy. */ +zone "dynamic.kasp" { + type master; + file "dynamic.kasp.db"; + dnssec-policy "default"; + allow-update { any; }; +}; + +/* A dynamic inline-signed zone with dnssec-policy. */ +zone "dynamic-inline-signing.kasp" { + type master; + file "dynamic-inline-signing.kasp.db"; + dnssec-policy "default"; + allow-update { any; }; + inline-signing yes; +}; + +/* An inline-signed zone with dnssec-policy. */ +zone "inline-signing.kasp" { + type master; + file "inline-signing.kasp.db"; + dnssec-policy "default"; + inline-signing yes; +}; + /* * A configured dnssec-policy but some keys already created. */ diff --git a/bin/tests/system/kasp/ns3/setup.sh b/bin/tests/system/kasp/ns3/setup.sh index 5be0f0b0c0..3549933642 100644 --- a/bin/tests/system/kasp/ns3/setup.sh +++ b/bin/tests/system/kasp/ns3/setup.sh @@ -44,6 +44,7 @@ U="UNRETENTIVE" # for zn in default rsasha1 dnssec-keygen some-keys legacy-keys pregenerated \ rumoured rsasha1-nsec3 rsasha256 rsasha512 ecdsa256 ecdsa384 \ + dynamic dynamic-inline-signing inline-signing \ inherit unlimited do setup "${zn}.kasp" diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index da770695cb..fee431d3ee 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -713,164 +713,6 @@ status=$((status+ret)) next_key_event_threshold=$((next_key_event_threshold+i)) -# -# Zone: default.kasp. -# - -# Check the zone with default kasp policy has loaded and is signed. -set_zone "default.kasp" -set_policy "default" "1" "3600" -set_server "ns3" "10.53.0.3" -# Key properties. -set_keyrole "KEY1" "csk" -set_keylifetime "KEY1" "0" -set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256" -set_keysigning "KEY1" "yes" -set_zonesigning "KEY1" "yes" - -# The first key is immediately published and activated. -set_keytime "KEY1" "PUBLISHED" "yes" -set_keytime "KEY1" "ACTIVE" "yes" -# DNSKEY, RRSIG (ksk), RRSIG (zsk) are published. DS needs to wait. -set_keystate "KEY1" "GOAL" "omnipresent" -set_keystate "KEY1" "STATE_DNSKEY" "rumoured" -set_keystate "KEY1" "STATE_KRRSIG" "rumoured" -set_keystate "KEY1" "STATE_ZRRSIG" "rumoured" -set_keystate "KEY1" "STATE_DS" "hidden" - -n=$((n+1)) -echo_i "check key is created for zone ${ZONE} ($n)" -ret=0 -ids=$(get_keyids "$DIR" "$ZONE") -for id in $ids; do - check_key "KEY1" "$id" -done -test "$ret" -eq 0 || echo_i "failed" -status=$((status+ret)) - -# Verify signed zone. -dnssec_verify "$ZONE" - -# Test DNSKEY query. -qtype="DNSKEY" -n=$((n+1)) -echo_i "check ${qtype} rrset is signed correctly for zone ${ZONE} ($n)" -ret=0 -dig_with_opts "$ZONE" "@${SERVER}" $qtype > "dig.out.$DIR.test$n" || log_error "dig ${ZONE} ${qtype} failed" -grep "status: NOERROR" "dig.out.$DIR.test$n" > /dev/null || log_error "mismatch status in DNS response" -grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${qtype}.*257.*.3.*$(key_get KEY1 ALG_NUM)" "dig.out.$DIR.test$n" > /dev/null || log_error "missing ${qtype} record in response" -lines=$(get_keys_which_signed $qtype "dig.out.$DIR.test$n" | wc -l) -test "$lines" -eq 1 || log_error "bad number ($lines) of RRSIG records in DNS response" -get_keys_which_signed $qtype "dig.out.$DIR.test$n" | grep "^${KEY_ID}$" > /dev/null || log_error "${qtype} RRset not signed with key ${KEY_ID}" -test "$ret" -eq 0 || echo_i "failed" -status=$((status+ret)) - -# Test SOA query. -qtype="SOA" -n=$((n+1)) -echo_i "check ${qtype} rrset is signed correctly for zone ${ZONE} ($n)" -ret=0 -dig_with_opts "$ZONE" "@${SERVER}" $qtype > "dig.out.$DIR.test$n" || log_error "dig ${ZONE} ${qtype} failed" -grep "status: NOERROR" "dig.out.$DIR.test$n" > /dev/null || log_error "mismatch status in DNS response" -grep "${ZONE}\..*${DEFAULT_TTL}.*IN.*${qtype}.*mname1\..*\." "dig.out.$DIR.test$n" > /dev/null || log_error "missing ${qtype} record in response" -lines=$(get_keys_which_signed $qtype "dig.out.$DIR.test$n" | wc -l) -test "$lines" -eq 1 || log_error "bad number ($lines) of RRSIG records in DNS response" -get_keys_which_signed $qtype "dig.out.$DIR.test$n" | grep "^${KEY_ID}$" > /dev/null || log_error "${qtype} RRset not signed with key ${KEY_ID}" -test "$ret" -eq 0 || echo_i "failed" -status=$((status+ret)) - -# Update zone. -n=$((n+1)) -echo_i "check that we can update unsigned zone file and new record gets signed for zone ${ZONE} ($n)" -ret=0 -cp "${DIR}/template2.db.in" "${DIR}/${ZONE}.db" -rndccmd 10.53.0.3 reload "$ZONE" > /dev/null || log_error "rndc reload zone ${ZONE} failed" -_log=0 -i=0 -while [ $i -lt 5 ] -do - ret=0 - - dig_with_opts "a.${ZONE}" "@${SERVER}" A > "dig.out.$DIR.test$n.a" || log_error "dig a.${ZONE} A failed" - grep "status: NOERROR" "dig.out.$DIR.test$n.a" > /dev/null || log_error "mismatch status in DNS response" - grep "a.${ZONE}\..*${DEFAULT_TTL}.*IN.*A.*10\.0\.0\.11" "dig.out.$DIR.test$n.a" > /dev/null || log_error "missing a.${ZONE} A record in response" - lines=$(get_keys_which_signed A "dig.out.$DIR.test$n.a" | wc -l) - test "$lines" -eq 1 || log_error "bad number ($lines) of RRSIG records in DNS response" - get_keys_which_signed A "dig.out.$DIR.test$n.a" | grep "^${KEY_ID}$" > /dev/null || log_error "A RRset not signed with key ${KEY_ID}" - - dig_with_opts "d.${ZONE}" "@${SERVER}" A > "dig.out.$DIR.test$n".d || log_error "dig d.${ZONE} A failed" - grep "status: NOERROR" "dig.out.$DIR.test$n".d > /dev/null || log_error "mismatch status in DNS response" - grep "d.${ZONE}\..*${DEFAULT_TTL}.*IN.*A.*10\.0\.0\.4" "dig.out.$DIR.test$n".d > /dev/null || log_error "missing d.${ZONE} A record in response" - lines=$(get_keys_which_signed A "dig.out.$DIR.test$n".d | wc -l) - test "$lines" -eq 1 || log_error "bad number ($lines) of RRSIG records in DNS response" - get_keys_which_signed A "dig.out.$DIR.test$n".d | grep "^${KEY_ID}$" > /dev/null || log_error "A RRset not signed with key ${KEY_ID}" - - i=$((i+1)) - if [ $ret = 0 ]; then break; fi - echo_i "waiting ... ($i)" - sleep 1 -done -_log=1 -test "$ret" -eq 0 || echo_i "failed" -status=$((status+ret)) - -# -# Zone: rsasha1.kasp. -# -set_zone "rsasha1.kasp" -set_policy "rsasha1" "3" "1234" -set_server "ns3" "10.53.0.3" -# Key properties. -key_clear "KEY1" -set_keyrole "KEY1" "ksk" -set_keylifetime "KEY1" "315360000" -set_keyalgorithm "KEY1" "5" "RSASHA1" "2048" -set_keysigning "KEY1" "yes" -set_zonesigning "KEY1" "no" - -key_clear "KEY2" -set_keyrole "KEY2" "zsk" -set_keylifetime "KEY2" "157680000" -set_keyalgorithm "KEY2" "5" "RSASHA1" "2048" -set_keysigning "KEY2" "no" -set_zonesigning "KEY2" "yes" - -key_clear "KEY3" -set_keyrole "KEY3" "zsk" -set_keylifetime "KEY3" "31536000" -set_keyalgorithm "KEY3" "5" "RSASHA1" "2000" -set_keysigning "KEY3" "no" -set_zonesigning "KEY3" "yes" -# The first keys are immediately published and activated. -# Because lifetime > 0, retired timing is also set. -set_keytime "KEY1" "PUBLISHED" "yes" -set_keytime "KEY1" "ACTIVE" "yes" -set_keytime "KEY1" "RETIRED" "yes" - -set_keytime "KEY2" "PUBLISHED" "yes" -set_keytime "KEY2" "ACTIVE" "yes" -set_keytime "KEY2" "RETIRED" "yes" - -set_keytime "KEY3" "PUBLISHED" "yes" -set_keytime "KEY3" "ACTIVE" "yes" -set_keytime "KEY3" "RETIRED" "yes" -# KSK: DNSKEY, RRSIG (ksk) published. DS needs to wait. -# ZSK: DNSKEY, RRSIG (zsk) published. -set_keystate "KEY1" "GOAL" "omnipresent" -set_keystate "KEY1" "STATE_DNSKEY" "rumoured" -set_keystate "KEY1" "STATE_KRRSIG" "rumoured" -set_keystate "KEY1" "STATE_DS" "hidden" - -set_keystate "KEY2" "GOAL" "omnipresent" -set_keystate "KEY2" "STATE_DNSKEY" "rumoured" -set_keystate "KEY2" "STATE_ZRRSIG" "rumoured" - -set_keystate "KEY3" "GOAL" "omnipresent" -set_keystate "KEY3" "STATE_DNSKEY" "rumoured" -set_keystate "KEY3" "STATE_ZRRSIG" "rumoured" -# Three keys only. -key_clear "KEY4" - # Check keys for a configured zone. This verifies: # 1. The right number of keys exist in the key pool ($1). # 2. The right number of keys is active. Checks KEY1, KEY2, KEY3, and KEY4. @@ -1174,6 +1016,164 @@ check_subdomain() { status=$((status+ret)) } +# +# Zone: default.kasp. +# + +# Check the zone with default kasp policy has loaded and is signed. +set_zone "default.kasp" +set_policy "default" "1" "3600" +set_server "ns3" "10.53.0.3" +# Key properties. +set_keyrole "KEY1" "csk" +set_keylifetime "KEY1" "0" +set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256" +set_keysigning "KEY1" "yes" +set_zonesigning "KEY1" "yes" + +# The first key is immediately published and activated. +set_keytime "KEY1" "PUBLISHED" "yes" +set_keytime "KEY1" "ACTIVE" "yes" +# DNSKEY, RRSIG (ksk), RRSIG (zsk) are published. DS needs to wait. +set_keystate "KEY1" "GOAL" "omnipresent" +set_keystate "KEY1" "STATE_DNSKEY" "rumoured" +set_keystate "KEY1" "STATE_KRRSIG" "rumoured" +set_keystate "KEY1" "STATE_ZRRSIG" "rumoured" +set_keystate "KEY1" "STATE_DS" "hidden" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Update zone. +n=$((n+1)) +echo_i "check that we can update unsigned zone file and new record gets signed for zone ${ZONE} ($n)" +ret=0 +cp "${DIR}/template2.db.in" "${DIR}/${ZONE}.db" +rndccmd 10.53.0.3 reload "$ZONE" > /dev/null || log_error "rndc reload zone ${ZONE} failed" +_log=0 +i=0 +while [ $i -lt 5 ] +do + ret=0 + + dig_with_opts "a.${ZONE}" "@${SERVER}" A > "dig.out.$DIR.test$n.a" || log_error "dig a.${ZONE} A failed" + grep "status: NOERROR" "dig.out.$DIR.test$n.a" > /dev/null || log_error "mismatch status in DNS response" + grep "a.${ZONE}\..*${DEFAULT_TTL}.*IN.*A.*10\.0\.0\.11" "dig.out.$DIR.test$n.a" > /dev/null || log_error "missing a.${ZONE} A record in response" + lines=$(get_keys_which_signed A "dig.out.$DIR.test$n.a" | wc -l) + test "$lines" -eq 1 || log_error "bad number ($lines) of RRSIG records in DNS response" + get_keys_which_signed A "dig.out.$DIR.test$n.a" | grep "^${KEY_ID}$" > /dev/null || log_error "A RRset not signed with key ${KEY_ID}" + + dig_with_opts "d.${ZONE}" "@${SERVER}" A > "dig.out.$DIR.test$n".d || log_error "dig d.${ZONE} A failed" + grep "status: NOERROR" "dig.out.$DIR.test$n".d > /dev/null || log_error "mismatch status in DNS response" + grep "d.${ZONE}\..*${DEFAULT_TTL}.*IN.*A.*10\.0\.0\.4" "dig.out.$DIR.test$n".d > /dev/null || log_error "missing d.${ZONE} A record in response" + lines=$(get_keys_which_signed A "dig.out.$DIR.test$n".d | wc -l) + test "$lines" -eq 1 || log_error "bad number ($lines) of RRSIG records in DNS response" + get_keys_which_signed A "dig.out.$DIR.test$n".d | grep "^${KEY_ID}$" > /dev/null || log_error "A RRset not signed with key ${KEY_ID}" + + i=$((i+1)) + if [ $ret = 0 ]; then break; fi + echo_i "waiting ... ($i)" + sleep 1 +done +_log=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) + +# +# Zone: dynamic.kasp +# +set_zone "dynamic.kasp" +set_policy "default" "1" "3600" +set_server "ns3" "10.53.0.3" +# Key properties, timings and states same as above. +check_keys +check_apex +check_subdomain +dnssec_verify + +# +# Zone: dynamic-inline-signing.kasp +# +set_zone "dynamic-inline-signing.kasp" +set_policy "default" "1" "3600" +set_server "ns3" "10.53.0.3" +# Key properties, timings and states same as above. +check_keys +check_apex +check_subdomain +dnssec_verify + +# +# Zone: inline-signing.kasp +# +set_zone "inline-signing.kasp" +set_policy "default" "1" "3600" +set_server "ns3" "10.53.0.3" +# Key properties, timings and states same as above. +check_keys +check_apex +check_subdomain +dnssec_verify + +# +# Zone: rsasha1.kasp. +# +set_zone "rsasha1.kasp" +set_policy "rsasha1" "3" "1234" +set_server "ns3" "10.53.0.3" +# Key properties. +key_clear "KEY1" +set_keyrole "KEY1" "ksk" +set_keylifetime "KEY1" "315360000" +set_keyalgorithm "KEY1" "5" "RSASHA1" "2048" +set_keysigning "KEY1" "yes" +set_zonesigning "KEY1" "no" + +key_clear "KEY2" +set_keyrole "KEY2" "zsk" +set_keylifetime "KEY2" "157680000" +set_keyalgorithm "KEY2" "5" "RSASHA1" "2048" +set_keysigning "KEY2" "no" +set_zonesigning "KEY2" "yes" + +key_clear "KEY3" +set_keyrole "KEY3" "zsk" +set_keylifetime "KEY3" "31536000" +set_keyalgorithm "KEY3" "5" "RSASHA1" "2000" +set_keysigning "KEY3" "no" +set_zonesigning "KEY3" "yes" +# The first keys are immediately published and activated. +# Because lifetime > 0, retired timing is also set. +set_keytime "KEY1" "PUBLISHED" "yes" +set_keytime "KEY1" "ACTIVE" "yes" +set_keytime "KEY1" "RETIRED" "yes" + +set_keytime "KEY2" "PUBLISHED" "yes" +set_keytime "KEY2" "ACTIVE" "yes" +set_keytime "KEY2" "RETIRED" "yes" + +set_keytime "KEY3" "PUBLISHED" "yes" +set_keytime "KEY3" "ACTIVE" "yes" +set_keytime "KEY3" "RETIRED" "yes" +# KSK: DNSKEY, RRSIG (ksk) published. DS needs to wait. +# ZSK: DNSKEY, RRSIG (zsk) published. +set_keystate "KEY1" "GOAL" "omnipresent" +set_keystate "KEY1" "STATE_DNSKEY" "rumoured" +set_keystate "KEY1" "STATE_KRRSIG" "rumoured" +set_keystate "KEY1" "STATE_DS" "hidden" + +set_keystate "KEY2" "GOAL" "omnipresent" +set_keystate "KEY2" "STATE_DNSKEY" "rumoured" +set_keystate "KEY2" "STATE_ZRRSIG" "rumoured" + +set_keystate "KEY3" "GOAL" "omnipresent" +set_keystate "KEY3" "STATE_DNSKEY" "rumoured" +set_keystate "KEY3" "STATE_ZRRSIG" "rumoured" +# Three keys only. +key_clear "KEY4" + check_keys check_apex check_subdomain diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 3668bf90f3..c1f8503b00 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -2275,9 +2275,10 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, for (element = cfg_list_first(kasps); element != NULL; element = cfg_list_next(element)) { - const char *kn = cfg_obj_asstring(cfg_tuple_get( - cfg_listelt_value(element), "name")); - if (strcmp(kaspname, kn) == 0) { + const cfg_obj_t *kobj = cfg_tuple_get( + cfg_listelt_value(element), "name"); + if (strcmp(kaspname, cfg_obj_asstring(kobj)) == + 0) { has_dnssecpolicy = true; } } @@ -2495,13 +2496,14 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, res1 = cfg_map_get(zoptions, "inline-signing", &obj); if (res1 == ISC_R_SUCCESS) { signing = cfg_obj_asboolean(obj); - } - - if (signing && has_dnssecpolicy) { - cfg_obj_log(obj, logctx, ISC_LOG_ERROR, - "inline-signing: cannot be configured if " - "dnssec-policy is also set"); - result = ISC_R_FAILURE; + if (has_dnssecpolicy && !ddns && !signing) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'inline-signing;' cannot be set " + "to 'no' " + "if dnssec-policy is also set on a " + "non-dynamic DNS zone"); + result = ISC_R_FAILURE; + } } obj = NULL; @@ -2511,7 +2513,7 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, arg = cfg_obj_asstring(obj); } if (strcasecmp(arg, "off") != 0) { - if (!ddns && !signing) { + if (!ddns && !signing && strcasecmp(arg, "off") != 0) { cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "'auto-dnssec %s;' requires%s " "inline-signing to be configured " @@ -2524,7 +2526,8 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, : ""); result = ISC_R_FAILURE; } - if (has_dnssecpolicy) { + + if (strcasecmp(arg, "off") != 0 && has_dnssecpolicy) { cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "'auto-dnssec %s;' cannot be " "configured if dnssec-policy is " diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 4ba75ef52d..05121323af 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -1809,6 +1809,11 @@ dns_zone_isdynamic(dns_zone_t *zone, bool ignore_freeze) { return (true); } + /* Kasp zones are always dynamic. */ + if (dns_zone_getkasp(zone) != NULL) { + return (true); + } + /* If !ignore_freeze, we need check whether updates are disabled. */ if (zone->type == dns_zone_master && (!zone->update_disabled || ignore_freeze) && @@ -2054,8 +2059,7 @@ zone_load(dns_zone_t *zone, unsigned int flags, bool locked) { goto cleanup; } - is_dynamic = dns_zone_isdynamic(zone, false) || - dns_zone_getkasp(zone) != NULL; + is_dynamic = dns_zone_isdynamic(zone, false); if (zone->db != NULL && is_dynamic) { /* * This is a slave, stub, dynamically updated, or kasp enabled @@ -3909,7 +3913,6 @@ check_nsec3param(dns_zone_t *zone, dns_db_t *db) { bool dynamic = (zone->type == dns_zone_master) ? dns_zone_isdynamic(zone, false) : false; - dynamic = dynamic || dns_zone_getkasp(zone); dns_rdataset_init(&rdataset); result = dns_db_findnode(db, &zone->origin, false, &node); @@ -4842,8 +4845,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, * journal file if it isn't, as we wouldn't be able to apply * updates otherwise. */ - is_dynamic = dns_zone_isdynamic(zone, true) || - dns_zone_getkasp(zone) != NULL; + is_dynamic = dns_zone_isdynamic(zone, true); if (zone->journal != NULL && is_dynamic && !DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS)) { @@ -5140,8 +5142,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, resume_addnsec3chain(zone); } - is_dynamic = dns_zone_isdynamic(zone, false) || - dns_zone_getkasp(zone) != NULL; + is_dynamic = dns_zone_isdynamic(zone, false); if (zone->type == dns_zone_master && !DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_NORESIGN) && is_dynamic && dns_db_issecure(db)) @@ -20928,8 +20929,7 @@ dns_zone_setserial(dns_zone_t *zone, uint32_t serial) { LOCK_ZONE(zone); if (!inline_secure(zone)) { - if (!dns_zone_isdynamic(zone, true) && - dns_zone_getkasp(zone) == NULL) { + if (!dns_zone_isdynamic(zone, true)) { result = DNS_R_NOTDYNAMIC; goto failure; } From e3aa12fc0a6cb8a66f6935f7f0fe34b0467e81b0 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 9 Apr 2020 08:46:37 +0200 Subject: [PATCH 2/3] Add kasp tests dyn update zone Add two tests that checks that dynamic zones can be updated and will be signed appropriately. One zone covers an update with freeze/thaw, the other covers an update through nsupdate. --- bin/tests/system/kasp/tests.sh | 67 +++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index fee431d3ee..69fd81ea27 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -1048,36 +1048,28 @@ dnssec_verify # Update zone. n=$((n+1)) -echo_i "check that we can update unsigned zone file and new record gets signed for zone ${ZONE} ($n)" +echo_i "modify unsigned zone file and check that new record is signed for zone ${ZONE} ($n)" ret=0 cp "${DIR}/template2.db.in" "${DIR}/${ZONE}.db" rndccmd 10.53.0.3 reload "$ZONE" > /dev/null || log_error "rndc reload zone ${ZONE} failed" -_log=0 -i=0 -while [ $i -lt 5 ] -do - ret=0 - dig_with_opts "a.${ZONE}" "@${SERVER}" A > "dig.out.$DIR.test$n.a" || log_error "dig a.${ZONE} A failed" - grep "status: NOERROR" "dig.out.$DIR.test$n.a" > /dev/null || log_error "mismatch status in DNS response" - grep "a.${ZONE}\..*${DEFAULT_TTL}.*IN.*A.*10\.0\.0\.11" "dig.out.$DIR.test$n.a" > /dev/null || log_error "missing a.${ZONE} A record in response" +update_is_signed() { + dig_with_opts "a.${ZONE}" "@${SERVER}" A > "dig.out.$DIR.test$n.a" || return 1 + grep "status: NOERROR" "dig.out.$DIR.test$n.a" > /dev/null || return 1 + grep "a.${ZONE}\..*${DEFAULT_TTL}.*IN.*A.*10\.0\.0\.11" "dig.out.$DIR.test$n.a" > /dev/null || return 1 lines=$(get_keys_which_signed A "dig.out.$DIR.test$n.a" | wc -l) - test "$lines" -eq 1 || log_error "bad number ($lines) of RRSIG records in DNS response" - get_keys_which_signed A "dig.out.$DIR.test$n.a" | grep "^${KEY_ID}$" > /dev/null || log_error "A RRset not signed with key ${KEY_ID}" + test "$lines" -eq 1 || return 1 + get_keys_which_signed A "dig.out.$DIR.test$n.a" | grep "^${KEY_ID}$" > /dev/null || return 1 - dig_with_opts "d.${ZONE}" "@${SERVER}" A > "dig.out.$DIR.test$n".d || log_error "dig d.${ZONE} A failed" - grep "status: NOERROR" "dig.out.$DIR.test$n".d > /dev/null || log_error "mismatch status in DNS response" - grep "d.${ZONE}\..*${DEFAULT_TTL}.*IN.*A.*10\.0\.0\.4" "dig.out.$DIR.test$n".d > /dev/null || log_error "missing d.${ZONE} A record in response" + dig_with_opts "d.${ZONE}" "@${SERVER}" A > "dig.out.$DIR.test$n".d || return 1 + grep "status: NOERROR" "dig.out.$DIR.test$n".d > /dev/null || return 1 + grep "d.${ZONE}\..*${DEFAULT_TTL}.*IN.*A.*10\.0\.0\.4" "dig.out.$DIR.test$n".d > /dev/null || return 1 lines=$(get_keys_which_signed A "dig.out.$DIR.test$n".d | wc -l) - test "$lines" -eq 1 || log_error "bad number ($lines) of RRSIG records in DNS response" - get_keys_which_signed A "dig.out.$DIR.test$n".d | grep "^${KEY_ID}$" > /dev/null || log_error "A RRset not signed with key ${KEY_ID}" + test "$lines" -eq 1 || return 1 + get_keys_which_signed A "dig.out.$DIR.test$n".d | grep "^${KEY_ID}$" > /dev/null || return 1 +} - i=$((i+1)) - if [ $ret = 0 ]; then break; fi - echo_i "waiting ... ($i)" - sleep 1 -done -_log=1 +retry_quiet 10 update_is_signed || ret=1 test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) @@ -1093,6 +1085,24 @@ check_apex check_subdomain dnssec_verify +# Update zone with nsupdate. +n=$((n+1)) +echo_i "nsupdate zone and check that new record is signed for zone ${ZONE} ($n)" +ret=0 +( +echo zone ${ZONE} +echo server 10.53.0.3 "$PORT" +echo update del "a.${ZONE}" 300 A 10.0.0.1 +echo update add "a.${ZONE}" 300 A 10.0.0.11 +echo update add "d.${ZONE}" 300 A 10.0.0.4 +echo send +) | $NSUPDATE + +retry_quiet 10 update_is_signed || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) + + # # Zone: dynamic-inline-signing.kasp # @@ -1105,6 +1115,19 @@ check_apex check_subdomain dnssec_verify +# Update zone with freeze/thaw. +n=$((n+1)) +echo_i "modify unsigned zone file and check that new record is signed for zone ${ZONE} ($n)" +ret=0 +rndccmd 10.53.0.3 freeze "$ZONE" > /dev/null || log_error "rndc freeze zone ${ZONE} failed" +sleep 1 +cp "${DIR}/template2.db.in" "${DIR}/${ZONE}.db" +rndccmd 10.53.0.3 thaw "$ZONE" > /dev/null || log_error "rndc thaw zone ${ZONE} failed" + +retry_quiet 10 update_is_signed || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) + # # Zone: inline-signing.kasp # From acae6cf438d48121b88d2eabab6a68c24ae4ab09 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 16 Apr 2020 14:24:25 +0200 Subject: [PATCH 3/3] Update changes --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index 24d1df47d7..e58cad4c49 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +5384. [bug] With dnssec-policy, inline-signing was implicitly set + to yes. Change and only set inline-signing to yes + if the zone is not dynamic. [GL #1709] + 5383. [func] Add a quota attach function with a callback and clean up the isc_quota API. [GL !3280]