Merge branch '4554-dnssec-policy-jitter-9.18' into 'bind-9.18'

[9.18] Add signatures-jitter option

See merge request isc-projects/bind9!8953
This commit is contained in:
Matthijs Mekking 2024-04-18 15:00:17 +00:00
commit 76cb1af2b3
15 changed files with 204 additions and 100 deletions

View file

@ -1,3 +1,5 @@
6372. [func] Implement signature jitter for dnssec-policy. [GL #4554]
--- 9.18.26 released ---
6364. [protocol] Add RESOLVER.ARPA to the built in empty zones.

View file

@ -309,6 +309,7 @@ dnssec-policy \"default\" {\n\
publish-safety " DNS_KASP_PUBLISH_SAFETY "; \n\
retire-safety " DNS_KASP_RETIRE_SAFETY "; \n\
purge-keys " DNS_KASP_PURGE_KEYS "; \n\
signatures-jitter " DNS_KASP_SIG_JITTER "; \n\
signatures-refresh " DNS_KASP_SIG_REFRESH "; \n\
signatures-validity " DNS_KASP_SIG_VALIDITY "; \n\
signatures-validity-dnskey " DNS_KASP_SIG_VALIDITY_DNSKEY "; \n\

View file

@ -0,0 +1,27 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/*
* The dnssec-policy jitter is more than signatures-validity,
* which is not allowed.
*/
dnssec-policy high-jitter {
signatures-jitter P8DT1S;
signatures-validity P8D;
};
zone "example.net" {
type primary;
file "example.db";
dnssec-policy high-jitter;
};

View file

@ -29,6 +29,7 @@ dnssec-policy "test" {
parent-propagation-delay PT1H;
publish-safety PT3600S;
retire-safety PT3600S;
signatures-jitter PT12H;
signatures-refresh P3D;
signatures-validity P2W;
signatures-validity-dnskey P14D;

View file

@ -30,6 +30,7 @@ dnssec-policy "test" {
publish-safety PT3600S;
purge-keys P90D;
retire-safety PT3600S;
signatures-jitter PT12H;
signatures-refresh P3D;
signatures-validity P2W;
signatures-validity-dnskey P14D;

View file

@ -6509,6 +6509,18 @@ The following options can be specified in a :any:`dnssec-policy` statement:
unforeseen events. This increases the time a key remains published
after it is no longer active. The default is ``PT1H`` (1 hour).
.. namedconf:statement:: signatures-jitter
:tags: dnssec
:short: Specifies a range for signatures expirations.
To prevent all signatures from expiring at the same moment, BIND 9 may
vary the validity interval of individual signatures. The validity of a
newly generated signatures is in range between :any:`signatures-validity`
(maximum) and :any:`signatures-validity` minus :any:`signatures-jitter`
(minimum). The default jitter is 12 hours and the configured value must
be lower than :any:`signatures-validity` and
:any:`signatures-validity-dnskey`.
.. namedconf:statement:: signatures-refresh
:tags: dnssec
:short: Specifies how frequently an RRSIG record is refreshed.

View file

@ -24,6 +24,7 @@ dnssec-policy "default" {
purge-keys P90D;
// Signature timings
signatures-jitter 12h;
signatures-refresh 5d;
signatures-validity 14d;
signatures-validity-dnskey 14d;

View file

@ -21,6 +21,7 @@ dnssec-policy <string> {
publish-safety <duration>;
purge-keys <duration>;
retire-safety <duration>;
signatures-jitter <duration>;
signatures-refresh <duration>;
signatures-validity <duration>;
signatures-validity-dnskey <duration>;

View file

@ -22,6 +22,9 @@ New Features
- None.
- A new option :any:`signatures-jitter` is added to :any:`dnssec-policy` to
spread out signature expiration times over a period of time. :gl:`#4554`
Removed Features
~~~~~~~~~~~~~~~~

View file

@ -75,6 +75,7 @@ struct dns_kasp {
ISC_LINK(struct dns_kasp) link;
/* Configuration: signatures */
uint32_t signatures_jitter;
uint32_t signatures_refresh;
uint32_t signatures_validity;
uint32_t signatures_validity_dnskey;
@ -105,6 +106,8 @@ struct dns_kasp {
#define DNS_KASP_VALID(kasp) ISC_MAGIC_VALID(kasp, DNS_KASP_MAGIC)
/* Defaults */
#define DEFAULT_JITTER (12 * 3600)
#define DNS_KASP_SIG_JITTER "PT12H"
#define DNS_KASP_SIG_REFRESH "P5D"
#define DNS_KASP_SIG_VALIDITY "P14D"
#define DNS_KASP_SIG_VALIDITY_DNSKEY "P14D"
@ -233,6 +236,30 @@ dns_kasp_signdelay(dns_kasp_t *kasp);
*\li signature refresh interval.
*/
uint32_t
dns_kasp_sigjitter(dns_kasp_t *kasp);
/*%<
* Get signature jitter value.
*
* Requires:
*
*\li 'kasp' is a valid, frozen kasp.
*
* Returns:
*
*\li signature jitter value.
*/
void
dns_kasp_setsigjitter(dns_kasp_t *kasp, uint32_t value);
/*%<
* Set signature jitter value.
*
* Requires:
*
*\li 'kasp' is a valid, thawed kasp.
*/
uint32_t
dns_kasp_sigrefresh(dns_kasp_t *kasp);
/*%<

View file

@ -127,6 +127,22 @@ dns_kasp_signdelay(dns_kasp_t *kasp) {
return (kasp->signatures_validity - kasp->signatures_refresh);
}
uint32_t
dns_kasp_sigjitter(dns_kasp_t *kasp) {
REQUIRE(DNS_KASP_VALID(kasp));
REQUIRE(kasp->frozen);
return (kasp->signatures_jitter);
}
void
dns_kasp_setsigjitter(dns_kasp_t *kasp, uint32_t value) {
REQUIRE(DNS_KASP_VALID(kasp));
REQUIRE(!kasp->frozen);
kasp->signatures_jitter = value;
}
uint32_t
dns_kasp_sigrefresh(dns_kasp_t *kasp) {
REQUIRE(DNS_KASP_VALID(kasp));

View file

@ -1492,23 +1492,37 @@ struct dns_update_state {
};
static uint32_t
dns__jitter_expire(dns_zone_t *zone, uint32_t sigvalidityinterval) {
dns__jitter_expire(dns_zone_t *zone) {
/* Spread out signatures over time */
if (sigvalidityinterval >= 3600U) {
uint32_t expiryinterval =
dns_zone_getsigresigninginterval(zone);
isc_stdtime_t jitter = DEFAULT_JITTER;
isc_stdtime_t sigvalidity = dns_zone_getsigvalidityinterval(zone);
dns_kasp_t *kasp = dns_zone_getkasp(zone);
if (sigvalidityinterval < 7200U) {
expiryinterval = 1200;
} else if (expiryinterval > sigvalidityinterval) {
expiryinterval = sigvalidityinterval;
if (kasp != NULL) {
jitter = dns_kasp_sigjitter(kasp);
sigvalidity = dns_kasp_sigvalidity(kasp);
INSIST(jitter <= sigvalidity);
} else {
jitter = dns_zone_getsigresigninginterval(zone);
if (jitter > sigvalidity) {
jitter = sigvalidity;
} else {
expiryinterval = sigvalidityinterval - expiryinterval;
jitter = sigvalidity - jitter;
}
uint32_t jitter = isc_random_uniform(expiryinterval);
sigvalidityinterval -= jitter;
}
return (sigvalidityinterval);
if (jitter > sigvalidity) {
jitter = sigvalidity;
}
if (sigvalidity >= 3600U) {
if (sigvalidity > 7200U) {
sigvalidity -= isc_random_uniform(jitter);
} else {
sigvalidity -= isc_random_uniform(1200);
}
}
return (sigvalidity);
}
isc_result_t
@ -1561,8 +1575,7 @@ dns_update_signaturesinc(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db,
isc_stdtime_get(&state->now);
state->inception = state->now - 3600; /* Allow for some clock
skew. */
state->expire = state->now +
dns__jitter_expire(zone, sigvalidityinterval);
state->expire = state->now + dns__jitter_expire(zone);
state->soaexpire = state->now + sigvalidityinterval;
state->keyexpire = dns_zone_getkeyvalidityinterval(zone);
if (state->keyexpire == 0) {

View file

@ -7184,6 +7184,60 @@ failure:
return (result);
}
static void
calculate_rrsig_validity(dns_zone_t *zone, isc_stdtime_t now,
isc_stdtime_t *inception, isc_stdtime_t *soaexpire,
isc_stdtime_t *expire, isc_stdtime_t *fullexpire) {
REQUIRE(inception != NULL);
REQUIRE(soaexpire != NULL);
/* expire and fullexpire are optional */
isc_stdtime_t jitter = DEFAULT_JITTER;
isc_stdtime_t sigvalidity = dns_zone_getsigvalidityinterval(zone);
isc_stdtime_t shortjitter = 0, fulljitter = 0;
if (zone->kasp != NULL) {
jitter = dns_kasp_sigjitter(zone->kasp);
sigvalidity = dns_kasp_sigvalidity(zone->kasp);
INSIST(jitter <= sigvalidity);
} else {
jitter = dns_zone_getsigresigninginterval(zone);
if (jitter > sigvalidity) {
jitter = sigvalidity;
} else {
jitter = sigvalidity - jitter;
}
}
if (jitter > sigvalidity) {
jitter = sigvalidity;
}
*inception = now - 3600; /* Allow for clock skew. */
*soaexpire = now + sigvalidity;
/*
* Spread out signatures over time if they happen to be
* clumped. We don't do this for each add_sigs() call as
* we still want some clustering to occur. In normal operations
* the records should be re-signed as they fall due and they should
* already be spread out. However if the server is off for a
* period we need to ensure that the clusters don't become
* synchronised by using the full jitter range.
*/
if (sigvalidity >= 3600U) {
if (sigvalidity > 7200U) {
shortjitter = isc_random_uniform(3600);
fulljitter = isc_random_uniform(jitter);
} else {
shortjitter = fulljitter = isc_random_uniform(1200);
}
}
SET_IF_NOT_NULL(expire, *soaexpire - shortjitter - 1);
SET_IF_NOT_NULL(fullexpire, *soaexpire - fulljitter - 1);
}
static void
zone_resigninc(dns_zone_t *zone) {
const char *me = "zone_resigninc";
@ -7199,7 +7253,6 @@ zone_resigninc(dns_zone_t *zone) {
bool check_ksk, keyset_kskonly = false;
isc_result_t result;
isc_stdtime_t now, inception, soaexpire, expire, fullexpire, stop;
uint32_t sigvalidityinterval, expiryinterval;
unsigned int i;
unsigned int nkeys = 0;
unsigned int resign;
@ -7250,38 +7303,9 @@ zone_resigninc(dns_zone_t *zone) {
goto failure;
}
sigvalidityinterval = dns_zone_getsigvalidityinterval(zone);
inception = now - 3600; /* Allow for clock skew. */
soaexpire = now + sigvalidityinterval;
expiryinterval = dns_zone_getsigresigninginterval(zone);
if (expiryinterval > sigvalidityinterval) {
expiryinterval = sigvalidityinterval;
} else {
expiryinterval = sigvalidityinterval - expiryinterval;
}
calculate_rrsig_validity(zone, now, &inception, &soaexpire, &expire,
&fullexpire);
/*
* Spread out signatures over time if they happen to be
* clumped. We don't do this for each add_sigs() call as
* we still want some clustering to occur. In normal operations
* the records should be re-signed as they fall due and they should
* already be spread out. However if the server is off for a
* period we need to ensure that the clusters don't become
* synchronised by using the full jitter range.
*/
if (sigvalidityinterval >= 3600U) {
uint32_t normaljitter, fulljitter;
if (sigvalidityinterval > 7200U) {
normaljitter = isc_random_uniform(3600);
fulljitter = isc_random_uniform(expiryinterval);
} else {
normaljitter = fulljitter = isc_random_uniform(1200);
}
expire = soaexpire - normaljitter - 1;
fullexpire = soaexpire - fulljitter - 1;
} else {
expire = fullexpire = soaexpire - 1;
}
stop = now + 5;
check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
@ -8376,7 +8400,6 @@ zone_nsec3chain(dns_zone_t *zone) {
bool first;
isc_result_t result;
isc_stdtime_t now, inception, soaexpire, expire;
uint32_t jitter, sigvalidityinterval, expiryinterval;
unsigned int i;
unsigned int nkeys = 0;
uint32_t nodes;
@ -8445,31 +8468,8 @@ zone_nsec3chain(dns_zone_t *zone) {
goto failure;
}
sigvalidityinterval = dns_zone_getsigvalidityinterval(zone);
inception = now - 3600; /* Allow for clock skew. */
soaexpire = now + sigvalidityinterval;
expiryinterval = dns_zone_getsigresigninginterval(zone);
if (expiryinterval > sigvalidityinterval) {
expiryinterval = sigvalidityinterval;
} else {
expiryinterval = sigvalidityinterval - expiryinterval;
}
/*
* Spread out signatures over time if they happen to be
* clumped. We don't do this for each add_sigs() call as
* we still want some clustering to occur.
*/
if (sigvalidityinterval >= 3600U) {
if (sigvalidityinterval > 7200U) {
jitter = isc_random_uniform(expiryinterval);
} else {
jitter = isc_random_uniform(1200);
}
expire = soaexpire - jitter - 1;
} else {
expire = soaexpire - 1;
}
calculate_rrsig_validity(zone, now, &inception, &soaexpire, NULL,
&expire);
check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
keyset_kskonly = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_DNSKEYKSKONLY);
@ -9482,7 +9482,6 @@ zone_sign(dns_zone_t *zone) {
bool first;
isc_result_t result;
isc_stdtime_t now, inception, soaexpire, expire;
uint32_t jitter, sigvalidityinterval, expiryinterval;
unsigned int i, j;
unsigned int nkeys = 0;
uint32_t nodes;
@ -9534,32 +9533,8 @@ zone_sign(dns_zone_t *zone) {
goto cleanup;
}
kasp = dns_zone_getkasp(zone);
sigvalidityinterval = dns_zone_getsigvalidityinterval(zone);
inception = now - 3600; /* Allow for clock skew. */
soaexpire = now + sigvalidityinterval;
expiryinterval = dns_zone_getsigresigninginterval(zone);
if (expiryinterval > sigvalidityinterval) {
expiryinterval = sigvalidityinterval;
} else {
expiryinterval = sigvalidityinterval - expiryinterval;
}
/*
* Spread out signatures over time if they happen to be
* clumped. We don't do this for each add_sigs() call as
* we still want some clustering to occur.
*/
if (sigvalidityinterval >= 3600U) {
if (sigvalidityinterval > 7200U) {
jitter = isc_random_uniform(expiryinterval);
} else {
jitter = isc_random_uniform(1200);
}
expire = soaexpire - jitter - 1;
} else {
expire = soaexpire - 1;
}
calculate_rrsig_validity(zone, now, &inception, &soaexpire, NULL,
&expire);
/*
* We keep pulling nodes off each iterator in turn until
@ -9575,6 +9550,7 @@ zone_sign(dns_zone_t *zone) {
check_ksk = false;
keyset_kskonly = true;
use_kasp = true;
kasp = zone->kasp;
} else {
check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
keyset_kskonly = DNS_ZONE_OPTION(zone,

View file

@ -312,7 +312,7 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
const char *kaspname = NULL;
dns_kasp_t *kasp = NULL;
size_t i = 0;
uint32_t sigrefresh = 0, sigvalidity = 0;
uint32_t sigjitter = 0, sigrefresh = 0, sigvalidity = 0;
uint32_t dnskeyttl = 0, dsttl = 0, maxttl = 0;
uint32_t publishsafety = 0, retiresafety = 0;
uint32_t zonepropdelay = 0, parentpropdelay = 0;
@ -360,6 +360,10 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
maps[i] = NULL;
/* Configuration: Signatures */
sigjitter = get_duration(maps, "signatures-jitter",
DNS_KASP_SIG_JITTER);
dns_kasp_setsigjitter(kasp, sigjitter);
sigrefresh = get_duration(maps, "signatures-refresh",
DNS_KASP_SIG_REFRESH);
dns_kasp_setsigrefresh(kasp, sigrefresh);
@ -376,6 +380,15 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
}
dns_kasp_setsigvalidity_dnskey(kasp, sigvalidity);
if (sigjitter > sigvalidity) {
cfg_obj_log(
config, logctx, ISC_LOG_ERROR,
"dnssec-policy: policy '%s' signatures-jitter cannot "
"be larger than signatures-validity-dnskey",
kaspname);
result = ISC_R_FAILURE;
}
sigvalidity = get_duration(maps, "signatures-validity",
DNS_KASP_SIG_VALIDITY);
if (sigrefresh >= (sigvalidity * 0.9)) {
@ -388,6 +401,15 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
}
dns_kasp_setsigvalidity(kasp, sigvalidity);
if (sigjitter > sigvalidity) {
cfg_obj_log(
config, logctx, ISC_LOG_ERROR,
"dnssec-policy: policy '%s' signatures-jitter cannot "
"be larger than signatures-validity",
kaspname);
result = ISC_R_FAILURE;
}
if (result != ISC_R_SUCCESS) {
goto cleanup;
}

View file

@ -2211,6 +2211,7 @@ static cfg_clausedef_t dnssecpolicy_clauses[] = {
{ "publish-safety", &cfg_type_duration, 0 },
{ "purge-keys", &cfg_type_duration, 0 },
{ "retire-safety", &cfg_type_duration, 0 },
{ "signatures-jitter", &cfg_type_duration, 0 },
{ "signatures-refresh", &cfg_type_duration, 0 },
{ "signatures-validity", &cfg_type_duration, 0 },
{ "signatures-validity-dnskey", &cfg_type_duration, 0 },