summaryrefslogtreecommitdiffstats
path: root/modules/bogus_log
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--modules/bogus_log/README.rst41
-rw-r--r--modules/bogus_log/bogus_log.c150
-rw-r--r--modules/bogus_log/bogus_log.mk8
-rw-r--r--modules/bogus_log/test.integr/deckard.yaml13
-rw-r--r--modules/bogus_log/test.integr/kresd_config.j282
-rw-r--r--modules/bogus_log/test.integr/val_minimal_expiredsignature.rpl124
6 files changed, 418 insertions, 0 deletions
diff --git a/modules/bogus_log/README.rst b/modules/bogus_log/README.rst
new file mode 100644
index 0000000..0b93e82
--- /dev/null
+++ b/modules/bogus_log/README.rst
@@ -0,0 +1,41 @@
+.. _mod-bogus_log:
+
+DNSSEC validation failure logging
+---------------------------------
+
+This module adds error message for each DNSSEC validation failure.
+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()
+ [1] => {
+ [type] => DNSKEY
+ [count] => 1
+ [name] => dnssec-failed.org.
+ }
+ [2] => {
+ [type] => DNSKEY
+ [count] => 13
+ [name] => rhybar.cz.
+ }
+
+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..98c0fc9
--- /dev/null
+++ b/modules/bogus_log/bogus_log.c
@@ -0,0 +1,150 @@
+/* Copyright (C) Knot Resolver contributors. Licensed under GNU GPLv3 or
+ * (at your option) any later version. See COPYING for text of the license.
+ *
+ * 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"
+
+/** @internal Compatibility wrapper for Lua < 5.2 */
+#if LUA_VERSION_NUM < 502
+#define lua_rawlen(L, obj) lua_objlen((L), (obj))
+#endif
+
+#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_error("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)
+{
+ struct stat_data *data = malloc(sizeof(*data));
+ if (!data) {
+ return kr_error(ENOMEM);
+ }
+ memset(data, 0, sizeof(*data));
+ 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_EXPORT
+const kr_layer_api_t *bogus_log_layer(struct kr_module *module)
+{
+ static kr_layer_api_t _layer = {
+ .consume = &consume,
+ };
+ _layer.data = module;
+ return &_layer;
+}
+
+KR_EXPORT
+struct kr_prop *bogus_log_props(void)
+{
+ static struct kr_prop prop_list[] = {
+ { &dump_frequent, "frequent", "List most frequent queries.", },
+ { NULL, NULL, NULL }
+ };
+ return prop_list;
+}
+
+KR_MODULE_EXPORT(bogus_log)
diff --git a/modules/bogus_log/bogus_log.mk b/modules/bogus_log/bogus_log.mk
new file mode 100644
index 0000000..7431b19
--- /dev/null
+++ b/modules/bogus_log/bogus_log.mk
@@ -0,0 +1,8 @@
+bogus_log_CFLAGS := -fPIC
+# We use a symbol that's not in libkres but the daemon.
+# On darwin this isn't accepted by default.
+bogus_log_LDFLAGS := -Wl,-undefined -Wl,dynamic_lookup
+bogus_log_SOURCES := modules/bogus_log/bogus_log.c
+bogus_log_DEPEND := $(libkres)
+bogus_log_LIBS := $(contrib_TARGET) $(libkres_TARGET) $(libkres_LIBS)
+$(call make_c_module,bogus_log)
diff --git a/modules/bogus_log/test.integr/deckard.yaml b/modules/bogus_log/test.integr/deckard.yaml
new file mode 100644
index 0000000..a8c8700
--- /dev/null
+++ b/modules/bogus_log/test.integr/deckard.yaml
@@ -0,0 +1,13 @@
+programs:
+- name: kresd
+ binary: kresd
+ additional:
+ - -f
+ - "1"
+ templates:
+ - modules/bogus_log/test.integr/kresd_config.j2
+ - tests/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..6e3dc09
--- /dev/null
+++ b/modules/bogus_log/test.integr/kresd_config.j2
@@ -0,0 +1,82 @@
+{% for TAF in TRUST_ANCHOR_FILES %}
+trust_anchors.add_file('{{TAF}}')
+{% endfor %}
+
+{% raw %}
+modules.load('bogus_log')
+
+function check_stats(got)
+ log('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('no problem found')
+ return policy.DENY_MSG('Ok')
+ else
+ log('mismatch!')
+ return policy.DENY_MSG('bogus_log.frequent mismatch, see logs')
+ end
+end
+
+function reply_result(state, req)
+ local got = bogus_log.frequent()
+ log('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 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
+verbose(true)
+{% 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()['{{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..d0a616b
--- /dev/null
+++ b/modules/bogus_log/test.integr/val_minimal_expiredsignature.rpl
@@ -0,0 +1,124 @@
+ 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