diff options
Diffstat (limited to 'modules/bogus_log')
-rw-r--r-- | modules/bogus_log/.packaging/test.config | 4 | ||||
-rw-r--r-- | modules/bogus_log/README.rst | 45 | ||||
-rw-r--r-- | modules/bogus_log/bogus_log.c | 135 | ||||
-rw-r--r-- | modules/bogus_log/meson.build | 22 | ||||
-rw-r--r-- | modules/bogus_log/test.integr/deckard.yaml | 13 | ||||
-rw-r--r-- | modules/bogus_log/test.integr/kresd_config.j2 | 90 | ||||
-rw-r--r-- | modules/bogus_log/test.integr/val_minimal_expiredsignature.rpl | 125 |
7 files changed, 434 insertions, 0 deletions
diff --git a/modules/bogus_log/.packaging/test.config b/modules/bogus_log/.packaging/test.config new file mode 100644 index 0000000..bf1c821 --- /dev/null +++ b/modules/bogus_log/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('bogus_log') +assert(bogus_log) +quit() diff --git a/modules/bogus_log/README.rst b/modules/bogus_log/README.rst new file mode 100644 index 0000000..d60c278 --- /dev/null +++ b/modules/bogus_log/README.rst @@ -0,0 +1,45 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-bogus_log: + +DNSSEC validation failure logging +================================= + +This module logs a message for each DNSSEC validation failure (on ``notice`` :func:`level <log_level>`). +It is meant to provide hint to operators which queries should be +investigated using diagnostic tools like DNSViz_. + +Add following line to your configuration file to enable it: + +.. code-block:: lua + + modules.load('bogus_log') + +Example of error message logged by this module: + +.. code-block:: none + + [dnssec] validation failure: dnssec-failed.org. DNSKEY + +.. _DNSViz: http://dnsviz.net/ + +List of most frequent queries which fail as DNSSEC bogus can be obtained at run-time: + +.. code-block:: lua + + > bogus_log.frequent() + { + { + ['count'] = 1, + ['name'] = 'dnssec-failed.org.', + ['type'] = 'DNSKEY', + }, + { + ['count'] = 13, + ['name'] = 'rhybar.cz.', + ['type'] = 'DNSKEY', + }, + } + +Please note that in future this module might be replaced +with some other way to log this information. diff --git a/modules/bogus_log/bogus_log.c b/modules/bogus_log/bogus_log.c new file mode 100644 index 0000000..7b36187 --- /dev/null +++ b/modules/bogus_log/bogus_log.c @@ -0,0 +1,135 @@ +/* Copyright (C) Knot Resolver contributors + * SPDX-License-Identifier: GPL-3.0-or-later + * + * This module logs (query name, type) pairs which failed DNSSEC validation. */ + +#include <libknot/packet/pkt.h> +#include <libknot/dname.h> +#include <ccan/json/json.h> +#include <contrib/cleanup.h> + +#include "daemon/engine.h" +#include "lib/layer.h" +#include "lib/generic/lru.h" + +#ifdef LRU_REP_SIZE + #define FREQUENT_COUNT LRU_REP_SIZE /* Size of frequent tables */ +#else + #define FREQUENT_COUNT 5000 /* Size of frequent tables */ +#endif + +/** @internal LRU hash of most frequent names. */ +typedef lru_t(unsigned) namehash_t; + +/** @internal Stats data structure. */ +struct stat_data { + namehash_t *frequent; +}; + +static int consume(kr_layer_t *ctx, knot_pkt_t *pkt) +{ + if (!(ctx->state & KR_STATE_FAIL) + || !ctx->req + || !ctx->req->current_query + || !ctx->req->current_query->flags.DNSSEC_BOGUS + || knot_wire_get_qdcount(pkt->wire) != 1) + return ctx->state; + + auto_free char *qname_text = kr_dname_text(knot_pkt_qname(pkt)); + auto_free char *qtype_text = kr_rrtype_text(knot_pkt_qtype(pkt)); + + kr_log_notice(DNSSEC, "validation failure: %s %s\n", qname_text, qtype_text); + + /* log of most frequent bogus queries */ + uint16_t type = knot_pkt_qtype(pkt); + char key[sizeof(type) + KNOT_DNAME_MAXLEN]; + memcpy(key, &type, sizeof(type)); + int key_len = knot_dname_to_wire((uint8_t *)key + sizeof(type), knot_pkt_qname(pkt), KNOT_DNAME_MAXLEN); + if (key_len >= 0) { + struct kr_module *module = ctx->api->data; + struct stat_data *data = module->data; + unsigned *count = lru_get_new(data->frequent, key, key_len+sizeof(type), NULL); + if (count) + *count += 1; + } + + return ctx->state; +} + +/** @internal Helper for dump_list: add a single namehash_t item to JSON. */ +static enum lru_apply_do dump_value(const char *key, uint len, unsigned *val, void *baton) +{ + uint16_t key_type = 0; + /* Extract query name, type and counter */ + memcpy(&key_type, key, sizeof(key_type)); + KR_DNAME_GET_STR(key_name, (uint8_t *)key + sizeof(key_type)); + KR_RRTYPE_GET_STR(type_str, key_type); + + /* Convert to JSON object */ + JsonNode *json_val = json_mkobject(); + json_append_member(json_val, "count", json_mknumber(*val)); + json_append_member(json_val, "name", json_mkstring(key_name)); + json_append_member(json_val, "type", json_mkstring(type_str)); + json_append_element((JsonNode *)baton, json_val); + return LRU_APPLY_DO_NOTHING; // keep the item +} + +/** + * List frequent names. + * + * Output: [{ count: <counter>, name: <qname>, type: <qtype>}, ... ] + */ +static char* dump_list(void *env, struct kr_module *module, const char *args, namehash_t *table) +{ + if (!table) { + return NULL; + } + JsonNode *root = json_mkarray(); + lru_apply(table, dump_value, root); + char *ret = json_encode(root); + json_delete(root); + return ret; +} + +static char* dump_frequent(void *env, struct kr_module *module, const char *args) +{ + struct stat_data *data = module->data; + return dump_list(env, module, args, data->frequent); +} + +KR_EXPORT +int bogus_log_init(struct kr_module *module) +{ + static kr_layer_api_t layer = { + .consume = &consume, + }; + layer.data = module; + module->layer = &layer; + + static const struct kr_prop props[] = { + { &dump_frequent, "frequent", "List most frequent queries.", }, + { NULL, NULL, NULL } + }; + module->props = props; + + struct stat_data *data = calloc(1, sizeof(*data)); + if (!data) { + return kr_error(ENOMEM); + } + module->data = data; + lru_create(&data->frequent, FREQUENT_COUNT, NULL, NULL); + return kr_ok(); +} + +KR_EXPORT +int bogus_log_deinit(struct kr_module *module) +{ + struct stat_data *data = module->data; + if (data) { + lru_free(data->frequent); + free(data); + } + return kr_ok(); +} + +KR_MODULE_EXPORT(bogus_log) diff --git a/modules/bogus_log/meson.build b/modules/bogus_log/meson.build new file mode 100644 index 0000000..e2faed5 --- /dev/null +++ b/modules/bogus_log/meson.build @@ -0,0 +1,22 @@ +# C module: bogus_log +# SPDX-License-Identifier: GPL-3.0-or-later + +bogus_log_src = files([ + 'bogus_log.c', +]) +c_src_lint += bogus_log_src + +bogus_log_mod = shared_module( + 'bogus_log', + bogus_log_src, + dependencies: libknot, + include_directories: mod_inc_dir, + name_prefix: '', + install: true, + install_dir: modules_dir, + link_with: kresd, +) + +integr_tests += [ + ['bogus_log', meson.current_source_dir() / 'test.integr'], +] diff --git a/modules/bogus_log/test.integr/deckard.yaml b/modules/bogus_log/test.integr/deckard.yaml new file mode 100644 index 0000000..ecd6cc5 --- /dev/null +++ b/modules/bogus_log/test.integr/deckard.yaml @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +programs: +- name: kresd + binary: kresd + additional: + - --noninteractive + templates: + - modules/bogus_log/test.integr/kresd_config.j2 + - tests/integration/hints_zone.j2 + configs: + - config + - hints +noclean: True diff --git a/modules/bogus_log/test.integr/kresd_config.j2 b/modules/bogus_log/test.integr/kresd_config.j2 new file mode 100644 index 0000000..0471a27 --- /dev/null +++ b/modules/bogus_log/test.integr/kresd_config.j2 @@ -0,0 +1,90 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +local ffi = require('ffi') + +{% for TAF in TRUST_ANCHOR_FILES %} +trust_anchors.add_file('{{TAF}}') +{% endfor %} + +{% raw %} +modules.load('bogus_log') + +function check_stats(got) + log_info(ffi.C.LOG_GRP_TESTS, 'checking if bogus_log.frequent values match expected values:') + local expected = { + [1] = { + ['type'] = 'DNSKEY', + ['count'] = 2, + ['name'] = '.', + } + } + print(table_print(expected)) + + if table_print(expected) == table_print(got) then + log_info(ffi.C.LOG_GRP_TESTS, 'no problem found') + return policy.DENY_MSG('Ok') + else + log_info(ffi.C.LOG_GRP_TESTS, 'mismatch!') + return policy.DENY_MSG('bogus_log.frequent mismatch, see logs') + end +end + +function reply_result(state, req) + local got = bogus_log.frequent() + print('current bogus_log.frequent() values:') + print(table_print(got)) + local result = check_stats(got) + return result(state, req) +end +policy.add(policy.pattern(reply_result, 'bogus_log.test.')) + +-- Disable RFC5011 TA update +if ta_update then + modules.unload('ta_update') +end + +-- Disable RFC8145 signaling, scenario doesn't provide expected answers +if ta_signal_query then + modules.unload('ta_signal_query') +end + +-- Disable RFC8109 priming, scenario doesn't provide expected answers +if priming then + modules.unload('priming') +end + +-- Disable this module because it make one priming query +if detect_time_skew then + modules.unload('detect_time_skew') +end + +_hint_root_file('hints') +cache.size = 2*MB +log_level('debug') +{% endraw %} + +net = { '{{SELF_ADDR}}' } + + +{% if QMIN == "false" %} +option('NO_MINIMIZE', true) +{% else %} +option('NO_MINIMIZE', false) +{% endif %} + + +-- Self-checks on globals +assert(help() ~= nil) +assert(worker.id ~= nil) +-- Self-checks on facilities +assert(cache.count() == 0) +assert(cache.stats() ~= nil) +assert(cache.backends() ~= nil) +assert(worker.stats() ~= nil) +assert(net.interfaces() ~= nil) +-- Self-checks on loaded stuff +assert(net.list()[1].transport.ip == '{{SELF_ADDR}}') +assert(#modules.list() > 0) +-- Self-check timers +ev = event.recurrent(1 * sec, function (ev) return 1 end) +event.cancel(ev) +ev = event.after(0, function (ev) return 1 end) diff --git a/modules/bogus_log/test.integr/val_minimal_expiredsignature.rpl b/modules/bogus_log/test.integr/val_minimal_expiredsignature.rpl new file mode 100644 index 0000000..46c228c --- /dev/null +++ b/modules/bogus_log/test.integr/val_minimal_expiredsignature.rpl @@ -0,0 +1,125 @@ +; SPDX-License-Identifier: GPL-3.0-or-later + trust-anchor: ". IN DS 49060 8 2 E7B1EB56D7D5791B3D45630FEAA9C823DB84B385ACEEAC5F44DD08885C36700F" + val-override-date: "20170410000000" + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. + query-minimization: off +CONFIG_END + +SCENARIO_BEGIN Date after expiration of signatures. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 + ADDRESS 2001:7fd::1 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. 518400 IN NS k.root-servers.net. +. 518400 IN RRSIG NS 8 0 518400 20170409093827 20170310093827 20661 . uBuJpbRh1NYVciSKK0r3SA6NFnqE4s/+CqLfTXu26/HrY5c1aOhQHXZM cCDDjfPGFa7Eh4mqF0i9I+i+bFbYQitI1Heexye599VE19REbVsK4qaU xkArvt9k6HVqd/7BXXUyzLN1N0CScdyuT5tiEI9154SDNVpnC+z8i2u0 9hW8JEk4qqVWX/I1MYQB/UOcFSeDhD1Qku/26opqDuLl/1eaShxhMQ/c rjzOb5ZYzD0x+TUJZMYSOMwAraaFuYTT84oe6QYY+EGctAk1b50nA/5E C3Tm/xGuo9ioVtYhTwoo1XDUVeHmghdILjQZvR4pOSZoRGGP9ovb08Qg OmPXuQ== +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +. IN DNSKEY +SECTION ANSWER +. IN DNSKEY 256 3 8 AwEAAe1oA46eOLNris1CtS0qM5TdMESK6i4hpalqa6JDv57eOUkaOeje ZW1tIFUokmaK7kuKEFEosddA89CYM8rt2RbC+sfKalbHAWOus0tXZyAL efb2sW95QRzyG6LNul0jQFn9eYWBUHrVe5Wqd0zrFCbTQLUhELSfrlkI UBpO/xKaGinRHX2JjyOnle4aPZY3bEVa/+KyY2ZU6UC4SBo3aHXanP26 ok91rOTmpTWp64ybsMdCXOU8deyuQFQf6q8DhIDmJrkymhX1MXWQQlE0 fAYIYf8/t9OCwucg8oEg4FPU8Gb4Zm/l6PgO4HFkFjBT6iGFCQt3qXe2 Qe3alUWoATc= +. IN DNSKEY 257 3 8 AwEAAb8sZgVVa02muJ+/+SVhJAvz2EWKGEGquhPbQXuF6ALBYoF4KWTO bZVF8sIVTGoaX5+UWkwwHthg7RwS1DALT/AJymYeHhUwA04gLsfCZ/cv BjmRy5RozeSJ1uxAhoCYHCT2hQBZ0cH0n8roXFXI2Y+6708pO1IBkTPT 9MpAGfezTtGYOortbSn+vqT/Zu8jOpNwkleXON4rlZRBZPd4JUMGL9Y5 N/j6+ClYeM+eFQTKXrLi1oC+0yK1sG5OlqrBDhAhBnz+IhfZz4TOkqJ9 Li2BVMatHBeB9GQHtu0FZuC3J0EQgiZxvq1RgkefFJAiB+5uVRN8U7up 5mLDxSgmT0M= +. 2592000 IN RRSIG DNSKEY 8 0 3600000 20170409093827 20170310093827 49060 . G7s3QiWNgOsl+LoG6OKjdBHPcFyhmCS17GFnaKjfJNdPQaFL5nM/vrXo eUIIdJXAvjj62TY7wTyFlnx3yjK93RVGKEEySpGC/1gkn5AdjVoQszog IxYjKzubizULSaX7SQ3/Ar+uHLxakdS1qgNdFu6hHCl857LJPtmC8SJt iFUmm5HFyARokMrfA88VrFRKEqojcCWajeZMfRtgBipFJZoYgPUCaFlz 8OupNdNUWCbGhnDWrXCWMzeKVXTQVlJf75PXXgtkuBUmr5RSWu7AYr+c wTJ4E4610goRqYxnZ33efKE/MuhKeY66xelPh0sirPrBMR5JAlyjV3k1 qDzhcQ== +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION ANSWER +k.root-servers.net. 3600000 IN AAAA 2001:7fd::1 +k.root-servers.net. 2592000 IN RRSIG AAAA 8 3 3600000 20170409093827 20170310093827 20661 . qjVXwuxzmoRhdrXyQvKfrrzFxGiYuTTJHxwZPasJ1nVmN48dPyU6wA55 JeqoJv1Jm+XvIL1q0WtX6Zh6KLt6vVjHuMkhmFuIZYkFi/dmsEwFY8C0 ebyXyztQT5+6FOSVTAKacYc40LfBo8FqEn8RYlCu1mkAd8ANvvLrdLWW W03LVOY7JlCzyrKlAlmPmuV8z+e9PxNkUh6KfTEvAReoAAX7wYZkdefg 2d64c7rNWXvYm6LxBX6qeQ39d5WyKc8v+G01DJuDzs2Tx368QoK86vm/ qo9ERdT7koRt+gBZNYv8V4fh2SjaFsy2TJq/tiYcSia9snGDTFj6LWVM 6sBCYQ== +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. 3600000 IN A 193.0.14.129 +k.root-servers.net. 2592000 IN RRSIG A 8 3 3600000 20170409093827 20170310093827 20661 . feIXXjskcsyH+ALZu67GVDaPWXjUGTWsTlDwzgJcLBzSuRVY/GVD5Z1Q B4/oUW99rLKB5bNS1MuasZ+nZFV67sBwJk1+SqNB2bAe7G5Tv1sR2Qgi qDAoB37YDVk5JGHfuxByLYbAVG9PrPXT60BN17OYrD/TFPzprye65gk3 7l9kPpAlblcsqdvh5piKrWc7VBcyMhlp56qdASNAl+Lrb+i0DZYyJXh+ b8LV5g5zp9FaVGKe0Gi4+yDXVjcM6VEtuNRAu2+flLoc3ho6qQF1Po4Y wueL72I+yFoUxkIOJvK47eWb+YUBIBK/L8/ORjYoLBRsrbc79wb0I3Zj Xy6O4Q== +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype +ADJUST copy_id copy_query +REPLY QR AA REFUSED +SECTION QUESTION +. IN RRSIG +SECTION ANSWER +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +. IN NS +ENTRY_END + + +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA DO SERVFAIL +SECTION QUESTION +. IN NS +SECTION ANSWER +SECTION AUTHORITY +SECTION ADDITIONAL +ENTRY_END + +STEP 11 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +. IN NS +ENTRY_END + +STEP 12 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA DO SERVFAIL +SECTION QUESTION +. IN NS +SECTION ANSWER +SECTION AUTHORITY +SECTION ADDITIONAL +ENTRY_END + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +bogus_log.test. IN TXT +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR AA RD RA DO NXDOMAIN +SECTION QUESTION +bogus_log.test. IN TXT +SECTION AUTHORITY +bogus_log.test. 10800 IN SOA bogus_log.test. nobody.invalid. 1 3600 1200 604800 10800 +SECTION ADDITIONAL +explanation.invalid. 10800 IN TXT "Ok" +ENTRY_END + +SCENARIO_END |