mirror of
https://gitlab.nic.cz/knot/knot-dns.git
synced 2026-06-09 08:33:59 -04:00
dnssec: implemented re-salt option on every ZSK roll occasion
This commit is contained in:
parent
d5a1cb1c99
commit
89662f91bf
11 changed files with 106 additions and 19 deletions
|
|
@ -1511,6 +1511,9 @@ A validity period of newly issued salt field.
|
|||
.sp
|
||||
Zero value means infinity.
|
||||
.sp
|
||||
Special value \fI\-1\fP triggers re\-salt every time when active ZSK changes.
|
||||
This optimizes the number of big changes to the zone.
|
||||
.sp
|
||||
\fIDefault:\fP 30 days
|
||||
.SS signing\-threads
|
||||
.sp
|
||||
|
|
|
|||
|
|
@ -1648,6 +1648,9 @@ A validity period of newly issued salt field.
|
|||
|
||||
Zero value means infinity.
|
||||
|
||||
Special value *-1* triggers re-salt every time when active ZSK changes.
|
||||
This optimizes the number of big changes to the zone.
|
||||
|
||||
*Default:* 30 days
|
||||
|
||||
.. _policy_signing-threads:
|
||||
|
|
|
|||
|
|
@ -375,7 +375,7 @@ static const yp_item_t desc_policy[] = {
|
|||
{ C_NSEC3_ITER, YP_TINT, YP_VINT = { 0, UINT16_MAX, 10 }, CONF_IO_FRLD_ZONES },
|
||||
{ C_NSEC3_OPT_OUT, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES },
|
||||
{ C_NSEC3_SALT_LEN, YP_TINT, YP_VINT = { 0, UINT8_MAX, 8 }, CONF_IO_FRLD_ZONES },
|
||||
{ C_NSEC3_SALT_LIFETIME, YP_TINT, YP_VINT = { 0, UINT32_MAX, DAYS(30), YP_STIME },
|
||||
{ C_NSEC3_SALT_LIFETIME, YP_TINT, YP_VINT = { -1, UINT32_MAX, DAYS(30), YP_STIME },
|
||||
CONF_IO_FRLD_ZONES },
|
||||
{ C_SIGNING_THREADS, YP_TINT, YP_VINT = { 1, UINT16_MAX, 1 } },
|
||||
{ C_KSK_SBM, YP_TREF, YP_VREF = { C_SBM }, CONF_IO_FRLD_ZONES,
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ typedef struct {
|
|||
// NSEC3
|
||||
bool nsec3_enabled;
|
||||
bool nsec3_opt_out;
|
||||
uint32_t nsec3_salt_lifetime; // like knot_time_t
|
||||
int64_t nsec3_salt_lifetime; // like knot_time_t
|
||||
uint16_t nsec3_iterations;
|
||||
uint8_t nsec3_salt_length;
|
||||
// zone
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@ static int generate_salt(dnssec_binary_t *salt, uint16_t length)
|
|||
return KNOT_EOK;
|
||||
}
|
||||
|
||||
int knot_dnssec_nsec3resalt(kdnssec_ctx_t *ctx, knot_time_t *salt_changed, knot_time_t *when_resalt)
|
||||
int knot_dnssec_nsec3resalt(kdnssec_ctx_t *ctx, bool soa_rrsigs_ok,
|
||||
knot_time_t *salt_changed, knot_time_t *when_resalt)
|
||||
{
|
||||
int ret = KNOT_EOK;
|
||||
|
||||
|
|
@ -74,11 +75,13 @@ int knot_dnssec_nsec3resalt(kdnssec_ctx_t *ctx, knot_time_t *salt_changed, knot_
|
|||
return KNOT_EOK;
|
||||
}
|
||||
|
||||
if (ctx->zone->nsec3_salt.size != ctx->policy->nsec3_salt_length || ctx->zone->nsec3_salt_created == 0) {
|
||||
if (ctx->policy->nsec3_salt_lifetime < 0 && !soa_rrsigs_ok) {
|
||||
*when_resalt = ctx->now;
|
||||
} else if (ctx->zone->nsec3_salt.size != ctx->policy->nsec3_salt_length || ctx->zone->nsec3_salt_created == 0) {
|
||||
*when_resalt = ctx->now;
|
||||
} else if (knot_time_cmp(ctx->now, ctx->zone->nsec3_salt_created) < 0) {
|
||||
return KNOT_EINVAL;
|
||||
} else {
|
||||
} else if (ctx->policy->nsec3_salt_lifetime > 0) {
|
||||
*when_resalt = knot_time_plus(ctx->zone->nsec3_salt_created, ctx->policy->nsec3_salt_lifetime);
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +101,9 @@ int knot_dnssec_nsec3resalt(kdnssec_ctx_t *ctx, knot_time_t *salt_changed, knot_
|
|||
*salt_changed = ctx->now;
|
||||
}
|
||||
// continue to planning next resalt even if NOK
|
||||
*when_resalt = knot_time_plus(ctx->now, ctx->policy->nsec3_salt_lifetime);
|
||||
if (ctx->policy->nsec3_salt_lifetime > 0) {
|
||||
*when_resalt = knot_time_plus(ctx->now, ctx->policy->nsec3_salt_lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -140,16 +145,6 @@ int knot_dnssec_zone_sign(zone_update_t *update,
|
|||
goto done;
|
||||
}
|
||||
|
||||
// perform nsec3resalt if pending
|
||||
if (roll_flags & KEY_ROLL_ALLOW_NSEC3RESALT) {
|
||||
result = knot_dnssec_nsec3resalt(&ctx, &reschedule->last_nsec3resalt, &reschedule->next_nsec3resalt);
|
||||
if (result != KNOT_EOK) {
|
||||
log_zone_error(zone_name, "DNSSEC, failed to update NSEC3 salt (%s)",
|
||||
knot_strerror(result));
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.rrsig_drop_existing = flags & ZONE_SIGN_DROP_SIGNATURES;
|
||||
|
||||
conf_val_t val = conf_zone_get(conf, C_ZONEMD_GENERATE, zone_name);
|
||||
|
|
@ -176,6 +171,18 @@ int knot_dnssec_zone_sign(zone_update_t *update,
|
|||
goto done;
|
||||
}
|
||||
|
||||
// perform nsec3resalt if pending
|
||||
if (roll_flags & KEY_ROLL_ALLOW_NSEC3RESALT) {
|
||||
knot_rdataset_t *rrsig = node_rdataset(update->new_cont->apex, KNOT_RRTYPE_RRSIG);
|
||||
bool issbaz = is_soa_signed_by_all_zsks(&keyset, rrsig);
|
||||
result = knot_dnssec_nsec3resalt(&ctx, issbaz, &reschedule->last_nsec3resalt, &reschedule->next_nsec3resalt);
|
||||
if (result != KNOT_EOK) {
|
||||
log_zone_error(zone_name, "DNSSEC, failed to update NSEC3 salt (%s)",
|
||||
knot_strerror(result));
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
log_zone_info(zone_name, "DNSSEC, signing started");
|
||||
|
||||
knot_time_t next_resign = 0;
|
||||
|
|
|
|||
|
|
@ -103,12 +103,14 @@ int knot_dnssec_sign_update(zone_update_t *update, conf_t *conf);
|
|||
* proper DNSSEC chain.
|
||||
*
|
||||
* \param ctx zone signing context
|
||||
* \param soa_rrsigs_ok Zone is signed by current active ZSKs.
|
||||
* \param salt_changed output if KNOT_EOK: when was the salt last changed? (either ctx->now or 0)
|
||||
* \param when_resalt output: timestamp when next resalt takes place
|
||||
*
|
||||
* \return KNOT_E*
|
||||
*/
|
||||
int knot_dnssec_nsec3resalt(kdnssec_ctx_t *ctx, knot_time_t *salt_changed, knot_time_t *when_resalt);
|
||||
int knot_dnssec_nsec3resalt(kdnssec_ctx_t *ctx, bool soa_rrsigs_ok,
|
||||
knot_time_t *salt_changed, knot_time_t *when_resalt);
|
||||
|
||||
/*!
|
||||
* \brief When DNSSEC signing failed, re-plan on this time.
|
||||
|
|
|
|||
|
|
@ -715,3 +715,41 @@ int dnssec_key_from_rdata(dnssec_key_t **key, const knot_dname_t *owner,
|
|||
*key = new_key;
|
||||
return KNOT_EOK;
|
||||
}
|
||||
|
||||
static bool soa_signed_by_key(const zone_key_t *key, const knot_rdataset_t *apex_rrsig)
|
||||
{
|
||||
assert(key != NULL);
|
||||
if (apex_rrsig == NULL) {
|
||||
return false;
|
||||
}
|
||||
uint16_t keytag = dnssec_key_get_keytag(key->key);
|
||||
|
||||
knot_rdata_t *rr = apex_rrsig->rdata;
|
||||
for (int i = 0; i < apex_rrsig->count; i++) {
|
||||
if (knot_rrsig_type_covered(rr) == KNOT_RRTYPE_SOA &&
|
||||
knot_rrsig_key_tag(rr) == keytag) {
|
||||
return true;
|
||||
}
|
||||
rr = knot_rdataset_next(rr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int is_soa_signed_by_all_zsks(const zone_keyset_t *keyset,
|
||||
const knot_rdataset_t *apex_rrsig)
|
||||
{
|
||||
if (keyset == NULL || keyset->count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < keyset->count; i++) {
|
||||
const zone_key_t *key = &keyset->keys[i];
|
||||
if (key->is_zsk && key->is_active &&
|
||||
!soa_signed_by_key(key, apex_rrsig)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,3 +202,12 @@ void zone_sign_ctx_free(zone_sign_ctx_t *ctx);
|
|||
*/
|
||||
int dnssec_key_from_rdata(dnssec_key_t **key, const knot_dname_t *owner,
|
||||
const uint8_t *rdata, size_t rdlen);
|
||||
|
||||
/*!
|
||||
* \brief Tell if apex SOA is signed by all active ZSKs.
|
||||
*
|
||||
* \param keyset Zone key set.
|
||||
* \param apex_rrsig Apex RRSIG RRSet.
|
||||
*/
|
||||
int is_soa_signed_by_all_zsks(const zone_keyset_t *keyset,
|
||||
const knot_rdataset_t *apex_rrsig);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ int event_nsec3resalt(conf_t *conf, zone_t *zone)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = knot_dnssec_nsec3resalt(&kctx, &salt_changed, &next_resalt);
|
||||
ret = knot_dnssec_nsec3resalt(&kctx, true, &salt_changed, &next_resalt);
|
||||
if (ret == KNOT_EOK && salt_changed != 0) {
|
||||
zone_events_schedule_now(zone, ZONE_EVENT_DNSSEC);
|
||||
zone->timers.last_resalt = kctx.now;
|
||||
|
|
|
|||
|
|
@ -130,7 +130,9 @@ void replan_from_timers(conf_t *conf, zone_t *zone)
|
|||
resalt = now;
|
||||
} else {
|
||||
val = conf_id_get(conf, C_POLICY, C_NSEC3_SALT_LIFETIME, &policy);
|
||||
resalt = zone->timers.last_resalt + conf_int(&val);
|
||||
if (conf_int(&val) > 0) {
|
||||
resalt = zone->timers.last_resalt + conf_int(&val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,21 @@ Check of automatic ZSK rollover with changing zone TTLs.
|
|||
from dnstest.utils import *
|
||||
from dnstest.test import Test
|
||||
|
||||
def get_salt(server, zone):
|
||||
resp = server.dig(zone[0].name, "NSEC3PARAM")
|
||||
return resp.resp.answer[0].to_rdataset()[0].to_text().split()[-1]
|
||||
|
||||
last_salt = ""
|
||||
|
||||
def check_salt(server, zone, shall_differ):
|
||||
global last_salt
|
||||
salt = get_salt(server, zone)
|
||||
if shall_differ != (salt != last_salt):
|
||||
msg_not = " " if shall_differ else " not "
|
||||
detail_log("Salt %s, Last salt %s, shall%sdiffer" % (salt, last_salt, msg_not))
|
||||
set_err("NSEC3 salt shall%sdiffer" % msg_not)
|
||||
last_salt = salt
|
||||
|
||||
def dnskey_count(server, zone):
|
||||
return server.dig(zone[0].name, "DNSKEY", dnssec=False).count("DNSKEY")
|
||||
|
||||
|
|
@ -47,17 +62,25 @@ master.dnssec(zone).manual = False
|
|||
master.dnssec(zone).dnskey_ttl = 3
|
||||
master.dnssec(zone).zsk_lifetime = 16
|
||||
master.dnssec(zone).propagation_delay = 3
|
||||
master.dnssec(zone).nsec3 = True
|
||||
master.dnssec(zone).nsec3_salt_lifetime = -1
|
||||
|
||||
t.start()
|
||||
master.zone_wait(zone)
|
||||
check_salt(master, zone, True)
|
||||
|
||||
wait4key(t, master, zone, 3, -1, 6, 20, "ZSK publish") # new ZSK published
|
||||
old_key = zsk_keytag(master, zone)
|
||||
check_salt(master, zone, False)
|
||||
|
||||
wait4key(t, master, zone, 3, old_key, 4, 8, "ZSK switch") # active ZSK switched
|
||||
check_salt(master, zone, True)
|
||||
up = master.update(zone)
|
||||
up.delete("longttl.example.com.", "A") # zone max TTL decreases
|
||||
up.send()
|
||||
master.ctl("zone-sign")
|
||||
|
||||
wait4key(t, master, zone, 2, old_key, 9, 14, "ZSK remove") # old ZSK removed
|
||||
check_salt(master, zone, False)
|
||||
|
||||
t.end()
|
||||
|
|
|
|||
Loading…
Reference in a new issue