.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
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:
{ 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,
// 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
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;
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);
}
*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;
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);
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;
* 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.
*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;
+}
*/
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);
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;
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);
+ }
}
}
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")
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()