Commit e826faca authored by Matthijs Mekking's avatar Matthijs Mekking 🏡

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.
parent df8276ae
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]
......
......@@ -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));
......
......@@ -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\
......
......@@ -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
......
......@@ -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;
......
......@@ -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;
......
......@@ -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"
......
......@@ -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.
#
......
......@@ -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
......
......@@ -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
~~~~~~~~~~~~~~~
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment