]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
dnskey-sync: add jitter to scheduled event to minimize race condition
authorDaniel Salzman <daniel.salzman@nic.cz>
Fri, 16 May 2025 07:56:27 +0000 (09:56 +0200)
committerDaniel Salzman <daniel.salzman@nic.cz>
Mon, 2 Jun 2025 06:39:55 +0000 (08:39 +0200)
src/knot/dnssec/key-events.c
src/knot/dnssec/key-events.h
src/knot/dnssec/zone-events.c
src/knot/events/handlers/dnskey_sync.c
src/knot/events/handlers/dnssec.c
tests-extra/tests/dnssec/dnskey_sync/test.py

index 21d9e2ce51eb8ab8bce752de95f7e2663f080dea..35a8924c499d2ee70c0bb7c90770b4e2d6d75fb6 100644 (file)
@@ -23,6 +23,8 @@
 #include "knot/dnssec/key-events.h"
 #include "knot/dnssec/policy.h"
 #include "knot/dnssec/zone-keys.h"
+#include "knot/zone/serial.h"
+#include "libdnssec/random.h"
 
 static bool key_present(const kdnssec_ctx_t *ctx, bool ksk, bool zsk)
 {
@@ -951,3 +953,19 @@ bool zone_has_key_sbm(const kdnssec_ctx_t *ctx)
        }
        return false;
 }
+
+unsigned dnskey_sync_jitter(conf_t *conf, zone_t *zone)
+{
+       conf_val_t id = conf_zone_get(conf, C_DNSSEC_POLICY, zone->name);
+       conf_id_fix_default(&id);
+       conf_val_t val = conf_id_get(conf, C_POLICY, C_KEYTAG_MODULO, &id);
+       if (val.code == KNOT_EOK) {
+               int zero;
+               uint32_t rem, mod;
+               int ret = serial_modulo_parse(conf_str(&val), &rem, &mod, &zero);
+               if (ret == KNOT_EOK && mod > 1) {
+                       return 2 * rem;
+               }
+       }
+       return dnssec_random_uint16_t() % 5;
+}
index d216f90e360b23ed81de8e6194442f6e564c285a..1b5892fea00b4d9a82ec89e67b59463e53a8402b 100644 (file)
@@ -67,3 +67,16 @@ int knot_dnssec_ksk_sbm_confirm(kdnssec_ctx_t *ctx, uint32_t retire_delay);
  * \return False if there is no submitted key or if error; True otherwise
  */
 bool zone_has_key_sbm(const kdnssec_ctx_t *ctx);
+
+/*!
+ * \brief Returns jitter for dnskey-sync event schedulling
+ *
+ * The purpose of the jitter is to minimize a race condition during mutual
+ * synchronization.
+ *
+ * \param conf    Configuration.
+ * \param zon     Zone to compute jitter for.
+ *
+ * \return Jitter value.
+ */
+unsigned dnskey_sync_jitter(conf_t *conf, zone_t *zone);
index 754340d8ea99ff04763cab4c494d5af8d22bb327..71ea06d844600b629ab4e01519049cd4257555dd 100644 (file)
@@ -455,7 +455,9 @@ done:
                // NOTE: this is usually NOOP since signing planned earlier
                zone_events_schedule_at(update->zone, ZONE_EVENT_DNSSEC, (time_t)(next ? next : -1));
                if (ctx.policy->has_dnskey_sync) {
-                       zone_events_schedule_now(update->zone, ZONE_EVENT_DNSKEY_SYNC);
+                       unsigned jitter = dnskey_sync_jitter(conf, update->zone);
+                       zone_events_schedule_at(update->zone, ZONE_EVENT_DNSKEY_SYNC,
+                                               time(NULL) + jitter);
                }
                update->new_cont->dnssec_expire = knot_time_min(update->zone->contents->dnssec_expire, ctx.stats->expire);
        }
index fe602b9eed1b65a7475240db2eb272f4208f2d58..a3fd1ff0038550a71cd1fc686a53dd2aa58b046b 100644 (file)
@@ -349,7 +349,7 @@ static int send_dnskey_sync(conf_t *conf, zone_t *zone, bool *uptodate,
        }
 
        if (data.ddns_sent && ret == KNOT_ETIMEOUT) {
-               DNSKEY_SYNC_LOG(LOG_WARNING, zone->name, remote, requestor.layer.flags,
+               DNSKEY_SYNC_LOG(LOG_NOTICE, zone->name, remote, requestor.layer.flags,
                                "timed out, may be caused by parallel mutual DNSKEY sync, "
                                "may settle down after check-interval");
                ret = KNOT_EOK;
index 4b5a12dcbe18e58646493527346af1c6e79d3bf3..a07d45cd1e57f681318f9018f96348f7fa553491 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "knot/common/log.h"
 #include "knot/conf/conf.h"
+#include "knot/dnssec/key-events.h"
 #include "knot/dnssec/zone-events.h"
 #include "knot/updates/apply.h"
 #include "knot/zone/zone.h"
@@ -53,10 +54,11 @@ void event_dnssec_reschedule(conf_t *conf, zone_t *zone,
                zone->timers.next_ds_check = now;
        }
 
+       unsigned jitter = dnskey_sync_jitter(conf, zone);
        zone_events_schedule_at(zone,
                ZONE_EVENT_DNSSEC, refresh_at ? (time_t)refresh_at : ignore,
                ZONE_EVENT_DS_CHECK, refresh->plan_ds_check ? now : ignore,
-               ZONE_EVENT_DNSKEY_SYNC, refresh->plan_dnskey_sync ? now : ignore
+               ZONE_EVENT_DNSKEY_SYNC, refresh->plan_dnskey_sync ? now + jitter : ignore
        );
        if (zone_changed) {
                zone_schedule_notify(zone, 0);
index f14b1c16f3d085d9afecd3b1f114258d3c4a252f..5ac7af331465f305d472f0b51fe88977c37adb17 100644 (file)
@@ -112,10 +112,10 @@ if SIGNERS3:
     t.sleep(0.5)
     signer3.ctl("zone-key-rollover %s %s" % (zone[0].name, "zsk" if SIGNER2ROLL == 0 else "ksk"))
 
-t.sleep(6)
+t.sleep(8)
 check_same_dnskey(signer1, signer2, signer3, t)
 
-t.sleep(6)
+t.sleep(8)
 check_same_dnskey(signer1, signer2, signer3, t)
 
 t.end()