summaryrefslogtreecommitdiffstats
path: root/modules/bogus_log
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:26:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:26:00 +0000
commit830407e88f9d40d954356c3754f2647f91d5c06a (patch)
treed6a0ece6feea91f3c656166dbaa884ef8a29740e /modules/bogus_log
parentInitial commit. (diff)
downloadknot-resolver-upstream/5.6.0.tar.xz
knot-resolver-upstream/5.6.0.zip
Adding upstream version 5.6.0.upstream/5.6.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/bogus_log')
-rw-r--r--modules/bogus_log/.packaging/test.config4
-rw-r--r--modules/bogus_log/README.rst45
-rw-r--r--modules/bogus_log/bogus_log.c135
-rw-r--r--modules/bogus_log/meson.build21
-rw-r--r--modules/bogus_log/test.integr/deckard.yaml13
-rw-r--r--modules/bogus_log/test.integr/kresd_config.j290
-rw-r--r--modules/bogus_log/test.integr/val_minimal_expiredsignature.rpl125
7 files changed, 433 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..2dcf87f
--- /dev/null
+++ b/modules/bogus_log/meson.build
@@ -0,0 +1,21 @@
+# 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,
+)
+
+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