From 79f56845a5410c8a573bcea7f865c73ba53517de Mon Sep 17 00:00:00 2001 From: Libor Peltan Date: Wed, 19 Sep 2018 17:30:05 +0200 Subject: [PATCH] offline KSK: major redesign: KSR (only ZSKs) and SKR (DNSKEY, CDS, RRSIG) --- doc/man/knot.conf.5in | 6 + doc/operation.rst | 15 +- doc/reference.rst | 10 + src/knot/conf/schema.c | 1 + src/knot/conf/schema.h | 1 + src/knot/dnssec/context.c | 4 + src/knot/dnssec/context.h | 2 + src/knot/dnssec/kasp/kasp_db.c | 69 +++++-- src/knot/dnssec/kasp/kasp_db.h | 6 +- src/knot/dnssec/kasp/policy.h | 1 + src/knot/dnssec/key-events.c | 12 +- src/knot/dnssec/zone-events.c | 5 +- src/knot/dnssec/zone-keys.c | 8 +- src/knot/dnssec/zone-sign.c | 73 +++---- src/knot/dnssec/zone-sign.h | 3 +- src/knot/journal/serialization.c | 9 +- src/utils/keymgr/offline_ksk.c | 182 +++++++++++++++--- tests-extra/tests/dnssec/offline_ksk/test.py | 116 ----------- tests-extra/tests/dnssec/offline_ksk2/test.py | 30 +-- tests-extra/tools/dnstest/server.py | 2 + 20 files changed, 311 insertions(+), 244 deletions(-) delete mode 100644 tests-extra/tests/dnssec/offline_ksk/test.py diff --git a/doc/man/knot.conf.5in b/doc/man/knot.conf.5in index cf003a2923..4b651beba3 100644 --- a/doc/man/knot.conf.5in +++ b/doc/man/knot.conf.5in @@ -620,6 +620,7 @@ policy: nsec3\-salt\-lifetime: TIME ksk\-submission: submission_id cds\-cdnskey\-publish: none | delete\-dnssec | rollover | always + offline\-ksk: BOOL .ft P .fi .UNINDENT @@ -822,6 +823,11 @@ Possible values: .UNINDENT .sp \fIDefault:\fP always +.SS offline\-ksk +.sp +Specifies if Offline KSK feature is enabled. +.sp +\fIDefault:\fP off .SH REMOTE SECTION .sp Definitions of remote servers for outgoing connections (source of a zone diff --git a/doc/operation.rst b/doc/operation.rst index 7d6c68acf8..1a9040e678 100644 --- a/doc/operation.rst +++ b/doc/operation.rst @@ -697,24 +697,23 @@ human-readable. For the "ZSK side" (i.e. the operator of the DNS server), the pre-requirements are similar to pervious case: proper DNSSEC configuration with :ref:`zsk-lifetime ` -but :ref:`manual `, a complete KASP db with the public part of the KSK -(or multiple KSKs if in the middle of a rollover), but without its private part. +but :ref:`manual `, a complete KASP db with just ZSKs. Also +:ref:`offline-ksk ` must be enabled. For the "KSK side" (i.e. the operator of the KSK signer), the pre-requirements are equal Knot configuration (at least the :ref:`Policy section` must be identical) and a KASP db -with the KSK(s). The timers for the KSKs must be identical on both sides! The ZSKs are not -necessary here, but their public parts might be in the KASP db too. +with the KSK(s). The first step for the "ZSK side" is the same as previously: ``keymgr pregenerate``. Then, the operator shall export the public parts of the future ZSKs (in facts in the form of -complete future DNSKEY records) by calling ``keymgr generate-ksr``. The output +future DNSKEY records) by calling ``keymgr generate-ksr``. The output (called Key Signing Request; perhaps redirected to a file) shall be then sent to the "KSK side" e.g. via e-mail. The third step is done by "KSK side" by using the command ``keymgr sign-ksr`` with -the KSR file as the parameter. This signs all the future forms of DNSKEY record, -creating the future RRSIGs, which are again printed on output (and called Signed Key -Response). This shall be sent back to "ZSK side". +the KSR file as the parameter. This completes and signs all the future forms of DNSKEY record, +creating the future RRSIGs (adding also CDNSKEYs and CDSs), which are again printed on output +(and called Signed Key Response). This shall be sent back to "ZSK side". The last step is importing the signatures from SKR to the KASP db for later use, this is done by ``keymgr import-skr``, followed by (as previously) ``knotc zone-resign`` diff --git a/doc/reference.rst b/doc/reference.rst index 93bca65600..f57633cb6f 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -684,6 +684,7 @@ DNSSEC policy configuration. nsec3-salt-lifetime: TIME ksk-submission: submission_id cds-cdnskey-publish: none | delete-dnssec | rollover | always + offline-ksk: BOOL .. _policy_id: @@ -924,6 +925,15 @@ Possible values: *Default:* always +.. _policy_offline-ksk: + +offline-ksk +----------- + +Specifies if Offline KSK feature is enabled. + +*Default:* off + .. _Remote section: Remote section diff --git a/src/knot/conf/schema.c b/src/knot/conf/schema.c index 829c83ed9a..df21a966e5 100644 --- a/src/knot/conf/schema.c +++ b/src/knot/conf/schema.c @@ -268,6 +268,7 @@ static const yp_item_t desc_policy[] = { { C_KSK_SBM, YP_TREF, YP_VREF = { C_SBM }, CONF_IO_FRLD_ZONES, { check_ref } }, { C_CHILD_RECORDS, YP_TOPT, YP_VOPT = { child_record, CHILD_RECORDS_ALWAYS } }, + { C_OFFLINE_KSK, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES }, { C_COMMENT, YP_TSTR, YP_VNONE }, { NULL } }; diff --git a/src/knot/conf/schema.h b/src/knot/conf/schema.h index 5725ad8146..23fe0cc4aa 100644 --- a/src/knot/conf/schema.h +++ b/src/knot/conf/schema.h @@ -81,6 +81,7 @@ #define C_NSEC3_SALT_LEN "\x11""nsec3-salt-length" #define C_NSEC3_SALT_LIFETIME "\x13""nsec3-salt-lifetime" #define C_NSID "\x04""nsid" +#define C_OFFLINE_KSK "\x0B""offline-ksk" #define C_PARENT "\x06""parent" #define C_PIDFILE "\x07""pidfile" #define C_POLICY "\x06""policy" diff --git a/src/knot/dnssec/context.c b/src/knot/dnssec/context.c index ed1429f4db..6686399036 100644 --- a/src/knot/dnssec/context.c +++ b/src/knot/dnssec/context.c @@ -112,6 +112,9 @@ static void policy_load(knot_kasp_policy_t *policy, conf_val_t *id) conf_val_next(&val); } } + + val = conf_id_get(conf(), C_POLICY, C_OFFLINE_KSK, id); + policy->offline_ksk = conf_bool(&val); } int kdnssec_ctx_init(conf_t *conf, kdnssec_ctx_t *ctx, const knot_dname_t *zone_name, @@ -209,6 +212,7 @@ void kdnssec_ctx_deinit(kdnssec_ctx_t *ctx) } free(ctx->policy); } + knot_rrset_free(ctx->offline_rrsig, NULL); dnssec_keystore_deinit(ctx->keystore); kasp_zone_free(&ctx->zone); free(ctx->kasp_zone_path); diff --git a/src/knot/dnssec/context.h b/src/knot/dnssec/context.h index 795c1235fb..560b449ffc 100644 --- a/src/knot/dnssec/context.h +++ b/src/knot/dnssec/context.h @@ -40,6 +40,8 @@ typedef struct { bool rrsig_drop_existing; bool keep_deleted_keys; bool rollover_only_zsk; + + knot_rrset_t *offline_rrsig; } kdnssec_ctx_t; /*! diff --git a/src/knot/dnssec/kasp/kasp_db.c b/src/knot/dnssec/kasp/kasp_db.c index f96aea3ef8..e15cc5eb50 100644 --- a/src/knot/dnssec/kasp/kasp_db.c +++ b/src/knot/dnssec/kasp/kasp_db.c @@ -757,16 +757,19 @@ static void for_time2string(char str[21], knot_time_t t) snprintf(str, 21, "%020lu", t); } -int kasp_db_store_offline_rrsig(kasp_db_t *db, knot_time_t for_time, const knot_rrset_t *rrsig) +int kasp_db_store_offline_rrsig(kasp_db_t *db, knot_time_t for_time, const knot_rrset_t *rrsig, const knot_rrset_t *dnskey, + const knot_rrset_t *cdnskey, const knot_rrset_t *cds) { - if (db == NULL || rrsig == NULL || rrsig->type != KNOT_RRTYPE_RRSIG) { + if (db == NULL || rrsig == NULL || rrsig->type != KNOT_RRTYPE_RRSIG || dnskey == NULL || dnskey->type != KNOT_RRTYPE_DNSKEY || + cdnskey == NULL || cdnskey->type != KNOT_RRTYPE_CDNSKEY || cds == NULL || cds->type != KNOT_RRTYPE_CDS) { return KNOT_EINVAL; } char for_time_str[21]; for_time2string(for_time_str, for_time); knot_db_val_t key = make_key(KASPDBKEY_OFFLINE_RRSIG, rrsig->owner, for_time_str), val; - val.len = rrset_serialized_size(rrsig); + val.len = rrset_serialized_size(rrsig) + rrset_serialized_size(dnskey) + + rrset_serialized_size(cdnskey) + rrset_serialized_size(cds); val.data = malloc(val.len); if (val.data == NULL) { free_key(&key); @@ -775,6 +778,15 @@ int kasp_db_store_offline_rrsig(kasp_db_t *db, knot_time_t for_time, const knot_ with_txn(KEYS_RW, NULL); wire_ctx_t wire = wire_ctx_init(val.data, val.len); ret = serialize_rrset(&wire, rrsig); + if (ret == KNOT_EOK) { + ret = serialize_rrset(&wire, dnskey); + } + if (ret == KNOT_EOK) { + ret = serialize_rrset(&wire, cdnskey); + } + if (ret == KNOT_EOK) { + ret = serialize_rrset(&wire, cds); + } if (ret == KNOT_EOK) { ret = db_api->insert(txn, &key, &val, 0); } @@ -783,25 +795,56 @@ int kasp_db_store_offline_rrsig(kasp_db_t *db, knot_time_t for_time, const knot_ return ret; } -int kasp_db_load_offline_rrsig(kasp_db_t *db, const knot_dname_t *for_dname, knot_time_t for_time, knot_rrset_t *rrsig) +int kasp_db_load_offline_rrsig(kasp_db_t *db, const knot_dname_t *for_dname, knot_time_t for_time, knot_time_t *next_time, + knot_rrset_t *rrsig, knot_rrset_t *dnskey, knot_rrset_t *cdnskey, knot_rrset_t *cds) { - if (db == NULL || rrsig == NULL) { + if (db == NULL || rrsig == NULL || dnskey == NULL || cdnskey == NULL || cds == NULL) { return KNOT_EINVAL; } char for_time_str[21]; for_time2string(for_time_str, for_time); with_txn(KEYS_RO, NULL); - knot_db_val_t key = make_key(KASPDBKEY_OFFLINE_RRSIG, for_dname, for_time_str), val; - ret = db_api->find(txn, &key, &val, KNOT_DB_LEQ); - if (ret == KNOT_EOK) { - wire_ctx_t wire = wire_ctx_init(val.data, val.len); - ret = deserialize_rrset(&wire, rrsig); - if (ret == KNOT_EOK && knot_dname_cmp(rrsig->owner, for_dname) != 0) { - ret = KNOT_ENOENT; + knot_db_val_t search = make_key(KASPDBKEY_OFFLINE_RRSIG, for_dname, for_time_str), key, val; + knot_db_iter_t *it = db_api->iter_begin(txn, KNOT_DB_NOOP); + if (it == NULL) { + ret = KNOT_ERROR; + goto cleanup; + } + it = db_api->iter_seek(it, &search, KNOT_DB_LEQ); + if (it == NULL) { + ret = KNOT_ENOENT; + goto cleanup; + } + if (db_api->iter_key(it, &key) != KNOT_EOK || db_api->iter_val(it, &val) != KNOT_EOK) { + ret = KNOT_ERROR; + goto cleanup; + } + if (knot_dname_cmp((const knot_dname_t *)key.data + 1, rrsig->owner) != 0) { + ret = KNOT_ENOENT; + goto cleanup; + } + wire_ctx_t wire = wire_ctx_init(val.data, val.len); +#define CHK_RET if (ret != KNOT_EOK) goto cleanup; + ret = deserialize_rrset(&wire, rrsig); + CHK_RET + ret = deserialize_rrset(&wire, dnskey); + CHK_RET + ret = deserialize_rrset(&wire, cdnskey); + CHK_RET + ret = deserialize_rrset(&wire, cds); + CHK_RET +#undef CHK_RET + *next_time = 0; + if ((it = db_api->iter_next(it)) != NULL && db_api->iter_key(it, &key) == KNOT_EOK) { + const knot_dname_t *next_name = (const knot_dname_t *)key.data + 1; + if (knot_dname_cmp(next_name, rrsig->owner) == 0) { + *next_time = atol((char *)key.data + 1 + knot_dname_size(next_name)); } } - free_key(&key); +cleanup: + db_api->iter_finish(it); + free_key(&search); with_txn_end(NULL); return ret; } diff --git a/src/knot/dnssec/kasp/kasp_db.h b/src/knot/dnssec/kasp/kasp_db.h index d09be132bf..c022c87232 100644 --- a/src/knot/dnssec/kasp/kasp_db.h +++ b/src/knot/dnssec/kasp/kasp_db.h @@ -243,7 +243,8 @@ int kasp_db_list_zones(kasp_db_t *db, list_t *dst); * * \return KNOT_E* */ -int kasp_db_store_offline_rrsig(kasp_db_t *db, knot_time_t for_time, const knot_rrset_t *rrsig); +int kasp_db_store_offline_rrsig(kasp_db_t *db, knot_time_t for_time, const knot_rrset_t *rrsig, + const knot_rrset_t *dnskey, const knot_rrset_t *cdnskey, const knot_rrset_t *cds); /*! * \brief Load pregenerated RRSIG. @@ -255,7 +256,8 @@ int kasp_db_store_offline_rrsig(kasp_db_t *db, knot_time_t for_time, const knot_ * * \return KNOT_E* */ -int kasp_db_load_offline_rrsig(kasp_db_t *db, const knot_dname_t *for_dname, knot_time_t for_time, knot_rrset_t *rrsig); +int kasp_db_load_offline_rrsig(kasp_db_t *db, const knot_dname_t *for_dname, knot_time_t for_time, knot_time_t *next_time, + knot_rrset_t *rrsig, knot_rrset_t *dnskey, knot_rrset_t *cdnskey, knot_rrset_t *cds); /*! * \brief Delete pregenerated RRSIGs from beginning to the specified timestamp. diff --git a/src/knot/dnssec/kasp/policy.h b/src/knot/dnssec/kasp/policy.h index 0628d71bee..f1374c53be 100644 --- a/src/knot/dnssec/kasp/policy.h +++ b/src/knot/dnssec/kasp/policy.h @@ -108,5 +108,6 @@ typedef struct { uint32_t ksk_sbm_check_interval; unsigned child_records_publish; parent_dynarray_t parents; + bool offline_ksk; } knot_kasp_policy_t; // TODO make the time parameters knot_timediff_t ?? diff --git a/src/knot/dnssec/key-events.c b/src/knot/dnssec/key-events.c index 2695993c9f..1b69c68464 100644 --- a/src/knot/dnssec/key-events.c +++ b/src/knot/dnssec/key-events.c @@ -584,12 +584,14 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_roll_flags_t flags, bool allowed_general_roll = ((flags & KEY_ROLL_ALLOW_KSK_ROLL) && (flags & KEY_ROLL_ALLOW_ZSK_ROLL)); // generate initial keys if missing if (!key_present(ctx, true, false) && !key_present(ctx, true, true)) { - if (ctx->policy->ksk_shared) { - ret = share_or_generate_key(ctx, GEN_KSK_FLAGS, ctx->now, false); - } else { - ret = generate_key(ctx, GEN_KSK_FLAGS, ctx->now, false); + if (!ctx->rollover_only_zsk) { + if (ctx->policy->ksk_shared) { + ret = share_or_generate_key(ctx, GEN_KSK_FLAGS, ctx->now, false); + } else { + ret = generate_key(ctx, GEN_KSK_FLAGS, ctx->now, false); + } + reschedule->plan_ds_query = true; } - reschedule->plan_ds_query = true; if (ret == KNOT_EOK) { reschedule->keys_changed = true; if (!ctx->policy->singe_type_signing && diff --git a/src/knot/dnssec/zone-events.c b/src/knot/dnssec/zone-events.c index 01017791cf..bf6fc8e24e 100644 --- a/src/knot/dnssec/zone-events.c +++ b/src/knot/dnssec/zone-events.c @@ -170,7 +170,8 @@ int knot_dnssec_zone_sign(zone_update_t *update, log_zone_info(zone_name, "DNSSEC, signing started"); - result = knot_zone_sign_update_dnskeys(update, &keyset, &ctx); + knot_time_t next_resign = 0; + result = knot_zone_sign_update_dnskeys(update, &keyset, &ctx, &next_resign); if (result != KNOT_EOK) { log_zone_error(zone_name, "DNSSEC, failed to update DNSKEY records (%s)", knot_strerror(result)); @@ -217,7 +218,7 @@ int knot_dnssec_zone_sign(zone_update_t *update, done: if (result == KNOT_EOK) { - reschedule->next_sign = schedule_next(&ctx, &keyset, zone_expire); + reschedule->next_sign = schedule_next(&ctx, &keyset, knot_time_min(zone_expire, next_resign)); } free_zone_keys(&keyset); diff --git a/src/knot/dnssec/zone-keys.c b/src/knot/dnssec/zone-keys.c index 4c06804da2..826063051f 100644 --- a/src/knot/dnssec/zone-keys.c +++ b/src/knot/dnssec/zone-keys.c @@ -288,6 +288,12 @@ static int walk_algorithms(kdnssec_ctx_t *ctx, zone_keyset_t *keyset) case 15: // all keys ready for signing have_active_alg = true; break; + case 10: + if (ctx->policy->offline_ksk) { + have_active_alg = true; + break; + } + // else FALLTHROUGH default: return KNOT_DNSSEC_EMISSINGKEYTYPE; } @@ -323,8 +329,6 @@ static int load_private_keys(dnssec_keystore_t *keystore, zone_keyset_t *keyset) case DNSSEC_EOK: case DNSSEC_KEY_ALREADY_PRESENT: break; - case DNSSEC_ENOENT: // we hope that this is just offline KSK - break; default: return r; } diff --git a/src/knot/dnssec/zone-sign.c b/src/knot/dnssec/zone-sign.c index d9e9488d9a..5a03752e77 100644 --- a/src/knot/dnssec/zone-sign.c +++ b/src/knot/dnssec/zone-sign.c @@ -277,28 +277,6 @@ static int remove_expired_rrsigs(const knot_rrset_t *covered, return result; } -static bool can_have_offline_rrsig(const knot_rrset_t *rr, const knot_dname_t *zone_apex) -{ - return (rr->type == KNOT_RRTYPE_DNSKEY && knot_dname_cmp(rr->owner, zone_apex) == 0); -} - -static int load_offline_rrsig(const knot_rrset_t *covered, - knot_rrset_t *rrsig, - const kdnssec_ctx_t *ctx) -{ - knot_rrset_init_empty(rrsig); - - if (!can_have_offline_rrsig(covered, ctx->zone->dname)) { - return KNOT_EOK; - } - - int ret = kasp_db_load_offline_rrsig(*ctx->kasp_db, covered->owner, ctx->now, rrsig); - if (ret == KNOT_ENOENT) { - ret = KNOT_EOK; - } - return ret; -} - /*! * \brief Add missing RRSIGs into the changeset for adding. * @@ -322,12 +300,14 @@ static int add_missing_rrsigs(const knot_rrset_t *covered, assert(zone_keys); assert(changeset); - knot_rrset_t to_add, offline_rrsigs; + int result = KNOT_EOK; + knot_rrset_t to_add; knot_rrset_init_empty(&to_add); - int result = load_offline_rrsig(covered, &offline_rrsigs, dnssec_ctx); - if (result != KNOT_EOK) { - log_zone_warning(dnssec_ctx->zone->dname, "DNSSEC, failed to load offline DNSKEY RRSIG (%s)", - knot_strerror(result)); + + if (covered->type == KNOT_RRTYPE_DNSKEY && + knot_dname_cmp(covered->owner, dnssec_ctx->zone->dname) == 0 && + dnssec_ctx->offline_rrsig != NULL) { + return changeset_add_addition(changeset, dnssec_ctx->offline_rrsig, CHANGESET_CHECK); } for (int i = 0; i < zone_keys->count; i++) { @@ -344,17 +324,6 @@ static int add_missing_rrsigs(const knot_rrset_t *covered, to_add = create_empty_rrsigs_for(covered); } - uint16_t at_offline; - if (valid_signature_exists(covered, &offline_rrsigs, key->key, key->ctx, dnssec_ctx, &at_offline)) { - log_zone_info(dnssec_ctx->zone->dname, "DNSSEC, using offline DNSKEY RRSIG"); - knot_rdata_t *offline_rd = knot_rdataset_at(&offline_rrsigs.rrs, at_offline); - result = knot_rrset_add_rdata(&to_add, offline_rd->data, offline_rd->len, NULL); - if (result != KNOT_EOK) { - break; - } - continue; - } - result = knot_sign_rrset(&to_add, covered, key->key, key->ctx, dnssec_ctx, NULL, expires_at); if (result != KNOT_EOK) { @@ -367,7 +336,6 @@ static int add_missing_rrsigs(const knot_rrset_t *covered, } knot_rdataset_clear(&to_add.rrs, NULL); - knot_rrset_clear(&offline_rrsigs, NULL); return result; } @@ -973,7 +941,8 @@ int knot_zone_sign_add_dnskeys(zone_keyset_t *zone_keys, const kdnssec_ctx_t *dn int knot_zone_sign_update_dnskeys(zone_update_t *update, zone_keyset_t *zone_keys, - const kdnssec_ctx_t *dnssec_ctx) + kdnssec_ctx_t *dnssec_ctx, + knot_time_t *next_resign) { if (update == NULL || zone_keys == NULL || dnssec_ctx == NULL) { return KNOT_EINVAL; @@ -986,6 +955,7 @@ int knot_zone_sign_update_dnskeys(zone_update_t *update, knot_rrset_t *add_dnskeys = NULL; knot_rrset_t *add_cdnskeys = NULL; knot_rrset_t *add_cdss = NULL; + knot_rrset_t *add_rrsigs = NULL; uint32_t dnskey_ttl = dnssec_ctx->policy->dnskey_ttl; knot_rrset_t soa = node_rrset(apex, KNOT_RRTYPE_SOA); if (knot_rrset_empty(&soa)) { @@ -1015,12 +985,25 @@ int knot_zone_sign_update_dnskeys(zone_update_t *update, 0, NULL); add_cdss = knot_rrset_new(apex->owner, KNOT_RRTYPE_CDS, soa.rclass, 0, NULL); - if (add_dnskeys == NULL || add_cdnskeys == NULL || add_cdss == NULL) { + add_rrsigs = knot_rrset_new(apex->owner, KNOT_RRTYPE_RRSIG, soa.rclass, + dnskey_ttl, NULL); + if (add_dnskeys == NULL || add_cdnskeys == NULL || add_cdss == NULL || add_rrsigs == NULL) { ret = KNOT_ENOMEM; CHECK_RET; } - ret = knot_zone_sign_add_dnskeys(zone_keys, dnssec_ctx, add_dnskeys, add_cdnskeys, add_cdss); + if (dnssec_ctx->policy->offline_ksk) { + ret = kasp_db_load_offline_rrsig(*dnssec_ctx->kasp_db, apex->owner, dnssec_ctx->now, next_resign, + add_rrsigs, add_dnskeys, add_cdnskeys, add_cdss); + if (ret == KNOT_EOK) { + log_zone_info(dnssec_ctx->zone->dname, "DNSSEC, using offline DNSKEY RRSIG"); + } else { + log_zone_warning(dnssec_ctx->zone->dname, "DNSSEC, failed to load offline DNSKEY RRSIG (%s)", + knot_strerror(ret)); + } + } else { + ret = knot_zone_sign_add_dnskeys(zone_keys, dnssec_ctx, add_dnskeys, add_cdnskeys, add_cdss); + } CHECK_RET; if (!knot_rrset_empty(add_cdnskeys)) { @@ -1041,6 +1024,11 @@ int knot_zone_sign_update_dnskeys(zone_update_t *update, CHECK_RET; } + if (!knot_rrset_empty(add_rrsigs)) { + dnssec_ctx->offline_rrsig = add_rrsigs; + add_rrsigs = NULL; // prevent free + } + ret = zone_update_apply_changeset(update, &ch); #undef CHECK_RET @@ -1049,6 +1037,7 @@ cleanup: knot_rrset_free(add_dnskeys, NULL); knot_rrset_free(add_cdnskeys, NULL); knot_rrset_free(add_cdss, NULL); + knot_rrset_free(add_rrsigs, NULL); changeset_clear(&ch); return ret; } diff --git a/src/knot/dnssec/zone-sign.h b/src/knot/dnssec/zone-sign.h index 4c6a231569..38472baea4 100644 --- a/src/knot/dnssec/zone-sign.h +++ b/src/knot/dnssec/zone-sign.h @@ -49,7 +49,8 @@ int knot_zone_sign_add_dnskeys(zone_keyset_t *zone_keys, const kdnssec_ctx_t *dn */ int knot_zone_sign_update_dnskeys(zone_update_t *update, zone_keyset_t *zone_keys, - const kdnssec_ctx_t *dnssec_ctx); + kdnssec_ctx_t *dnssec_ctx, + knot_time_t *next_resign); /*! * \brief Check if key can be used to sign given RR. diff --git a/src/knot/journal/serialization.c b/src/knot/journal/serialization.c index d764fe606c..fd307111ab 100644 --- a/src/knot/journal/serialization.c +++ b/src/knot/journal/serialization.c @@ -429,6 +429,13 @@ int deserialize_rrset(wire_ctx_t *wire, knot_rrset_t *rrset) if (wire->error != KNOT_EOK) { return wire->error; } + if (rrset->owner != NULL) { + if (knot_dname_cmp(owner, rrset->owner) != 0) { + knot_dname_free(owner, NULL); + return KNOT_ESEMCHECK; + } + knot_rrset_clear(rrset, NULL); + } knot_rrset_init(rrset, owner, type, rclass, 0); for (size_t phase = 0; phase < rrcount && wire_ctx_available(wire) > 0; phase++) { @@ -452,7 +459,7 @@ int deserialize_rrset(wire_ctx_t *wire, knot_rrset_t *rrset) size_t rrset_serialized_size(const knot_rrset_t *rrset) { - if (rrset == NULL || rrset->rrs.count == 0) { + if (rrset == NULL) { return 0; } diff --git a/src/utils/keymgr/offline_ksk.c b/src/utils/keymgr/offline_ksk.c index d9d0b1fc85..d62b37ac9c 100644 --- a/src/utils/keymgr/offline_ksk.c +++ b/src/utils/keymgr/offline_ksk.c @@ -127,7 +127,7 @@ static int presign_once(kdnssec_ctx_t *ctx) // store it to KASP db assert(!knot_rrset_empty(rrsig)); - ret = kasp_db_store_offline_rrsig(*ctx->kasp_db, ctx->now, rrsig); + ret = kasp_db_store_offline_rrsig(*ctx->kasp_db, ctx->now, rrsig, NULL, NULL, NULL); // TODO ! if (ret != KNOT_EOK) { printf("store rrsig failed\n"); goto done; @@ -186,20 +186,41 @@ static int dump_rrset_to_buf(const knot_rrset_t *rrset, char **buf, size_t *buf_ int keymgr_print_rrsig(kdnssec_ctx_t *ctx, knot_time_t when) { - knot_rrset_t rrsig = { 0 }; - knot_rrset_init_empty(&rrsig); - int ret = kasp_db_load_offline_rrsig(*ctx->kasp_db, ctx->zone->dname, when, &rrsig); + knot_time_t next = 0; + knot_rrset_t rrsig = { 0 }, dnskey = { 0 }, cdnskey = { 0 }, cds = { 0 }; + knot_rrset_init(&rrsig, knot_dname_copy(ctx->zone->dname, NULL), + KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, ctx->policy->dnskey_ttl); + knot_rrset_init_empty(&dnskey); + knot_rrset_init_empty(&cdnskey); + knot_rrset_init_empty(&cds); + int ret = kasp_db_load_offline_rrsig(*ctx->kasp_db, ctx->zone->dname, when, &next, &rrsig, &dnskey, &cdnskey, &cds); if (ret == KNOT_EOK) { char *buf = NULL; size_t buf_size = 512; ret = dump_rrset_to_buf(&rrsig, &buf, &buf_size); + if (ret >= 0) { + printf("%s", buf); + ret = dump_rrset_to_buf(&dnskey, &buf, &buf_size); + } + if (ret >= 0) { + printf("%s", buf); + ret = dump_rrset_to_buf(&cdnskey, &buf, &buf_size); + } + if (ret >= 0) { + printf("%s", buf); + ret = dump_rrset_to_buf(&cds, &buf, &buf_size); + } if (ret >= 0) { printf("%s", buf); ret = KNOT_EOK; } free(buf); + printf("; next %lu\n", next); } knot_rrset_clear(&rrsig, NULL); + knot_rrset_clear(&dnskey, NULL); + knot_rrset_clear(&cdnskey, NULL); + knot_rrset_clear(&cds, NULL); return ret; } @@ -227,7 +248,7 @@ static void print_generated_message() printf("generated on %s by KnotDNS %s\n", buf, VERSION); } -static int ksr_once(kdnssec_ctx_t *ctx, char **buf, size_t *buf_size) +static int ksr_once(kdnssec_ctx_t *ctx, char **buf, size_t *buf_size, knot_time_t *next_ksr) { knot_rrset_t *dnskey = NULL; zone_keyset_t keyset = { 0 }; @@ -243,6 +264,9 @@ static int ksr_once(kdnssec_ctx_t *ctx, char **buf, size_t *buf_size) } done: + if (ret == KNOT_EOK && next_ksr != NULL) { + *next_ksr = knot_get_next_zone_key_event(&keyset); + } knot_rrset_free(dnskey, NULL); free_zone_keys(&keyset); return ret; @@ -255,11 +279,18 @@ int keymgr_print_ksr(kdnssec_ctx_t *ctx, knot_time_t upto) char *buf = NULL; size_t buf_size = 4096; - while (ret == KNOT_EOK && knot_time_cmp(next, upto) <= 0) { + while (ret == KNOT_EOK && knot_time_cmp(next, upto) < 0) { ctx->now = next; - ret = ksr_once(ctx, &buf, &buf_size); - next_resign(&next, ctx); + ret = ksr_once(ctx, &buf, &buf_size, &next); } + if (ret != KNOT_EOK) { + free(buf); + return ret; + } + ctx->now = upto; + // force end of period as a KSR timestamp + ret = ksr_once(ctx, &buf, &buf_size, NULL); + printf(";; KeySigningRequest "); print_generated_message(); @@ -268,11 +299,15 @@ int keymgr_print_ksr(kdnssec_ctx_t *ctx, knot_time_t upto) } typedef struct { - knot_rrset_t *rr; + knot_rrset_t *dnskey; + knot_rrset_t *dnskey_prev; + knot_rrset_t *cdnskey; + knot_rrset_t *cds; + knot_rrset_t *rrsig; kdnssec_ctx_t *kctx; } ksr_sign_ctx_t; -static int ksr_sign_dnskey(kdnssec_ctx_t *ctx, knot_rrset_t *dnskey) +static int ksr_sign_dnskey(kdnssec_ctx_t *ctx, knot_rrset_t *zsk, knot_time_t *next_sign) { zone_keyset_t keyset = { 0 }; char *buf = NULL; @@ -281,11 +316,20 @@ static int ksr_sign_dnskey(kdnssec_ctx_t *ctx, knot_rrset_t *dnskey) if (ret != KNOT_EOK) { return ret; } - knot_rrset_t *rrsig = knot_rrset_new(ctx->zone->dname, KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, ctx->policy->dnskey_ttl, NULL); - if (rrsig == NULL) { + knot_rrset_t *dnskey = knot_rrset_copy(zsk, NULL); + knot_rrset_t *cdnskey = knot_rrset_new(zsk->owner, KNOT_RRTYPE_CDNSKEY, zsk->rclass, 0, NULL); + knot_rrset_t *cds = knot_rrset_new(zsk->owner, KNOT_RRTYPE_CDS, zsk->rclass, 0, NULL); + knot_rrset_t *rrsig = knot_rrset_new(zsk->owner, KNOT_RRTYPE_RRSIG, zsk->rclass, zsk->ttl, NULL); + if (dnskey == NULL || cdnskey == NULL || cds == NULL || rrsig == NULL) { ret = KNOT_ENOMEM; goto done; } + + ret = knot_zone_sign_add_dnskeys(&keyset, ctx, dnskey, cdnskey, cds); + if (ret != KNOT_EOK) { + goto done; + } + // no check if the KSK used for signing (in keyset) is contained in DNSKEY record being signed (in KSR) ! for (int i = 0; i < keyset.count; i++) { zone_key_t *key = &keyset.keys[i]; @@ -296,49 +340,116 @@ static int ksr_sign_dnskey(kdnssec_ctx_t *ctx, knot_rrset_t *dnskey) } } } - ret = dump_rrset_to_buf(rrsig, &buf, &buf_size); + printf(";;SKR ===========\n"); + ret = dump_rrset_to_buf(dnskey, &buf, &buf_size); + if (ret >= 0) { + printf("%s", buf); + ret = dump_rrset_to_buf(cdnskey, &buf, &buf_size); + } + if (ret >= 0) { + printf("%s", buf); + ret = dump_rrset_to_buf(cds, &buf, &buf_size); + } + if (ret >= 0) { + printf("%s", buf); + ret = dump_rrset_to_buf(rrsig, &buf, &buf_size); + } if (ret >= 0) { buf[strlen(buf) - 1] = '\0'; // remove trailing newline - printf(";;SKR %lu %hu %d\n%s ; end SKR %lu\n", ctx->now, rrsig->rrs.count, ret, buf, ctx->now); + printf("%s ; end SKR %lu\n", buf, ctx->now); ret = KNOT_EOK; } done: free(buf); + knot_rrset_free(dnskey, NULL); + knot_rrset_free(cdnskey, NULL); + knot_rrset_free(cds, NULL); knot_rrset_free(rrsig, NULL); + if (ret == KNOT_EOK) { + *next_sign = knot_get_next_zone_key_event(&keyset); + } free_zone_keys(&keyset); return ret; } +static int process_skr_between_ksrs(ksr_sign_ctx_t *ctx, knot_time_t next_ksr) +{ + static knot_time_t prev_ksr = 0; + + assert((prev_ksr == 0 ? 1 : 0) == (knot_rrset_empty(ctx->dnskey_prev) ? 1 : 0)); + + if (!knot_rrset_empty(ctx->dnskey_prev)) { + for (knot_time_t inbetween_skr = prev_ksr; inbetween_skr < next_ksr; ) { + ctx->kctx->now = inbetween_skr; + int ret = ksr_sign_dnskey(ctx->kctx, ctx->dnskey_prev, &inbetween_skr); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + // finally, do the "requested" SKR and copy ctx->dnskey into ct->rrsig + ctx->kctx->now = next_ksr; + int ret = ksr_sign_dnskey(ctx->kctx, ctx->dnskey, &prev_ksr); + if (ret != KNOT_EOK) { + return ret; + } + knot_rdataset_clear(&ctx->dnskey_prev->rrs, NULL); + ret = knot_rdataset_copy(&ctx->dnskey_prev->rrs, &ctx->dnskey->rrs, NULL); + return ret; +} + static void ksr_sign_once(zs_scanner_t *sc) { ksr_sign_ctx_t *ctx = sc->process.data; - sc->error.code = knot_rrset_add_rdata(ctx->rr, sc->r_data, sc->r_data_length, NULL); - ctx->rr->ttl = sc->r_ttl; + sc->error.code = knot_rrset_add_rdata(ctx->dnskey, sc->r_data, sc->r_data_length, NULL); + ctx->dnskey->ttl = sc->r_ttl; if (sc->error.code == KNOT_EOK && sc->buffer_length > 9 && strncmp((const char *)sc->buffer, " end KSR ", 9) == 0) { - ctx->kctx->now = atol((const char *)sc->buffer + 9); - sc->error.code = ksr_sign_dnskey(ctx->kctx, ctx->rr); - knot_rdataset_clear(&ctx->rr->rrs, NULL); + knot_time_t next_ksr = atol((const char *)sc->buffer + 9); + sc->error.code = process_skr_between_ksrs(ctx, next_ksr); + knot_rdataset_clear(&ctx->dnskey->rrs, NULL); } } static void skr_import_once(zs_scanner_t *sc) { ksr_sign_ctx_t *ctx = sc->process.data; + knot_rrset_t *to_add = NULL; + switch (sc->r_type) { + case KNOT_RRTYPE_DNSKEY: + to_add = ctx->dnskey; + break; + case KNOT_RRTYPE_CDNSKEY: + to_add = ctx->cdnskey; + break; + case KNOT_RRTYPE_CDS: + to_add = ctx->cds; + break; + case KNOT_RRTYPE_RRSIG: + to_add = ctx->rrsig; + break; + default: + sc->error.code = KNOT_EINVAL; + return; + } - sc->error.code = knot_rrset_add_rdata(ctx->rr, sc->r_data, sc->r_data_length, NULL); - ctx->rr->ttl = sc->r_ttl; + sc->error.code = knot_rrset_add_rdata(to_add, sc->r_data, sc->r_data_length, NULL); + to_add->ttl = sc->r_ttl; if (sc->error.code == KNOT_EOK && sc->buffer_length > 9 && strncmp((const char *)sc->buffer, " end SKR ", 9) == 0) { knot_time_t for_time = atol((const char *)sc->buffer + 9); - sc->error.code = kasp_db_store_offline_rrsig(*ctx->kctx->kasp_db, for_time, ctx->rr); - knot_rdataset_clear(&ctx->rr->rrs, NULL); + sc->error.code = kasp_db_store_offline_rrsig(*ctx->kctx->kasp_db, for_time, ctx->rrsig, ctx->dnskey, ctx->cdnskey, ctx->cds); + knot_rdataset_clear(&ctx->dnskey->rrs, NULL); + knot_rdataset_clear(&ctx->cdnskey->rrs, NULL); + knot_rdataset_clear(&ctx->cds->rrs, NULL); + knot_rdataset_clear(&ctx->rrsig->rrs, NULL); } } -static int read_ksr_skr(kdnssec_ctx_t *ctx, const char *infile, void (*cb)(zs_scanner_t *), uint16_t rrtype) +static int read_ksr_skr(kdnssec_ctx_t *ctx, const char *infile, void (*cb)(zs_scanner_t *)) { zs_scanner_t sc = { 0 }; int ret = zs_init(&sc, "", KNOT_CLASS_IN, 0); @@ -352,10 +463,14 @@ static int read_ksr_skr(kdnssec_ctx_t *ctx, const char *infile, void (*cb)(zs_sc return KNOT_EFILE; } - knot_rrset_t rr = { 0 }; - knot_rrset_init(&rr, ctx->zone->dname, rrtype, KNOT_CLASS_IN, ctx->policy->dnskey_ttl); + knot_rrset_t dnskey = { 0 }, dnskey_prev = { 0 }, cdnskey = { 0 }, cds = { 0 }, rrsig = { 0 }; + knot_rrset_init(&dnskey, ctx->zone->dname, KNOT_RRTYPE_DNSKEY, KNOT_CLASS_IN, ctx->policy->dnskey_ttl); + knot_rrset_init(&dnskey_prev, ctx->zone->dname, KNOT_RRTYPE_DNSKEY, KNOT_CLASS_IN, ctx->policy->dnskey_ttl); + knot_rrset_init(&cdnskey, ctx->zone->dname, KNOT_RRTYPE_CDNSKEY, KNOT_CLASS_IN, 0); + knot_rrset_init(&cds, ctx->zone->dname, KNOT_RRTYPE_CDS, KNOT_CLASS_IN, 0); + knot_rrset_init(&rrsig, ctx->zone->dname, KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, ctx->policy->dnskey_ttl); - ksr_sign_ctx_t pctx = { &rr, ctx }; + ksr_sign_ctx_t pctx = { &dnskey, &dnskey_prev, &cdnskey, &cds, &rrsig, ctx }; ret = zs_set_processing(&sc, cb, NULL, &pctx); if (ret < 0) { zs_deinit(&sc); @@ -366,17 +481,22 @@ static int read_ksr_skr(kdnssec_ctx_t *ctx, const char *infile, void (*cb)(zs_sc if (sc.error.code != KNOT_EOK) { ret = sc.error.code; - } else if (ret < 0 || rr.rrs.count > 0) { + } else if (ret < 0 || dnskey.rrs.count > 0 || cdnskey.rrs.count > 0 || + cds.rrs.count > 0 || rrsig.rrs.count > 0) { ret = KNOT_EMALF; } - knot_rdataset_clear(&rr.rrs, NULL); + knot_rdataset_clear(&dnskey.rrs, NULL); + knot_rdataset_clear(&dnskey_prev.rrs, NULL); + knot_rdataset_clear(&cdnskey.rrs, NULL); + knot_rdataset_clear(&cds.rrs, NULL); + knot_rdataset_clear(&rrsig.rrs, NULL); zs_deinit(&sc); return ret; } int keymgr_sign_ksr(kdnssec_ctx_t *ctx, const char *ksr_file) { - int ret = read_ksr_skr(ctx, ksr_file, ksr_sign_once, KNOT_RRTYPE_DNSKEY); + int ret = read_ksr_skr(ctx, ksr_file, ksr_sign_once); printf(";; SignedKeyResponse "); print_generated_message(); return ret; @@ -384,5 +504,5 @@ int keymgr_sign_ksr(kdnssec_ctx_t *ctx, const char *ksr_file) int keymgr_import_skr(kdnssec_ctx_t *ctx, const char *skr_file) { - return read_ksr_skr(ctx, skr_file, skr_import_once, KNOT_RRTYPE_RRSIG); + return read_ksr_skr(ctx, skr_file, skr_import_once); } diff --git a/tests-extra/tests/dnssec/offline_ksk/test.py b/tests-extra/tests/dnssec/offline_ksk/test.py deleted file mode 100644 index 1ac056b58a..0000000000 --- a/tests-extra/tests/dnssec/offline_ksk/test.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env python3 - -""" -Test of offline KSK by trying pre-generated ZSK rollover. -""" - -import collections -import os -import shutil -import datetime -import subprocess -import time -from subprocess import check_call - -from dnstest.utils import * -from dnstest.keys import Keymgr -from dnstest.test import Test - -# check zone if keys are present and used for signing -def check_zone(server, zone, dnskeys, dnskey_rrsigs, soa_rrsigs, msg): - qdnskeys = server.dig("example.com", "DNSKEY", bufsize=4096) - found_dnskeys = qdnskeys.count("DNSKEY") - - qdnskeyrrsig = server.dig("example.com", "DNSKEY", dnssec=True, bufsize=4096) - found_rrsigs = qdnskeyrrsig.count("RRSIG") - - qsoa = server.dig("example.com", "SOA", dnssec=True, bufsize=4096) - found_soa_rrsigs = qsoa.count("RRSIG") - - check_log("DNSKEYs: %d (expected %d)" % (found_dnskeys, dnskeys)); - check_log("RRSIGs: %d (expected %d)" % (found_soa_rrsigs, soa_rrsigs)); - check_log("DNSKEY-RRSIGs: %d (expected %d)" % (found_rrsigs, dnskey_rrsigs)); - - if found_dnskeys != dnskeys: - set_err("BAD DNSKEY COUNT: " + msg) - detail_log("!DNSKEYs not published and activated as expected: " + msg) - - if found_soa_rrsigs != soa_rrsigs: - set_err("BAD RRSIG COUNT: " + msg) - detail_log("!RRSIGs not published and activated as expected: " + msg) - - if found_rrsigs != dnskey_rrsigs: - set_err("BAD DNSKEY RRSIG COUNT: " + msg) - detail_log("!RRSIGs not published and activated as expected: " + msg) - - detail_log(SEP) - - # Valgrind delay breaks the timing! - if not server.valgrind: - server.zone_backup(zone, flush=True) - server.zone_verify(zone) - -def wait_for_rrsig_count(t, server, rrtype, rrsig_count, timeout): - rtime = 0 - while True: - qdnskeyrrsig = server.dig("example.com", rrtype, dnssec=True, bufsize=4096) - found_rrsigs = qdnskeyrrsig.count("RRSIG") - if found_rrsigs == rrsig_count: - break - rtime = rtime + 1 - t.sleep(1) - if rtime > timeout: - break - -def wait_for_dnskey_count(t, server, dnskey_count, timeout): - for rtime in range(1, timeout): - qdnskeyrrsig = server.dig("example.com", "DNSKEY", dnssec=True, bufsize=4096) - found_dnskeys = qdnskeyrrsig.count("DNSKEY") - if found_dnskeys == dnskey_count: - break - t.sleep(1) - -t = Test() - -knot = t.server("knot") -ZONE = "example.com." -FUTURE = 100 - -zone = t.zone(ZONE) -t.link(zone, knot) - -knot.zonefile_sync = 24 * 60 * 60 - -knot.dnssec(zone).enable = True -knot.dnssec(zone).manual = True -knot.dnssec(zone).alg = "ECDSAP384SHA384" -knot.dnssec(zone).dnskey_ttl = 2 -knot.dnssec(zone).zsk_lifetime = 12 -knot.dnssec(zone).ksk_lifetime = 300 # this can be possibly left also infinity -knot.dnssec(zone).propagation_delay = 3 -knot.port = 1234 # dummy, will be overwritten -knot.gen_confile() - -key_ksk = knot.gen_key(zone, ksk=True, alg="ECDSAP384SHA384", key_len=384) -key_zsk = knot.gen_key(zone, ksk=False, alg="ECDSAP384SHA384", key_len=384) - -Keymgr.run_check(knot.confile, ZONE, "pregenerate", str(FUTURE)) -Keymgr.run_check(knot.confile, ZONE, "presign", str(FUTURE)) - -os.remove(knot.keydir + "/keys/" + key_ksk.keyid + ".pem") - -# parameters - -t.start() -knot.zone_wait(zone) -check_zone(knot, zone, 2, 1, 1, "init") - -wait_for_dnskey_count(t, knot, 3, knot.dnssec(zone).zsk_lifetime) -check_zone(knot, zone, 3, 1, 1, "ZSK rollover") - -t.sleep(2) - -wait_for_dnskey_count(t, knot, 2, 2 * (knot.dnssec(zone).propagation_delay + knot.dnssec(zone).dnskey_ttl)) -check_zone(knot, zone, 2, 1, 1, "end") - -t.end() diff --git a/tests-extra/tests/dnssec/offline_ksk2/test.py b/tests-extra/tests/dnssec/offline_ksk2/test.py index ac4729e466..33ecef2db1 100644 --- a/tests-extra/tests/dnssec/offline_ksk2/test.py +++ b/tests-extra/tests/dnssec/offline_ksk2/test.py @@ -94,35 +94,23 @@ knot.dnssec(zone).dnskey_ttl = 2 knot.dnssec(zone).zsk_lifetime = STARTUP + 6*TICK # see ksk1 lifetime knot.dnssec(zone).ksk_lifetime = 300 # this can be possibly left also infinity knot.dnssec(zone).propagation_delay = TICK-2 -knot.dnssec(zone).cds_publish = "none" +knot.dnssec(zone).offline_ksk = "on" knot.port = 1234 # dummy, will be overwritten knot.gen_confile() -def tickf(when): - return "+%d" % (STARTUP + when * TICK) - -# generate keys, including manual KSK rollover on the beginning -key_ksk1 = knot.key_gen(ZONE, ksk="true", created="+0", publish="+0", ready="+0", active="+0", retire=tickf(4), remove=tickf(5)) -key_ksk2 = knot.key_gen(ZONE, ksk="true", created="+0", publish=tickf(2), ready=tickf(3), active=tickf(4), retire="+2h", remove="+3h") -key_zsk1 = knot.key_gen(ZONE, ksk="false", created="+0", publish="+0", active="+0") - -# signer knot, copy everything from "knot" signer = t.server("knot") t.link(zone, signer) signer.zones[ZONE].dnssec = knot.zones[ZONE].dnssec signer.port = 1235 signer.gen_confile() -_, keys_list, _ = Keymgr.run_check(knot.confile, ZONE, "list") -for keyparm in keys_list.splitlines(): - pem = knot.keydir + "/keys/" + keyparm.split()[0] + ".pem" - parm1 = keyparm.split()[1] - parm2 = keyparm.replace("-", "_").split()[7:] - Keymgr.run_check(signer.confile, ZONE, "import-pem", pem, parm1, *parm2) - -# delete KSKs in "knot" and ZSKs in "signer" -os.remove(knot.keydir + "/keys/" + key_ksk1 + ".pem") -os.remove(knot.keydir + "/keys/" + key_ksk2 + ".pem") -os.remove(signer.keydir + "/keys/" + key_zsk1 + ".pem") + +def tickf(when): + return "+%d" % (STARTUP + when * TICK) + +# generate keys, including manual KSK rollover on the beginning +key_ksk1 = signer.key_gen(ZONE, ksk="true", created="+0", publish="+0", ready="+0", active="+0", retire=tickf(4), remove=tickf(5)) +key_ksk2 = signer.key_gen(ZONE, ksk="true", created="+0", publish=tickf(2), ready=tickf(3), active=tickf(4), retire="+2h", remove="+3h") +key_zsk1 = knot.key_gen(ZONE, ksk="false", created="+0", publish="+0", active="+0") # pregenerate keys, exchange KSR, pre-sign it, exchange SKR KSR = knot.keydir + "/ksr" diff --git a/tests-extra/tools/dnstest/server.py b/tests-extra/tools/dnstest/server.py index c6377dc09b..4958ad47a4 100644 --- a/tests-extra/tools/dnstest/server.py +++ b/tests-extra/tools/dnstest/server.py @@ -58,6 +58,7 @@ class ZoneDnssec(object): self.ksk_sbm_check_interval = None self.ksk_shared = None self.cds_publish = None + self.offline_ksk = None class Zone(object): '''DNS zone description''' @@ -1199,6 +1200,7 @@ class Knot(Server): s.item("ksk-submission", z.name) self._bool(s, "ksk-shared", z.dnssec.ksk_shared) self._str(s, "cds-cdnskey-publish", z.dnssec.cds_publish) + self._str(s, "offline-ksk", z.dnssec.offline_ksk) if have_policy: s.end() -- 2.47.3