From: Matthijs Mekking Date: Fri, 21 Aug 2020 13:38:00 +0000 (+0200) Subject: Add rndc dnssec -rollover command X-Git-Tag: v9.17.6~15^2~4 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=e826facadb71388587d4efe6d21797bdfd671226;p=thirdparty%2Fbind9.git Add rndc dnssec -rollover command This command is similar in arguments as -checkds so refactor the 'named_server_dnssec' function accordingly. The only difference are that: - It does not take a "publish" or "withdrawn" argument. - It requires the key id to be set (add a check to make sure). Add tests that will trigger rollover immediately and one that schedules a test in the future. --- diff --git a/CHANGES b/CHANGES index 0227ef6582e..e355fb9b829 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +5515. [func] Add 'rndc dnssec -rollover' command to trigger a + manual rollover for a specific key. [GL #1749] + 5514. [bug] Fix KASP expected key size for Ed25519 and Ed448. [GL #2171] diff --git a/bin/named/server.c b/bin/named/server.c index 3a9e9ed58ec..c2c12693718 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -14595,10 +14595,14 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, dns_kasp_t *kasp = NULL; dns_dnsseckeylist_t keys; dns_dnsseckey_t *key; - char *ptr; + char *ptr, *zonetext = NULL; const char *msg = NULL; /* variables for -checkds */ - bool checkds = false, dspublish = false, use_keyid = false; + bool checkds = false, dspublish = false; + /* variables for -rollover */ + bool rollover = false; + /* variables for -key */ + bool use_keyid = false; dns_keytag_t keyid = 0; uint8_t algorithm = 0; /* variables for -status */ @@ -14629,9 +14633,15 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, if (strcasecmp(ptr, "-status") == 0) { status = true; + } else if (strcasecmp(ptr, "-rollover") == 0) { + rollover = true; } else if (strcasecmp(ptr, "-checkds") == 0) { checkds = true; + } else { + CHECK(DNS_R_SYNTAX); + } + if (rollover || checkds) { /* Check for options */ for (;;) { ptr = next_token(lex, text); @@ -14678,7 +14688,7 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, } else if (ptr[0] == '-') { msg = "Unknown option"; CHECK(DNS_R_SYNTAX); - } else { + } else if (checkds) { /* * No arguments provided, so we must be * parsing "published|withdrawn". @@ -14688,20 +14698,29 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, } else if (strcasecmp(ptr, "withdrawn") != 0) { CHECK(DNS_R_SYNTAX); } + } else if (rollover) { + /* + * No arguments provided, so we must be + * parsing the zone. + */ + zonetext = ptr; } break; } + if (rollover && !use_keyid) { + msg = "Key id is required when scheduling rollover"; + CHECK(DNS_R_SYNTAX); + } + if (algorithm > 0 && !use_keyid) { msg = "Key id is required when setting algorithm"; CHECK(DNS_R_SYNTAX); } - } else { - CHECK(DNS_R_SYNTAX); } /* Get zone. */ - CHECK(zone_from_args(server, lex, NULL, &zone, NULL, text, false)); + CHECK(zone_from_args(server, lex, zonetext, &zone, NULL, text, false)); if (zone == NULL) { msg = "Zone not found"; CHECK(ISC_R_UNEXPECTEDEND); @@ -14750,11 +14769,11 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, LOCK(&kasp->lock); if (use_keyid) { - result = dns_keymgr_checkds_id(kasp, &keys, dir, when, - dspublish, keyid, + result = dns_keymgr_checkds_id(kasp, &keys, dir, now, + when, dspublish, keyid, (unsigned int)algorithm); } else { - result = dns_keymgr_checkds(kasp, &keys, dir, when, + result = dns_keymgr_checkds(kasp, &keys, dir, now, when, dspublish); } UNLOCK(&kasp->lock); @@ -14789,6 +14808,48 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, CHECK(putstr(text, "Error executing checkds command")); break; } + } else if (rollover) { + /* + * Manually rollover a key. + */ + char whenbuf[80]; + isc_time_set(&timewhen, when, 0); + isc_time_formattimestamp(&timewhen, whenbuf, sizeof(whenbuf)); + + LOCK(&kasp->lock); + result = dns_keymgr_rollover(kasp, &keys, dir, now, when, keyid, + (unsigned int)algorithm); + UNLOCK(&kasp->lock); + + switch (result) { + case ISC_R_SUCCESS: + if (use_keyid) { + char tagbuf[6]; + snprintf(tagbuf, sizeof(tagbuf), "%u", keyid); + CHECK(putstr(text, "Key ")); + CHECK(putstr(text, tagbuf)); + CHECK(putstr(text, ": ")); + } + CHECK(putstr(text, "Rollover scheduled on ")); + CHECK(putstr(text, whenbuf)); + break; + case ISC_R_NOTFOUND: + CHECK(putstr(text, "No matching keyfound")); + break; + case ISC_R_FAILURE: + CHECK(putstr(text, + "Error: multiple possible keys found, " + "retry command with -alg algorithm")); + break; + case ISC_R_UNEXPECTED: + CHECK(putstr(text, + "Error: key is not active and cannot " + "be rolled at this time")); + break; + default: + CHECK(putstr(text, "Error executing rollover command")); + break; + } } CHECK(putnull(text)); diff --git a/bin/rndc/rndc.c b/bin/rndc/rndc.c index 0bf246c8e3b..f35d79c5ba5 100644 --- a/bin/rndc/rndc.c +++ b/bin/rndc/rndc.c @@ -117,6 +117,9 @@ command is one of the following:\n\ specific key by providing the keytag with -key id and\n\ optionally the key's algorithm with -alg algorithm.\n\ Requires the zone to have a dnssec-policy.\n\ + dnssec -rollover -key id [-alg algorithm] [-when time] zone [class [view]]\n\ + Rollover key with id of the given zone. Requires the zone\n\ + to have a dnssec-policy.\n\ dnssec -status zone [class [view]]\n\ Show the DNSSEC signing state for the specified zone.\n\ Requires the zone to have a dnssec-policy.\n\ diff --git a/bin/rndc/rndc.rst b/bin/rndc/rndc.rst index 84c58a8311e..70c33783158 100644 --- a/bin/rndc/rndc.rst +++ b/bin/rndc/rndc.rst @@ -161,13 +161,19 @@ Currently supported commands are: See also ``rndc addzone`` and ``rndc modzone``. -``dnssec`` ( **-status** | **-checkds** [**-key** *id* [**-alg** *algorithm*]] [**-when** *time*] ( *published* | *withdrawn* )) *zone* [*class* [*view*]] +``dnssec`` ( **-status** | + **-rollover** **-key** id [**-alg** *algorithm*] [**-when** *time*] | + **-checkds** [**-key** *id* [**-alg** *algorithm*]] [**-when** *time*] ( *published* | *withdrawn* ) + ) *zone* [*class* [*view*]] This command allows you to interact with the "dnssec-policy" of a given zone. ``rndc dnssec -status`` show the DNSSEC signing state for the specified zone. + ``rndc dnssec -rollover`` allows you to schedule key rollover for a + specific key (overriding the original key lifetime). + ``rndc dnssec -checkds`` will let ``named`` know that the DS for the given key has been seen published into or withdrawn from the parent. This is required in order to complete a KSK rollover. If the ``-key id`` argument diff --git a/bin/tests/system/kasp/ns3/named.conf.in b/bin/tests/system/kasp/ns3/named.conf.in index ef1fc22fa74..d6e5300e254 100644 --- a/bin/tests/system/kasp/ns3/named.conf.in +++ b/bin/tests/system/kasp/ns3/named.conf.in @@ -73,6 +73,13 @@ zone "unlimited.kasp" { dnssec-policy "unlimited"; }; +/* Manual rollover. */ +zone "manual-rollover.kasp" { + type primary; + file "manual-rollover.kasp.db"; + dnssec-policy "manual-rollover"; +}; + /* A master zone with dnssec-policy, no keys created. */ zone "rsasha1.kasp" { type primary; diff --git a/bin/tests/system/kasp/ns3/policies/kasp.conf b/bin/tests/system/kasp/ns3/policies/kasp.conf index 81c764c7f20..e5eaadba7a7 100644 --- a/bin/tests/system/kasp/ns3/policies/kasp.conf +++ b/bin/tests/system/kasp/ns3/policies/kasp.conf @@ -17,6 +17,15 @@ dnssec-policy "unlimited" { }; }; +dnssec-policy "manual-rollover" { + dnskey-ttl 3600; + + keys { + ksk key-directory lifetime unlimited algorithm 13; + zsk key-directory lifetime unlimited algorithm 13; + }; +}; + dnssec-policy "rsasha1" { dnskey-ttl 1234; diff --git a/bin/tests/system/kasp/ns3/setup.sh b/bin/tests/system/kasp/ns3/setup.sh index b72067b4b4b..dc5d8656099 100644 --- a/bin/tests/system/kasp/ns3/setup.sh +++ b/bin/tests/system/kasp/ns3/setup.sh @@ -53,7 +53,8 @@ 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 \ - checkds-ksk checkds-doubleksk checkds-csk inherit unlimited + checkds-ksk checkds-doubleksk checkds-csk inherit unlimited \ + manual-rollover do setup "${zn}.kasp" cp template.db.in "$zonefile" @@ -109,6 +110,20 @@ $SETTIME -s -g $O -k $R $Tpub -z $R $Tpub "$ZSK2" > settime.out.$zo # Set up zones that are already signed. # +# Zone to test manual rollover. +setup manual-rollover.kasp +T="now-1d" +ksktimes="-P $T -A $T -P sync $T" +zsktimes="-P $T -A $T" +KSK=$($KEYGEN -a ECDSAP256SHA256 -L 3600 -f KSK $ksktimes $zone 2> keygen.out.$zone.1) +ZSK=$($KEYGEN -a ECDSAP256SHA256 -L 3600 $zsktimes $zone 2> keygen.out.$zone.2) +$SETTIME -s -g $O -d $O $T -k $O $T -r $O $T "$KSK" > settime.out.$zone.1 2>&1 +$SETTIME -s -g $O -k $O $T -z $O $T "$ZSK" > settime.out.$zone.2 2>&1 +cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile" +private_type_record $zone 13 "$KSK" >> "$infile" +private_type_record $zone 13 "$ZSK" >> "$infile" +$SIGNER -PS -x -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + # These signatures are set to expire long in the past, update immediately. setup expired-sigs.autosign T="now-6mo" diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index f9d20ee5c58..be067da571d 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -1201,7 +1201,7 @@ check_cdslog() { } # -# rndc dnssec -checkds +# Utility to call after 'rndc dnssec -checkds|-rollover'. # _loadkeys_on() { _server=$1 @@ -1242,6 +1242,31 @@ rndc_checkds() { _loadkeys_on $_server $_dir $_zone || log_error "loadkeys zone ${_zone} failed ($n)" } +# Tell named to schedule a key rollover. +rndc_rollover() { + _server=$1 + _dir=$2 + _keyid=$3 + _when=$4 + _zone=$5 + _view=$6 + + n=$((n+1)) + echo_i "calling rndc dnssec -rollover key ${_keyid} zone ${_zone} ($n)" + ret=0 + + if [ "${_when}" = "now" ]; then + rndccmd $_server dnssec -rollover -key $_keyid $_zone in $_view > rndc.dnssec.rollover.out.$_zone.$n || log_error "rndc dnssec -rollover (key ${_keyid} when ${_when}) zone ${_zone} failed" + else + rndccmd $_server dnssec -rollover -key $_keyid -when $_when $_zone in $_view > rndc.dnssec.rollover.out.$_zone.$n || log_error "rndc dnssec -rollover (key ${_keyid} when ${_when}) zone ${_zone} failed" + fi + + _loadkeys_on $_server $_dir $_zone || log_error "loadkeys zone ${_zone} failed ($n)" + + test "$ret" -eq 0 || echo_i "failed" + status=$((status+ret)) +} + # # Zone: default.kasp. # @@ -2655,6 +2680,128 @@ status=$((status+ret)) # Clear TSIG. TSIG="" +# +# Testing manual rollover. +# +set_zone "manual-rollover.kasp" +set_policy "manual-rollover" "2" "3600" +set_server "ns3" "10.53.0.3" +key_clear "KEY1" +key_clear "KEY2" +key_clear "KEY3" +key_clear "KEY4" +# Key properties. +set_keyrole "KEY1" "ksk" +set_keylifetime "KEY1" "0" +set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256" +set_keysigning "KEY1" "yes" +set_zonesigning "KEY1" "no" + +set_keyrole "KEY2" "zsk" +set_keylifetime "KEY2" "0" +set_keyalgorithm "KEY2" "13" "ECDSAP256SHA256" "256" +set_keysigning "KEY2" "no" +set_zonesigning "KEY2" "yes" +# During set up everything was set to OMNIPRESENT. +set_keystate "KEY1" "GOAL" "omnipresent" +set_keystate "KEY1" "STATE_DNSKEY" "omnipresent" +set_keystate "KEY1" "STATE_KRRSIG" "omnipresent" +set_keystate "KEY1" "STATE_DS" "omnipresent" + +set_keystate "KEY2" "GOAL" "omnipresent" +set_keystate "KEY2" "STATE_DNSKEY" "omnipresent" +set_keystate "KEY2" "STATE_ZRRSIG" "omnipresent" + +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" + +# The first keys were published and activated a day ago. +created=$(key_get KEY1 CREATED) +set_addkeytime "KEY1" "PUBLISHED" "${created}" -86400 +set_addkeytime "KEY1" "SYNCPUBLISH" "${created}" -86400 +set_addkeytime "KEY1" "ACTIVE" "${created}" -86400 +created=$(key_get KEY2 CREATED) +set_addkeytime "KEY2" "PUBLISHED" "${created}" -86400 +set_addkeytime "KEY2" "ACTIVE" "${created}" -86400 +# Key lifetimes are unlimited, so not setting RETIRED and REMOVED. +check_keytimes +check_apex +check_subdomain +dnssec_verify + +# Schedule KSK rollover in six months (15552000 seconds). +active=$(key_get KEY1 ACTIVE) +set_addkeytime "KEY1" "RETIRED" "${active}" 15552000 +retired=$(key_get KEY1 RETIRED) +rndc_rollover "$SERVER" "$DIR" $(key_get KEY1 ID) "${retired}" "$ZONE" +# Rollover starts in six months, but lifetime is set to six months plus +# prepublication duration = 15552000 + 7500 = 15559500 seconds. +set_keylifetime "KEY1" "15559500" +set_addkeytime "KEY1" "RETIRED" "${active}" 15559500 +retired=$(key_get KEY1 RETIRED) +# Retire interval of this policy is 26h (93600 seconds). +set_addkeytime "KEY1" "REMOVED" "${retired}" 93600 + +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" +check_keytimes +check_apex +check_subdomain +dnssec_verify + +# Schedule KSK rollover now. +set_policy "manual-rollover" "3" "3600" +set_keystate "KEY1" "GOAL" "hidden" +# This key was activated one day agao, so lifetime is set to 1d plus +# prepublication duration (7500 seconds) = 93900 seconds. +set_keylifetime "KEY1" "93900" +created=$(key_get KEY1 CREATED) +set_keytime "KEY1" "RETIRED" "${created}" +rndc_rollover "$SERVER" "$DIR" $(key_get KEY1 ID) "${created}" "$ZONE" +# New key is introduced. +set_keyrole "KEY3" "ksk" +set_keylifetime "KEY3" "0" +set_keyalgorithm "KEY3" "13" "ECDSAP256SHA256" "256" +set_keysigning "KEY3" "yes" +set_zonesigning "KEY3" "no" + +set_keystate "KEY3" "GOAL" "omnipresent" +set_keystate "KEY3" "STATE_DNSKEY" "rumoured" +set_keystate "KEY3" "STATE_KRRSIG" "rumoured" +set_keystate "KEY3" "STATE_DS" "hidden" + +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" +check_apex +check_subdomain +dnssec_verify + +# Schedule ZSK rollover now. +set_policy "manual-rollover" "4" "3600" +set_keystate "KEY2" "GOAL" "hidden" +# This key was activated one day agao, so lifetime is set to 1d plus +# prepublication duration (7500 seconds) = 93900 seconds. +set_keylifetime "KEY2" "93900" +created=$(key_get KEY2 CREATED) +set_keytime "KEY2" "RETIRED" "${created}" +rndc_rollover "$SERVER" "$DIR" $(key_get KEY2 ID) "${created}" "$ZONE" +# New key is introduced. +set_keyrole "KEY4" "zsk" +set_keylifetime "KEY4" "0" +set_keyalgorithm "KEY4" "13" "ECDSAP256SHA256" "256" +set_keysigning "KEY4" "no" +set_zonesigning "KEY4" "no" # not yet, first prepublish DNSKEY. + +set_keystate "KEY4" "GOAL" "omnipresent" +set_keystate "KEY4" "STATE_DNSKEY" "rumoured" +set_keystate "KEY4" "STATE_ZRRSIG" "hidden" + +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" +check_apex +check_subdomain +dnssec_verify + # # Testing DNSSEC introduction. # diff --git a/doc/man/rndc.8in b/doc/man/rndc.8in index 2b821109fc0..da4afa2436a 100644 --- a/doc/man/rndc.8in +++ b/doc/man/rndc.8in @@ -161,13 +161,29 @@ recreated. To remove it permanently, it must also be removed from .sp See also \fBrndc addzone\fP and \fBrndc modzone\fP\&. .TP -\fBdnssec\fP ( \fB\-status\fP | \fB\-checkds\fP [\fB\-key\fP \fIid\fP [\fB\-alg\fP \fIalgorithm\fP]] [\fB\-when\fP \fItime\fP] ( \fIpublished\fP | \fIwithdrawn\fP )) \fIzone\fP [\fIclass\fP [\fIview\fP]] +\fBdnssec\fP ( \fB\-status\fP | +.INDENT 7.0 +.INDENT 3.5 +.INDENT 0.0 +.INDENT 3.5 +\fB\-rollover\fP \fB\-key\fP id [\fB\-alg\fP \fIalgorithm\fP] [\fB\-when\fP \fItime\fP] | +\fB\-checkds\fP [\fB\-key\fP \fIid\fP [\fB\-alg\fP \fIalgorithm\fP]] [\fB\-when\fP \fItime\fP] ( \fIpublished\fP | \fIwithdrawn\fP ) +.UNINDENT +.UNINDENT +.sp +) \fIzone\fP [\fIclass\fP [\fIview\fP]] +.UNINDENT +.UNINDENT +.sp This command allows you to interact with the "dnssec\-policy" of a given zone. .sp \fBrndc dnssec \-status\fP show the DNSSEC signing state for the specified zone. .sp +\fBrndc dnssec \-rollover\fP allows you to schedule key rollover for a +specific key (overriding the original key lifetime). +.sp \fBrndc dnssec \-checkds\fP will let \fBnamed\fP know that the DS for the given key has been seen published into or withdrawn from the parent. This is required in order to complete a KSK rollover. If the \fB\-key id\fP argument diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 045449120d0..03baa1660a2 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -26,6 +26,9 @@ New Features - None. +- Add a new ``rndc`` command, ``rndc dnssec -rollover``, which triggers + a manual rollover for a specific key. [GL #1749] + - New ``rndc`` command ``rndc dumpdb -expired`` that dumps the cache database to the dump-file including expired RRsets that are awaiting cleanup, for diagnostic purposes. [GL #1870] @@ -35,6 +38,7 @@ Removed Features - None. + Feature Changes ~~~~~~~~~~~~~~~