]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add rndc dnssec -rollover command
authorMatthijs Mekking <matthijs@isc.org>
Fri, 21 Aug 2020 13:38:00 +0000 (15:38 +0200)
committerMatthijs Mekking <matthijs@isc.org>
Mon, 5 Oct 2020 08:53:45 +0000 (10:53 +0200)
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.

CHANGES
bin/named/server.c
bin/rndc/rndc.c
bin/rndc/rndc.rst
bin/tests/system/kasp/ns3/named.conf.in
bin/tests/system/kasp/ns3/policies/kasp.conf
bin/tests/system/kasp/ns3/setup.sh
bin/tests/system/kasp/tests.sh
doc/man/rndc.8in
doc/notes/notes-current.rst

diff --git a/CHANGES b/CHANGES
index 0227ef6582ebf8539171bf67f8198351b4b8709b..e355fb9b829b6039965b2bd047f1cc2c36bc8fea 100644 (file)
--- 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]
 
index 3a9e9ed58ecd47d1cb35ce80dafb1606fbaff421..c2c126937183a4bcaa37b22a7801ebbcc4382cc5 100644 (file)
@@ -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));
 
index 0bf246c8e3bd20e9be9d39fc95a82a676f84007d..f35d79c5ba5cc2dd7d93979cefac3587099a41f5 100644 (file)
@@ -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\
index 84c58a8311e2f6c7e40b6cf71bc8edbcf061ba4a..70c33783158256a60b977aad0c0a44427d9b0f6d 100644 (file)
@@ -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
index ef1fc22fa743ad5de3910164bb48799e1bc90be6..d6e5300e25444f1d1a8bac30c21073edbce1a2c6 100644 (file)
@@ -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;
index 81c764c7f203f65117f3994ed6b51d503871cd4e..e5eaadba7a79c2da3e9d2e8fa3c1dec92265c86c 100644 (file)
@@ -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;
 
index b72067b4b4bd95a7d96ea9564515885ed7e37d7e..dc5d8656099363fe84ae1cc6c5f78b6feb1d650a 100644 (file)
@@ -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"
index f9d20ee5c582f291efe57edc99253c717f3c644c..be067da571d8fcc5d884d5e529bf204bcf239aaf 100644 (file)
@@ -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.
 #
index 2b821109fc010ab075ebaed3421ca6e47b96c075..da4afa2436a36e530e62feb936bcb48cfa859461 100644 (file)
@@ -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
index 045449120d024945ef02cd659dfdc5b6fa1fe879..03baa1660a2e197764c7c40e7622adda06bdfe3f 100644 (file)
@@ -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
 ~~~~~~~~~~~~~~~