Commit cb4c1973 authored by Daniel Salzman's avatar Daniel Salzman

Merge branch 'multiple_cds' into 'master'

Multiple cds

Closes #605

See merge request !955
parents de486f67 d710af57
Pipeline #44364 passed with stages
in 10 minutes and 33 seconds
......@@ -175,12 +175,9 @@ be set concurrently with the \fBksk\fP flag.
Overrides the standard setting of the Secure Entry Point flag for the generated key.
.UNINDENT
.sp
The following arguments are timestamps of key lifetime:
The following arguments are timestamps of key lifetime (see DNSSEC Key states):
.INDENT 0.0
.TP
\fBcreated\fP
Key created.
.TP
\fBpre_active\fP
Key started to be used for signing, not published (only for algorithm rollover).
.TP
......
......@@ -154,9 +154,6 @@ A module identifier in the form of the \fBmod\-\fP prefix and module name suffix
.sp
A path to a shared library file with the module implementation.
.sp
\fIDefault:\fP \fB${libdir}/knot/modules\-${version}\fP/module_name.so
(or \fB${path}\fP/module_name.so if configured with \fB\-\-with\-moduledir=path\fP)
.sp
\fBWARNING:\fP
.INDENT 0.0
.INDENT 3.5
......@@ -164,6 +161,9 @@ If the path is not absolute, the library is searched in the set of
system directories. See \fBman dlopen\fP for more details.
.UNINDENT
.UNINDENT
.sp
\fIDefault:\fP \fB${libdir}/knot/modules\-${version}\fP/module_name.so
(or \fB${path}\fP/module_name.so if configured with \fB\-\-with\-moduledir=path\fP)
.SH SERVER SECTION
.sp
General options related to the server.
......@@ -675,7 +675,7 @@ policy:
nsec3\-salt\-lifetime: TIME
signing\-threads: INT
ksk\-submission: submission_id
cds\-cdnskey\-publish: none | delete\-dnssec | rollover | always
cds\-cdnskey\-publish: none | delete\-dnssec | rollover | always | double\-ds
offline\-ksk: BOOL
.ft P
.fi
......@@ -724,14 +724,14 @@ Possible values:
\fBed25519\fP
.UNINDENT
.sp
\fIDefault:\fP ecdsap256sha256
.sp
\fBNOTE:\fP
.INDENT 0.0
.INDENT 3.5
Ed25519 algorithm is only available when compiled with GnuTLS 3.6.0+.
.UNINDENT
.UNINDENT
.sp
\fIDefault:\fP ecdsap256sha256
.SS ksk\-size
.sp
A length of newly generated KSK or
......@@ -752,14 +752,14 @@ If enabled, all zones with this policy assigned will share one KSK.
.sp
A TTL value for DNSKEY records added into zone apex.
.sp
\fIDefault:\fP zone SOA TTL
.sp
\fBNOTE:\fP
.INDENT 0.0
.INDENT 3.5
Has infuence over ZSK key lifetime.
.UNINDENT
.UNINDENT
.sp
\fIDefault:\fP zone SOA TTL
.SS zone\-max\-ttl
.sp
Maximal TTL value among all the records in zone.
......@@ -777,8 +777,6 @@ explicitly whenever possible. It\(aqs required for DNSSEC Offline KSK\&.
.sp
A period between ZSK publication and the next rollover initiation.
.sp
\fIDefault:\fP 30 days
.sp
\fBNOTE:\fP
.INDENT 0.0
.INDENT 3.5
......@@ -787,12 +785,12 @@ ZSK key lifetime is also infuenced by propagation\-delay and dnskey\-ttl
Zero (aka infinity) value causes no ZSK rollover as a result.
.UNINDENT
.UNINDENT
.sp
\fIDefault:\fP 30 days
.SS ksk\-lifetime
.sp
A period between KSK publication and the next rollover initiation.
.sp
\fIDefault:\fP 0
.sp
\fBNOTE:\fP
.INDENT 0.0
.INDENT 3.5
......@@ -804,19 +802,21 @@ Zero (aka infinity) value causes no KSK rollover as a result.
This applies for CSK lifetime if single\-type\-signing is enabled.
.UNINDENT
.UNINDENT
.sp
\fIDefault:\fP 0
.SS propagation\-delay
.sp
An extra delay added for each key rollover step. This value should be high
enough to cover propagation of data from the master server to all slaves.
.sp
\fIDefault:\fP 1 hour
.sp
\fBNOTE:\fP
.INDENT 0.0
.INDENT 3.5
Has infuence over ZSK key lifetime.
.UNINDENT
.UNINDENT
.sp
\fIDefault:\fP 1 hour
.SS rrsig\-lifetime
.sp
A validity period of newly issued signatures.
......@@ -886,13 +886,6 @@ Some steps of the DNSSEC signing operation are not parallelized.
.sp
Controls if and how shall the CDS and CDNSKEY be published in the zone.
.sp
\fBNOTE:\fP
.INDENT 0.0
.INDENT 3.5
This only applies if the zone keys are automatically managed by the server.
.UNINDENT
.UNINDENT
.sp
Possible values:
.INDENT 0.0
.IP \(bu 2
......@@ -902,7 +895,17 @@ Possible values:
.IP \(bu 2
\fBrollover\fP – Publish CDS and CDNSKEY records only in the submission phase of KSK rollover.
.IP \(bu 2
\fBalways\fP – Always publish CDS and CDNSKEY records for the current KSK.
\fBalways\fP – Always publish one CDS and one CDNSKEY records for the current KSK.
.IP \(bu 2
\fBdouble\-ds\fP – Always publish up to two CDS and two CDNSKEY records for ready and/or active KSKs.
.UNINDENT
.sp
\fBNOTE:\fP
.INDENT 0.0
.INDENT 3.5
If the zone keys are managed manually, the CDS and CDNSKEY rrsets may contain
more records depending on the keys available.
.UNINDENT
.UNINDENT
.sp
\fIDefault:\fP always
......@@ -1071,14 +1074,14 @@ This option is only available in the \fIdefault\fP template.
A KASP database path. Non\-absolute path is relative to
\fI\%storage\fP\&.
.sp
\fIDefault:\fP \fI\%storage\fP/keys
.sp
\fBNOTE:\fP
.INDENT 0.0
.INDENT 3.5
This option is only available in the \fIdefault\fP template.
.UNINDENT
.UNINDENT
.sp
\fIDefault:\fP \fI\%storage\fP/keys
.SS max\-kasp\-db\-size
.sp
Hard limit for the KASP database maximum size.
......@@ -1317,8 +1320,6 @@ Possible values:
.sp
Policy how much space in journal DB will the zone\(aqs journal occupy.
.sp
\fIDefault:\fP 100 MiB
.sp
\fBNOTE:\fP
.INDENT 0.0
.INDENT 3.5
......@@ -1326,6 +1327,8 @@ Journal DB may grow far above the sum of max\-journal\-usage across
all zones, because of DB free space fragmentation.
.UNINDENT
.UNINDENT
.sp
\fIDefault:\fP 100 MiB
.SS max\-journal\-depth
.sp
Maximum history length of journal.
......
......@@ -151,10 +151,7 @@ Arguments are separated by space, each of them is in format 'name=value'.
**sep**
Overrides the standard setting of the Secure Entry Point flag for the generated key.
The following arguments are timestamps of key lifetime:
**created**
Key created.
The following arguments are timestamps of key lifetime (see :ref:`DNSSEC Key states`):
**pre_active**
Key started to be used for signing, not published (only for algorithm rollover).
......
......@@ -414,6 +414,54 @@ current SOA serial in the zone (not in the zone file) if manually updated!
In the case of "zonefile-load: difference-no-serial", the SOA serial is
handled by the server automatically during server reload.
.. _DNSSEC Key states:
DNSSEC key states
=================
During its lifetime, DNSSEC key finds itself in different states. Most of the time it
is usually used for signing the zone and published in the zone. In order to change
this state, one type of a key rollover is necessary, and during this rollover,
the key goes through various states, with respect to the rollover type and also the
state of the other key being rolled-over.
First, let's list the states of the key being rolled-in.
Standard states:
- ``active`` The key is used for signing.
- ``published`` The key is published in the zone, but not used for signing.
- ``ready`` (only for KSK) The key is published in the zone and used for signing. The
old key is still active, since we are waiting for the DS records in the parent zone to be
updated (i.e. "KSK submission").
Special states for algorithm rollover:
- ``pre-active`` The key is not yet published in the zone, but it's used for signing the zone.
- ``published`` — The key is published in the zone, and it's still used for signing since the
pre-active state.
Second, we list the states of the key being rolled-out.
Standard states:
- ``post-active`` The key is still used for signing and published in the zone, waiting for
the updated DS records in parent zone to be acked by resolvers (KSK case) or synchronizing
with KSK during algorithm rollover (ZSK case).
- ``retired`` The key is no longer used for signing, but still published in the zone.
- ``removed`` The key is not used in any way (in most cases such keys are deleted immediately).
Special states for algorithm rollover:
- ``retire-active`` The key is no longer published in the zone, but still used for signing.
The states listed above are relevant for :doc:`keymgr <man_keymgr>` operations like generating
a key, setting its timers and listing KASP database.
On the other hand, the key "states" displayed in the server log lines while zone signing
are not according to listed above, but just a hint what the key is currently used to
(e.g. "public, active" = key is published in the zone and used for signing).
.. _DNSSEC Key rollovers:
DNSSEC key rollovers
......
......@@ -107,13 +107,13 @@ file
A path to a shared library file with the module implementation.
*Default:* ``${libdir}/knot/modules-${version}``/module_name.so
(or ``${path}``/module_name.so if configured with ``--with-moduledir=path``)
.. WARNING::
If the path is not absolute, the library is searched in the set of
system directories. See ``man dlopen`` for more details.
*Default:* ``${libdir}/knot/modules-${version}``/module_name.so
(or ``${path}``/module_name.so if configured with ``--with-moduledir=path``)
.. _Server section:
Server section
......@@ -747,7 +747,7 @@ DNSSEC policy configuration.
nsec3-salt-lifetime: TIME
signing-threads: INT
ksk-submission: submission_id
cds-cdnskey-publish: none | delete-dnssec | rollover | always
cds-cdnskey-publish: none | delete-dnssec | rollover | always | double-ds
offline-ksk: BOOL
.. _policy_id:
......@@ -804,11 +804,11 @@ Possible values:
- ``ecdsap384sha384``
- ``ed25519``
*Default:* ecdsap256sha256
.. NOTE::
Ed25519 algorithm is only available when compiled with GnuTLS 3.6.0+.
*Default:* ecdsap256sha256
.. _policy_ksk-size:
ksk-size
......@@ -844,11 +844,11 @@ dnskey-ttl
A TTL value for DNSKEY records added into zone apex.
*Default:* zone SOA TTL
.. NOTE::
Has infuence over ZSK key lifetime.
*Default:* zone SOA TTL
.. _policy_zone-max-ttl:
zone-max-ttl
......@@ -869,13 +869,13 @@ zsk-lifetime
A period between ZSK publication and the next rollover initiation.
*Default:* 30 days
.. NOTE::
ZSK key lifetime is also infuenced by propagation-delay and dnskey-ttl
Zero (aka infinity) value causes no ZSK rollover as a result.
*Default:* 30 days
.. _policy_ksk-lifetime:
ksk-lifetime
......@@ -883,8 +883,6 @@ ksk-lifetime
A period between KSK publication and the next rollover initiation.
*Default:* 0
.. NOTE::
KSK key lifetime is also infuenced by propagation-delay, dnskey-ttl,
and KSK submission delay.
......@@ -893,6 +891,8 @@ A period between KSK publication and the next rollover initiation.
This applies for CSK lifetime if single-type-signing is enabled.
*Default:* 0
.. _policy_propagation-delay:
propagation-delay
......@@ -901,11 +901,11 @@ propagation-delay
An extra delay added for each key rollover step. This value should be high
enough to cover propagation of data from the master server to all slaves.
*Default:* 1 hour
.. NOTE::
Has infuence over ZSK key lifetime.
*Default:* 1 hour
.. _policy_rrsig-lifetime:
rrsig-lifetime
......@@ -1006,15 +1006,17 @@ cds-cdnskey-publish
Controls if and how shall the CDS and CDNSKEY be published in the zone.
.. NOTE::
This only applies if the zone keys are automatically managed by the server.
Possible values:
- ``none`` – Never publish any CDS or CDNSKEY records in the zone.
- ``delete-dnssec`` – Publish special CDS and CDNSKEY records indicating turning off DNSSEC.
- ``rollover`` – Publish CDS and CDNSKEY records only in the submission phase of KSK rollover.
- ``always`` – Always publish CDS and CDNSKEY records for the current KSK.
- ``always`` – Always publish one CDS and one CDNSKEY records for the current KSK.
- ``double-ds`` – Always publish up to two CDS and two CDNSKEY records for ready and/or active KSKs.
.. NOTE::
If the zone keys are managed manually, the CDS and CDNSKEY rrsets may contain
more records depending on the keys available.
*Default:* always
......@@ -1203,11 +1205,11 @@ kasp-db
A KASP database path. Non-absolute path is relative to
:ref:`storage<zone_storage>`.
*Default:* :ref:`storage<zone_storage>`/keys
.. NOTE::
This option is only available in the *default* template.
*Default:* :ref:`storage<zone_storage>`/keys
.. _template_max-kasp-db-size:
max-kasp-db-size
......@@ -1464,12 +1466,12 @@ max-journal-usage
Policy how much space in journal DB will the zone's journal occupy.
*Default:* 100 MiB
.. NOTE::
Journal DB may grow far above the sum of max-journal-usage across
all zones, because of DB free space fragmentation.
*Default:* 100 MiB
.. _zone_max_journal_depth:
max-journal-depth
......
......@@ -76,6 +76,7 @@ const knot_lookup_t child_record[] = {
{ CHILD_RECORDS_EMPTY, "delete-dnssec" },
{ CHILD_RECORDS_ROLLOVER, "rollover" },
{ CHILD_RECORDS_ALWAYS, "always" },
{ CHILD_RECORDS_DOUBLE_DS,"double-ds" },
{ 0, NULL }
};
......
......@@ -135,6 +135,7 @@ enum {
CHILD_RECORDS_EMPTY = 1,
CHILD_RECORDS_ROLLOVER = 2,
CHILD_RECORDS_ALWAYS = 3,
CHILD_RECORDS_DOUBLE_DS= 4,
};
enum {
......
......@@ -230,7 +230,8 @@ int knot_parent_ds_query(kdnssec_ctx_t *kctx, zone_keyset_t *keyset, size_t time
for (size_t i = 0; i < keyset->count; i++) {
zone_key_t *key = &keyset->keys[i];
if (key->is_ksk && key->cds_priority > 1) {
if (key->is_ready) {
assert(key->is_ksk);
if (parents_have_ds(kctx, key, timeout, &max_ds_ttl)) {
return knot_dnssec_ksk_sbm_confirm(kctx, max_ds_ttl);
} else {
......
......@@ -132,7 +132,7 @@ int key_records_dump(char **buf, size_t *buf_size, const key_records_t *r, bool
int key_records_sign(const zone_key_t *key, key_records_t *r, const kdnssec_ctx_t *kctx)
{
if (!key->is_active) {
if (!key->is_active && !key->is_post_active) {
return KNOT_EOK;
}
......
......@@ -217,18 +217,30 @@ static void set_key(knot_kasp_key_t *kasp_key, knot_time_t now, zone_key_t *zone
knot_time_cmp(timing->post_active, now) > 0 &&
knot_time_cmp(timing->remove, now) > 0);
zone_key->is_active = (((zone_key->is_zsk && knot_time_cmp(timing->pre_active, now) <= 0) ||
(knot_time_cmp(timing->pre_active, now) <= 0 && knot_time_cmp(timing->publish, now) <= 0) ||
knot_time_cmp(timing->ready, now) <= 0 ||
zone_key->is_ready = (zone_key->is_ksk && knot_time_cmp(timing->ready, now) <= 0 &&
knot_time_cmp(timing->active, now) > 0);
zone_key->is_active = ((knot_time_cmp(timing->ready, now) <= 0 ||
knot_time_cmp(timing->active, now) <= 0) &&
knot_time_cmp(timing->retire, now) > 0 &&
(zone_key->is_zsk || knot_time_cmp(timing->post_active, now) > 0) &&
knot_time_cmp(timing->retire_active, now) > 0 &&
knot_time_cmp(timing->remove, now) > 0);
zone_key->cds_priority = (knot_time_cmp(timing->ready, now) <= 0 ? (
(knot_time_cmp(timing->active, now) <= 0) ? (
(knot_time_cmp(timing->retire_active, now) <= 0 ||
knot_time_cmp(timing->retire, now) <= 0) ? 0 : 1) : 2) : 0);
zone_key->is_post_active = false;
if (knot_time_cmp(timing->pre_active, now) <= 0 &&
knot_time_cmp(timing->ready, now) > 0 &&
knot_time_cmp(timing->active, now) > 0) {
zone_key->is_post_active = (zone_key->is_zsk ||
knot_time_cmp(timing->publish, now) <= 0);
}
if (knot_time_cmp(timing->retire_active, now) <= 0 &&
knot_time_cmp(timing->retire, now) > 0) {
zone_key->is_post_active = true;
} // not "else" !
if (knot_time_cmp(timing->post_active, now) <= 0 &&
knot_time_cmp(timing->remove, now) > 0) {
zone_key->is_post_active = zone_key->is_zsk;
}
}
/*!
......@@ -259,14 +271,15 @@ static int walk_algorithms(kdnssec_ctx_t *ctx, zone_keyset_t *keyset)
dnssec_key_get_keytag(key->key));
key->is_public = false;
key->is_active = false;
key->cds_priority = 0;
key->is_ready = false;
key->is_post_active = false;
continue;
}
if (key->is_ksk && key->is_public) { alg_usage[alg] |= 1; }
if (key->is_zsk && key->is_public) { alg_usage[alg] |= 2; }
if (key->is_ksk && key->is_active) { alg_usage[alg] |= 4; }
if (key->is_zsk && key->is_active) { alg_usage[alg] |= 8; }
if (key->is_ksk && (key->is_active || key->is_post_active)) { alg_usage[alg] |= 4; }
if (key->is_zsk && (key->is_active || key->is_post_active)) { alg_usage[alg] |= 8; }
}
for (size_t i = 0; i < sizeof(alg_usage); i++) {
......@@ -305,11 +318,10 @@ static int load_private_keys(dnssec_keystore_t *keystore, zone_keyset_t *keyset)
assert(keyset);
for (size_t i = 0; i < keyset->count; i++) {
if (!keyset->keys[i].is_active) {
zone_key_t *key = &keyset->keys[i];
if (!key->is_active && !key->is_post_active) {
continue;
}
zone_key_t *key = &keyset->keys[i];
int r = dnssec_key_import_keystore(key->key, keystore, key->id);
switch (r) {
case DNSSEC_EOK:
......@@ -339,13 +351,14 @@ static void log_key_info(const zone_key_t *key, char *out, size_t out_len)
(void)snprintf(alg_code_str, sizeof(alg_code_str), "%d", alg_code);
}
(void)snprintf(out, out_len, "DNSSEC, key, tag %5d, algorithm %s%s%s%s%s",
(void)snprintf(out, out_len, "DNSSEC, key, tag %5d, algorithm %s%s%s%s%s%s",
dnssec_key_get_keytag(key->key),
(alg != NULL ? alg->name : alg_code_str),
(key->is_ksk ? (key->is_zsk ? ", CSK" : ", KSK") : ""),
(key->is_public ? ", public" : ""),
(key->cds_priority > 1 ? ", ready" : ""),
(key->is_active ? ", active" : ""));
(key->is_public ? ", public" : ""),
(key->is_ready ? ", ready" : ""),
(key->is_active ? ", active" : ""),
(key->is_post_active ? ", active+" : ""));
}
int log_key_sort(const void *a, const void *b)
......
......@@ -38,7 +38,8 @@ typedef struct {
bool is_zsk;
bool is_active;
bool is_public;
int cds_priority;
bool is_ready;
bool is_post_active; // also for retire_active, pre_active
} zone_key_t;
dynarray_declare(keyptr, zone_key_t *, DYNARRAY_VISIBILITY_PUBLIC, 1)
......
......@@ -234,7 +234,8 @@ static int remove_expired_rrsigs(const knot_rrset_t *covered,
for (size_t j = 0; j < sign_ctx->count; j++) {
zone_key_t *key = &sign_ctx->keys[j];
if (!key->is_active || dnssec_key_get_keytag(key->key) != keytag) {
if ((!key->is_active && !key->is_post_active) ||
dnssec_key_get_keytag(key->key) != keytag) {
continue;
}
......@@ -794,30 +795,34 @@ keyptr_dynarray_t knot_zone_sign_get_cdnskeys(const kdnssec_ctx_t *ctx,
zone_keyset_t *zone_keys)
{
keyptr_dynarray_t r = { 0 };
zone_key_t *ksk_for_cds = NULL;
unsigned crp = ctx->policy->child_records_publish;
int kfc_prio = (crp == CHILD_RECORDS_ALWAYS ? 0 : (crp == CHILD_RECORDS_ROLLOVER ? 1 : 2));
for (int i = 0; i < zone_keys->count; i++) {
zone_key_t *key = &zone_keys->keys[i];
// determine which key (if any) will be the one for CDS/CDNSKEY
if (key->is_ksk && key->cds_priority > kfc_prio) {
ksk_for_cds = key;
kfc_prio = key->cds_priority;
if (crp == CHILD_RECORDS_ROLLOVER || crp == CHILD_RECORDS_ALWAYS ||
crp == CHILD_RECORDS_DOUBLE_DS) {
// first, add strictly-ready keys
for (int i = 0; i < zone_keys->count; i++) {
zone_key_t *key = &zone_keys->keys[i];
if (key->is_ready) {
assert(key->is_ksk);
keyptr_dynarray_add(&r, &key);
}
}
}
for (int i = 0; i < zone_keys->count; i++) {
zone_key_t *key = &zone_keys->keys[i];
// determine which key (if any) will be the one for CDS/CDNSKEY
if (key->is_ksk && key->cds_priority > kfc_prio) {
ksk_for_cds = key;
kfc_prio = key->cds_priority;
// second, add active keys
if ((crp == CHILD_RECORDS_ALWAYS && r.size == 0) ||
(crp == CHILD_RECORDS_DOUBLE_DS)) {
for (int i = 0; i < zone_keys->count; i++) {
zone_key_t *key = &zone_keys->keys[i];
if (key->is_ksk && key->is_active && !key->is_ready) {
keyptr_dynarray_add(&r, &key);
}
}
}
}
if (ksk_for_cds != NULL) {
keyptr_dynarray_add(&r, &ksk_for_cds);
if ((crp != CHILD_RECORDS_DOUBLE_DS && r.size > 1) ||
(r.size > 2)) {
log_zone_warning(ctx->zone->dname, "DNSSEC, published CDS/CDNSKEY records for too many (%zu) keys", r.size);
}
}
return r;
......@@ -950,7 +955,7 @@ bool knot_zone_sign_use_key(const zone_key_t *key, const knot_rrset_t *covered)
return false;
}
if (!key->is_active) {
if (!key->is_active && !key->is_post_active) {
return false;
}
......
......@@ -28,6 +28,7 @@
#include "knot/dnssec/policy.h"
#include "knot/dnssec/rrset-sign.h"
#include "knot/dnssec/zone-events.h"
#include "knot/dnssec/zone-sign.h"
#include "knot/nameserver/query_module.h"
#include "knot/nameserver/process_query.h"
......@@ -428,23 +429,6 @@ static knot_rrset_t *synth_dnskey(knotd_qdata_t *qdata, knotd_mod_t *mod,
return dnskey;
}
/* copied from the middle of zone-sign.c */
static zone_key_t *ksk_for_cds(knotd_mod_t *mod)
{
zone_key_t *res = NULL;
unsigned crp = mod->dnssec->policy->child_records_publish;
int kfc_prio = (crp == CHILD_RECORDS_ALWAYS ?
0 : (crp == CHILD_RECORDS_ROLLOVER ? 1 : 2));
for (int i = 0; i < mod->keyset->count; i++) {
zone_key_t *key = &mod->keyset->keys[i];
if (key->is_ksk && key->cds_priority > kfc_prio) {
res = key;
kfc_prio = key->cds_priority;
}
}
return res;
}
static knot_rrset_t *synth_cdnskey(knotd_qdata_t *qdata, knotd_mod_t *mod,
knot_mm_t *mm)
{
......@@ -458,21 +442,13 @@ static knot_rrset_t *synth_cdnskey(knotd_qdata_t *qdata, knotd_mod_t *mod,
dnssec_binary_t rdata = { 0 };
online_sign_ctx_t *ctx = knotd_mod_ctx(mod);
pthread_rwlock_rdlock(&ctx->signing_mutex);
zone_key_t *key