Commit 3fbf23de authored by Libor Peltan's avatar Libor Peltan Committed by Daniel Salzman

ksk rollover: parent DS check implemented +test

parent b864bafe
......@@ -229,6 +229,7 @@ src/knot/events/handlers/nsec3resalt.c
src/knot/events/handlers/refresh.c
src/knot/events/handlers/update.c
src/knot/events/handlers/key_rollover.c
src/knot/events/handlers/parent_ds_query.c
src/knot/events/log.c
src/knot/events/log.h
src/knot/events/replan.c
......
......@@ -289,6 +289,7 @@ libknotd_la_SOURCES = \
knot/events/handlers/refresh.c \
knot/events/handlers/update.c \
knot/events/handlers/key_rollover.c \
knot/events/handlers/parent_ds_query.c \
knot/events/log.c \
knot/events/log.h \
knot/events/replan.c \
......
......@@ -203,6 +203,8 @@ static const yp_item_t desc_policy[] = {
{ C_NSEC3_SALT_LEN, YP_TINT, YP_VINT = { 0, UINT8_MAX, 8 }, CONF_IO_FRLD_ZONES },
{ C_NSEC3_SALT_LIFETIME, YP_TINT, YP_VINT = { 1, UINT32_MAX, DAYS(30), YP_STIME },
CONF_IO_FRLD_ZONES },
{ C_KSK_SUBMITTION_CHECK,YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI | CONF_IO_FRLD_ZONES, { check_ref } },
{ C_KSK_SUBMITTION_CHECK_INTERVAL, YP_TINT, YP_VINT = { 1, UINT32_MAX, HOURS(1), YP_STIME }, CONF_IO_FRLD_ZONES },
{ C_COMMENT, YP_TSTR, YP_VNONE },
{ NULL }
};
......@@ -292,10 +294,10 @@ const yp_item_t conf_scheme[] = {
{ C_LOG, YP_TGRP, YP_VGRP = { desc_log }, YP_FMULTI | CONF_IO_FRLD_LOG },
{ C_STATS, YP_TGRP, YP_VGRP = { desc_stats }, CONF_IO_FRLD_SRV },
{ C_KEYSTORE, YP_TGRP, YP_VGRP = { desc_keystore }, YP_FMULTI, { check_keystore } },
{ C_POLICY, YP_TGRP, YP_VGRP = { desc_policy }, YP_FMULTI, { check_policy } },
{ C_KEY, YP_TGRP, YP_VGRP = { desc_key }, YP_FMULTI, { check_key } },
{ C_ACL, YP_TGRP, YP_VGRP = { desc_acl }, YP_FMULTI, { check_acl } },
{ C_RMT, YP_TGRP, YP_VGRP = { desc_remote }, YP_FMULTI, { check_remote } },
{ C_POLICY, YP_TGRP, YP_VGRP = { desc_policy }, YP_FMULTI, { check_policy } },
{ C_TPL, YP_TGRP, YP_VGRP = { desc_template }, YP_FMULTI, { check_template } },
{ C_ZONE, YP_TGRP, YP_VGRP = { desc_zone }, YP_FMULTI | CONF_IO_FZONE, { check_zone } },
{ C_INCL, YP_TSTR, YP_VNONE, CONF_IO_FDIFF_ZONES | CONF_IO_FRLD_ALL, { include_file } },
......
......@@ -60,7 +60,10 @@
#define C_KASP_DB_MAPSIZE "\x0F""kasp-db-mapsize"
#define C_KEY "\x03""key"
#define C_KEYSTORE "\x08""keystore"
#define C_KSK_LIFETIME "\x0C""ksk-lifetime"
#define C_KSK_SIZE "\x08""ksk-size"
#define C_KSK_SUBMITTION_CHECK "\x14""ksk-submittion-check"
#define C_KSK_SUBMITTION_CHECK_INTERVAL "\x1D""ksk-submittion-check-interval"
#define C_LISTEN "\x06""listen"
#define C_LOG "\x03""log"
#define C_MANUAL "\x06""manual"
......@@ -117,7 +120,6 @@
#define C_ZONE "\x04""zone"
#define C_ZONEFILE_SYNC "\x0D""zonefile-sync"
#define C_ZSK_LIFETIME "\x0C""zsk-lifetime"
#define C_KSK_LIFETIME "\x0C""ksk-lifetime"
#define C_ZSK_SIZE "\x08""zsk-size"
enum {
......
......@@ -179,7 +179,7 @@ static roll_action next_action(kdnssec_ctx_t *ctx)
restype = SUBMIT;
break;
case DNSSEC_KEY_STATE_READY:
break; // TODO !!!
break;
case DNSSEC_KEY_STATE_ACTIVE:
if (!is_ksk_published) {
keytime = ksk_publish_time(key->timing.active, ctx);
......@@ -279,7 +279,7 @@ static int exec_remove_old_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *key)
return kdnssec_delete_key(ctx, key);
}
int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, bool *keys_changed, time_t *next_rollover)
int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_t *zone, bool *keys_changed, time_t *next_rollover)
{
if (ctx->policy->manual) {
return KNOT_EOK;
......@@ -312,6 +312,11 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, bool *keys_changed, time_t *nex
break;
case SUBMIT:
ret = submit_key(ctx, next.key);
if (zone == NULL) {
return KNOT_EINVAL;
}
zone_events_schedule_now(zone, ZONE_EVENT_PARENT_DS_Q);
// "now" it won't probably succeed, but it replans itself for proper interval
break;
case REPLACE:
ret = exec_new_signatures(ctx, next.key);
......@@ -354,3 +359,17 @@ int knot_dnssec_ksk_submittion_confirm(kdnssec_ctx_t *ctx, uint16_t for_key)
}
return KNOT_ENOENT;
}
bool zone_has_key_submittion(const kdnssec_ctx_t *ctx)
{
assert(ctx->zone);
for (size_t i = 0; i < ctx->zone->num_keys; i++) {
knot_kasp_key_t *key = &ctx->zone->keys[i];
if (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK &&
get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) {
return true;
}
}
return false;
}
......@@ -19,6 +19,7 @@
#include <time.h>
#include "knot/dnssec/context.h"
#include "knot/zone/zone.h"
/*!
* \brief Perform correct ZSK and KSK rollover action and plan next one.
......@@ -37,6 +38,8 @@
*
* \return KNOT_E*
*/
int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, bool *keys_changed, time_t *next_rollover);
int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_t *zone, bool *keys_changed, time_t *next_rollover);
int knot_dnssec_ksk_submittion_confirm(kdnssec_ctx_t *ctx, uint16_t for_key);
bool zone_has_key_submittion(const kdnssec_ctx_t *ctx);
......@@ -646,6 +646,15 @@ static bool cds_rdata_match(zone_key_t *key,
return (ret == KNOT_EOK && res == 0);
}
bool knot_match_key_ds(zone_key_t *key, const knot_rdata_t *rdata)
{
dnssec_binary_t rdata_bin = {
.data = knot_rdata_data(rdata),
.size = knot_rdata_rdlen(rdata)
};
return cds_rdata_match(key, &rdata_bin);
}
/*!
* \brief Check if DNSKEY/DS is present in public zone key set.
*/
......
......@@ -125,4 +125,6 @@ int knot_zone_sign_nsecs_in_changeset(const zone_keyset_t *zone_keys,
bool knot_zone_sign_rr_should_be_signed(const zone_node_t *node,
const knot_rrset_t *rrset);
bool knot_match_key_ds(zone_key_t *key, const knot_rdata_t *rdata);
/*! @} */
......@@ -48,6 +48,7 @@ static const event_info_t EVENT_INFO[] = {
{ ZONE_EVENT_UTHAW, event_uthaw, "update thaw" },
{ ZONE_EVENT_NSEC3RESALT, event_nsec3resalt, "NSEC3 resalt" },
{ ZONE_EVENT_KEY_ROLLOVER, event_key_rollover, "KEY rollover" },
{ ZONE_EVENT_PARENT_DS_Q, event_parent_ds_q, "parent DS query" },
{ 0 }
};
......@@ -79,6 +80,7 @@ bool ufreeze_applies(zone_event_type_t type)
case ZONE_EVENT_DNSSEC:
case ZONE_EVENT_NSEC3RESALT:
case ZONE_EVENT_KEY_ROLLOVER:
case ZONE_EVENT_PARENT_DS_Q:
return true;
default:
return false;
......
......@@ -41,6 +41,7 @@ typedef enum zone_event_type {
ZONE_EVENT_UTHAW,
ZONE_EVENT_NSEC3RESALT,
ZONE_EVENT_KEY_ROLLOVER,
ZONE_EVENT_PARENT_DS_Q,
// terminator
ZONE_EVENT_COUNT,
} zone_event_type_t;
......
......@@ -41,3 +41,5 @@ int event_uthaw(conf_t *conf, zone_t *zone);
int event_nsec3resalt(conf_t *conf, zone_t *zone);
/*! \brief ZSK rollover related actions (key creation, publishing, deleting...). */
int event_key_rollover(conf_t *conf, zone_t *zone);
/*! \brief When CDS/CDNSKEY published, look for matching DS */
int event_parent_ds_q(conf_t *conf, zone_t *zone);
......@@ -29,7 +29,7 @@ int event_key_rollover(conf_t *conf, zone_t *zone)
return ret;
}
ret = knot_dnssec_key_rollover(&kctx, &keys_updated, &next_rollover);
ret = knot_dnssec_key_rollover(&kctx, zone, &keys_updated, &next_rollover);
kdnssec_ctx_deinit(&kctx);
if (next_rollover) {
......
/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include "knot/zone/zone.h"
#include "knot/common/log.h"
#include "knot/conf/conf.h"
#include "knot/dnssec/context.h"
#include "knot/dnssec/key-events.h"
#include "knot/dnssec/zone-keys.h"
#include "knot/dnssec/zone-sign.h" // match key and DS rdata
#include "knot/query/layer.h"
#include "knot/query/query.h"
#include "knot/query/requestor.h"
struct ds_query_data {
zone_t *zone;
conf_t *conf;
const struct sockaddr *remote;
zone_key_t *key;
bool ds_ok;
};
static int ds_query_begin(knot_layer_t *layer, void *params)
{
layer->data = params;
return KNOT_STATE_PRODUCE;
}
static int ds_query_produce(knot_layer_t *layer, knot_pkt_t *pkt)
{
struct ds_query_data *data = layer->data;
query_init_pkt(pkt);
int r = knot_pkt_put_question(pkt, data->zone->name, KNOT_CLASS_IN, KNOT_RRTYPE_DS);
if (r != KNOT_EOK) {
return KNOT_STATE_FAIL;
}
return KNOT_STATE_CONSUME;
}
static int ds_query_consume(knot_layer_t *layer, knot_pkt_t *pkt)
{
struct ds_query_data *data = layer->data;
if (knot_pkt_ext_rcode(pkt) != KNOT_RCODE_NOERROR) {
ns_log(LOG_WARNING, data->zone->name, LOG_OPERATION_PARENT,
LOG_DIRECTION_OUT, data->remote, "failed (%s)", knot_pkt_ext_rcode_name(pkt));
return KNOT_STATE_FAIL;
}
const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
bool match = false;
for (size_t j = 0; j < answer->count; j++) {
const knot_rrset_t *rr = knot_pkt_rr(answer, j);
if (!rr || rr->type != KNOT_RRTYPE_DS || rr->rrs.rr_count != 1) {
ns_log(LOG_WARNING, data->zone->name, LOG_OPERATION_PARENT,
LOG_DIRECTION_OUT, data->remote, "malformed message");
return KNOT_STATE_FAIL;
}
if (knot_match_key_ds(data->key, knot_rdataset_at(&rr->rrs, 0))) {
match = true;
break;
}
}
ns_log(LOG_INFO, data->zone->name, LOG_OPERATION_PARENT,
LOG_DIRECTION_OUT, data->remote, "KSK submittion attempt: %s",
(match ? "positive" : "negative"));
if (match) data->ds_ok = true;
return KNOT_STATE_DONE;
}
static const knot_layer_api_t ds_query_api = {
.begin = ds_query_begin,
.produce = ds_query_produce,
.consume = ds_query_consume,
.reset = NULL,
.finish = NULL,
};
static int try_ds(conf_t *conf, zone_t *zone, const conf_remote_t *parent, zone_key_t *key)
{
// TODO: Abstract interface to issue DNS queries. This is almost copy-pasted.
assert(zone);
assert(parent);
struct ds_query_data data = {
.zone = zone,
.conf = conf,
.remote = (struct sockaddr *)&parent->addr,
.key = key,
.ds_ok = false,
};
struct knot_requestor requestor;
knot_requestor_init(&requestor, &ds_query_api, &data, NULL);
knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
if (!pkt) {
knot_requestor_clear(&requestor);
return KNOT_ENOMEM;
}
const struct sockaddr *dst = (struct sockaddr *)&parent->addr;
const struct sockaddr *src = (struct sockaddr *)&parent->via;
struct knot_request *req = knot_request_make(NULL, dst, src, pkt, &parent->key, 0);
if (!req) {
knot_request_free(req, NULL);
knot_requestor_clear(&requestor);
return KNOT_ENOMEM;
}
int timeout = conf->cache.srv_tcp_reply_timeout * 1000;
int ret = knot_requestor_exec(&requestor, req, timeout);
knot_request_free(req, NULL);
knot_requestor_clear(&requestor);
// alternative: we could put answer back through ctx instead of errcode
if (ret == KNOT_EOK && !data.ds_ok) {
ret = KNOT_ENORECORD;
}
return ret;
}
static bool parents_have_ds(zone_t *zone, conf_t *conf, zone_key_t *key) {
conf_val_t policy = conf_zone_get(conf, C_DNSSEC_POLICY, zone->name);
uint8_t *policy_name = (uint8_t *)conf_str(&policy);
size_t policy_name_len = strlen((const char *)policy_name) + 1;
conf_val_t parents = conf_rawid_get(conf, C_POLICY, C_KSK_SUBMITTION_CHECK,
policy_name, policy_name_len);
bool success = false;
while (parents.code == KNOT_EOK) {
success = false;
conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, &parents);
size_t addr_count = conf_val_count(&addr);
for (size_t i = 0; i < addr_count; i++) {
conf_remote_t parent = conf_remote(conf, &parents, i);
int ret = try_ds(conf, zone, &parent, key);
if (ret == KNOT_EOK) {
success = true;
break;
}
}
if (!success) {
// TODO dnssec warning, or not ?
}
conf_val_next(&parents);
}
return success;
}
int event_parent_ds_q(conf_t *conf, zone_t *zone)
{
conf_val_t policy = conf_zone_get(conf, C_DNSSEC_POLICY, zone->name);
kdnssec_ctx_t ctx = { 0 };
int ret = kdnssec_ctx_init(&ctx, zone->name, &policy);
if (ret != KNOT_EOK) {
return ret;
}
zone_keyset_t keyset = { 0 };
ret = load_zone_keys(ctx.zone, ctx.keystore, false, ctx.now, &keyset);
if (ret != KNOT_EOK) {
return ret;
}
for (size_t i = 0; i < keyset.count; i++) {
zone_key_t *key = &keyset.keys[i];
if (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK &&
key->is_ready && !key->is_active) {
if (parents_have_ds(zone, conf, key)) {
ret = knot_dnssec_ksk_submittion_confirm(&ctx, dnssec_key_get_keytag(key->key)); // TODO get rid of keytag
} else {
ret = KNOT_ENOENT;
}
}
}
if (ret != KNOT_EOK) {
uint8_t *policy_name = (uint8_t *)conf_str(&policy);
size_t policy_name_len = strlen((const char *)policy_name) + 1;
conf_val_t check_interval = conf_rawid_get(conf, C_POLICY, C_KSK_SUBMITTION_CHECK_INTERVAL,
policy_name, policy_name_len);
time_t next_check = time(NULL) + conf_int(&check_interval);
zone_events_schedule_at(zone, ZONE_EVENT_PARENT_DS_Q, next_check);
} else {
zone_events_schedule_now(zone, ZONE_EVENT_DNSSEC);
}
free_zone_keys(&keyset);
kdnssec_ctx_deinit(&ctx);
return KNOT_EOK; // allways ok, if failure it has been rescheduled
}
......@@ -453,7 +453,7 @@ static int get_online_key(dnssec_key_t **key_ptr, knotd_mod_t *mod)
bool ignore1 = false;
time_t ignore2 = 0;
r = knot_dnssec_key_rollover(&kctx, &ignore1, &ignore2);
r = knot_dnssec_key_rollover(&kctx, NULL, &ignore1, &ignore2);
if (r != DNSSEC_EOK) {
goto fail;
}
......
......@@ -26,6 +26,7 @@ enum log_operation {
LOG_OPERATION_NOTIFY,
LOG_OPERATION_REFRESH,
LOG_OPERATION_UPDATE,
LOG_OPERATION_PARENT,
};
enum log_direction {
......@@ -46,6 +47,8 @@ static inline const char *log_operation_name(enum log_operation operation)
return "refresh";
case LOG_OPERATION_UPDATE:
return "DDNS";
case LOG_OPERATION_PARENT:
return "parent DS check";
default:
return "?";
}
......
......@@ -244,7 +244,12 @@ int zone_load_post(conf_t *conf, zone_t *zone, zone_contents_t *contents,
}
ignore1 = false; ignore2 = 0;
ret = knot_dnssec_key_rollover(&kctx, &ignore1, &ignore2);
ret = knot_dnssec_key_rollover(&kctx, zone, &ignore1, &ignore2);
if (zone_has_key_submittion(&kctx)) {
zone_events_schedule_now(zone, ZONE_EVENT_PARENT_DS_Q);
}
kdnssec_ctx_deinit(&kctx);
if (ret != KNOT_EOK) {
changeset_clear(&change);
......
$ORIGIN com.
$TTL 1200
@ SOA ns admin 20110100 7 7 16 600
ns AAAA ::0
$ORIGIN com.
$TTL 1200
@ SOA ns admin 20110101 25 25 80 600
ns AAAA ::0
example.com. 3600 DS 48031 7 2 19C30FF016E701DB8D9A600EEF5F29F3B5B5197AE78648AA8ED5308E341A7FFD
-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAsDQulN1onk4XnGGF
LhWmRudSBpBty4XbOyFBikeE9N3S1MMqzszuMbOeiXXdVwIcPgmDl5N1t3ClBJJg
SH++JQIDAQABAkAVegbDdkkuIm6WTPyipVjjA4bn7eq0B9i02FTYuebmwX7xg9rr
jcMRXEaA6yGqP62mvVWBNx91yaUvRji3RNqBAiEAxrljK01EgXdgE49l9SdV78Mf
A2e6con84vgbMb9B7S0CIQDi/Sj9WH1Q8DAqibaaM3mrUCHfOVBWeS2rdRbo4UJf
2QIhAI6PmkQLN1UFdYgyvDsF0BGj0dDYjhnzQdb1lFS41yu1AiEAtC0JvVfhWT7e
rNVVeb9EY8Sermb7KzjTFJdD0SUFH7kCIGvczpFwGty6p3MPatS2vc7A8Z8gvAe3
tE5uJQObc+Z+
-----END PRIVATE KEY-----
-----BEGIN PRIVATE KEY-----
MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAomNuj0gH5HYjdC0r
7i0mxtBp5rr9dzPPSUwS72ZCFPsq3b8sdfAh2cZsvGQgkYCPPZV0Zx+kUP3WyGFy
9xqiCQIDAQABAkA6KMKALpwlBurLwSHqu+EXc616JZ6CAtxKtCRT+ZvRR1GWcEJ6
O8TeIU4YjWK7N8CQcgmeCX5bKbAW/DxUdksBAiEAy3xujDpOidIhBbYwq/E6a3/Q
EnQ/FGfq6cw3hWI/4k0CIQDMS9ShKl/QouORUlW1zCKWIK/7sAHuncYx6E9pH7uE
rQIgRzrGg8XBSlNJBfPRs86ccZbrIhqZHi6GN9MpuEI9NJ0CIQCgNjuqpDN9x7AV
L+99YXgiKcI46/+n5F0gmGFmPHdvBQIgEQcjUUtC1+qDOlulSjvheIi/Al5c9SRs
AXhDSTXtN3A=
-----END PRIVATE KEY-----
-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAu9eosHX/Ag8J0r71
lT9tzQeWbEZRAOZY8w+zC4hqTvtAZeE9SsB+ppoNM8bvdaxLVQNIIKKqOxsteOZY
xMFicQIDAQABAkEAtXq84oeNsRqAXhjaQbB/T8gV31PsLNdfdq1jSTAprVVOmHSk
CfKq30FOdIXnlLum2kypxejpdHGocI1rqZLzBQIhAOoPNuh/k3NeEau2VZt9dENN
JL4ByVpMG2gMjiucHl57AiEAzXNc16CmvEfQ/i3JhEhbb1I8o7QGsOk9v8MP/DEz
pQMCIF8EcCjwaX6DKK9JpPUrd8A+l/TeqswSa2nQ9wIzLYzzAiEAzBl4+DV+rrjh
pEE0WpfPTe3yk+Z6ZzGuyFwt+ymd1qUCIBzE561e4uE5tyPB46ybM/029/GFa89z
0D1ZBKVF7AWi
-----END PRIVATE KEY-----
-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA0u57CJSNnnkVPSqx
tX+LqlnAEkroE+zadaZACE/AvYh2sKP+ktZxOphEN5wNXjI+zsfQTyyKazcHaZOT
XFlSIQIDAQABAkAYjdRaqlhWA0hap3aqZx+OBAac2oVrlF3V5jV8AjSwm1T4gbDq
iV+ARxIgqP6bAVa+rVC1hUQVAjRGI6fioSgBAiEA3zFS9A6salJdprW4Hxoudn5Q
oEidH2+tVaFmtmgW5sECIQDx78ZOQi7bmQz/s6Zp0cqcdLRHyfVojqwL5vtvHX+j
YQIgPk/uAfdqyZBPVzDaw8wydqCTb/x16YXrVcHnBDwRUMECIQDesivie08wedga
Qp5Kx35tt4r+jarkfHdnWU1VwVxs4QIhAJnSVxhAiKBW+RXuW+sW8NTVvCSjOp8y
uEK+Tq88J7qx
-----END PRIVATE KEY-----
#!/usr/bin/env python3
"""
Check if keytag conflict is correctly handled by Knot.
"""
import collections
import os
import shutil
import datetime
import subprocess
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_zone5(server, min_dnskeys, min_rrsigs, min_cdnskeys, msg):
dnskeys = server.dig("example.com", "DNSKEY")
found_dnskeys = dnskeys.count("DNSKEY")
soa = server.dig("example.com", "DNSKEY", dnssec=True)
found_rrsigs = soa.count("RRSIG")
cdnskey = server.dig("example.com", "CDNSKEY")
found_cdnskeys = cdnskey.count("CDNSKEY")
check_log("RRSIGs: %d (expected %d)" % (found_rrsigs, min_rrsigs));
check_log("DNSKEYs: %d (expected %d)" % (found_dnskeys, min_dnskeys));
check_log("CDNSKEYs: %d (expected %d)" % (found_cdnskeys, min_cdnskeys));
if found_rrsigs != min_rrsigs:
set_err("BAD RRSIG COUNT: " + msg)
detail_log("!RRSIGs not published and activated as expected: " + msg)
if found_dnskeys != min_dnskeys:
set_err("BAD DNSKEY COUNT: " + msg)
detail_log("!DNSKEYs not published and activated as expected: " + msg)
if found_cdnskeys != min_cdnskeys:
set_err("BAD CDNSKEY COUNT: " + msg)
detail_log("!CDNSKEYs not published and activated as expected: " + msg)
detail_log(SEP)
t = Test()
parent = t.server("knot")
parent_zone = t.zone("com.", storage=".")
t.link(parent_zone, parent)
child = t.server("knot")
child_zone = t.zone("example.com.")
t.link(child_zone, child)
child.dnssec(child_zone).enable = True
child.dnssec(child_zone).manual = False
child.dnssec(child_zone).zsk_lifetime = 99999
child.dnssec(child_zone).ksk_lifetime = 300 # this can be possibly left also infinity
child.dnssec(child_zone).propagation_delay = 17
child.dnssec(child_zone).ksk_submittion_check = [ parent ]
child.dnssec(child_zone).ksk_submittion_check_interval = 2
# install KASP db (one always enabled, one for testing)
shutil.copytree(os.path.join(t.data_dir, "keys"), child.keydir)
# parameters
ZONE = "example.com."
KSK1 = "7a3500c7feac3fd99f09a208a83b97f7455fa3e0"
KSK2 = "7e7492f7dcaf4d819a29eb30ad80c04f830d76cf"
ZSK1 = "6abddc73bcb46c4e6078cf764290ac315fff03f0"
ZSK2 = "301d3fc5392e83ea02312dc5bdc1a9f0b7937ddf"
t.rel_sleep(0)
# note that some of these paraneters will be immediately or later modified by automated key management
child.key_set(ZONE, KSK1, publish="t-2y", ready="t-1y", active="t-1y", retire="t+10y", remove="t+20y")
# KSK1's retire and remove shall be reconfigured by Knot to soon as KSK2 takes place
child.key_set(ZONE, KSK2, publish="t+0", ready="t+1h", active="t+10y", retire="t+11y", remove="t+12y")
child.key_set(ZONE, ZSK1, publish="t-20", ready="t-10", active="t-10", retire="t+15y", remove="t+20y")
# ZSK1 simply valid for all the time
child.key_set(ZONE, ZSK2, publish="t-2", ready="t+14y", active="t+14y", retire="t+31y", remove="t+36y")
# ZSK2 only reason: prevents Knot from publishing another ZSK
t.start()
child.zone_wait(child_zone)
check_zone5(child, 4, 1, 0, "only first KSK")
t.rel_sleep(19)
check_zone5(child, 4, 2, 1, "new KSK ready")
parent.update_zonefile(parent_zone, version=1)
parent.reload()
parent.zone_wait(parent_zone)
t.sleep(21)
check_zone5(child, 2, 1, 0, "old KSK retired")
t.end()
......@@ -44,6 +44,7 @@ class ZoneDnssec(object):
self.ksk_size = None
self.zsk_size = None
self.dnskey_ttl = None
self.ksk_lifetime = None
self.zsk_lifetime = None
self.propagation_delay = None
self.rrsig_lifetime = None
......@@ -52,6 +53,8 @@ class ZoneDnssec(object):
self.nsec3_iters = None
self.nsec3_salt_lifetime = None
self.nsec3_salt_len = None
self.ksk_submittion_check = []
self.ksk_submittion_check_interval = None
class Zone(object):
'''DNS zone description'''
......@@ -1031,6 +1034,15 @@ class Knot(Server):
if slave.tsig:
s.item_str("key", slave.tsig.name)
servers.add(slave.name)
for parent in z.dnssec.ksk_submittion_check:
if parent.name not in servers:
if not have_remote:
s.begin("remote")
have_remote = True
s.id_item("id", parent.name)
s.item_str("address", "%s@%s" % (parent.addr, parent.port))
servers.add(parent.name)
if have_remote:
s.end()
......@@ -1094,6 +1106,7 @@ class Knot(Server):
self._str(s, "ksk_size", z.dnssec.ksk_size)
self._str(s, "zsk_size", z.dnssec.zsk_size)
self._str(s, "dnskey-ttl", z.dnssec.dnskey_ttl)
self._str(s, "ksk-lifetime", z.dnssec.ksk_lifetime)
self._str(s, "zsk-lifetime", z.dnssec.zsk_lifetime)
self._str(s, "propagation-delay", z.dnssec.propagation_delay)
self._str(s, "rrsig-lifetime", z.dnssec.rrsig_lifetime)
......@@ -1102,6 +1115,14 @@ class Knot(Server):
self._str(s, "nsec3-iterations", z.dnssec.nsec3_iters)
self._str(s, "nsec3-salt-lifetime", z.dnssec.nsec3_salt_lifetime)
self._str(s, "nsec3-salt-length", z.dnssec.nsec3_salt_len)
if len(z.dnssec.ksk_submittion_check) > 0:
parents = ""
for parent in z.dnssec.ksk_submittion_check:
if parents:
parents += ", "
parents += parent.name
s.item("ksk-submittion-check", "[%s]" % parents)
self._str(s, "ksk-submittion-check-interval", z.dnssec.ksk_submittion_check_interval)
if have_policy:
s.end()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment