diff options
Diffstat (limited to '')
-rw-r--r-- | modules/ta_signal_query/.packaging/test.config | 4 | ||||
-rw-r--r-- | modules/ta_signal_query/README.rst | 31 | ||||
-rw-r--r-- | modules/ta_signal_query/ta_signal_query.lua | 64 |
3 files changed, 99 insertions, 0 deletions
diff --git a/modules/ta_signal_query/.packaging/test.config b/modules/ta_signal_query/.packaging/test.config new file mode 100644 index 0000000..dfa7c2a --- /dev/null +++ b/modules/ta_signal_query/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('ta_signal_query') +assert(ta_signal_query) +quit() diff --git a/modules/ta_signal_query/README.rst b/modules/ta_signal_query/README.rst new file mode 100644 index 0000000..3136ecb --- /dev/null +++ b/modules/ta_signal_query/README.rst @@ -0,0 +1,31 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-ta_signal_query: + +Signaling Trust Anchor Knowledge in DNSSEC +========================================== + +The module for Signaling Trust Anchor Knowledge in DNSSEC Using Key Tag Query, +implemented according to :rfc:`8145#section-5`. + +This feature allows validating resolvers to signal to authoritative servers +which keys are referenced in their chain of trust. The data from such +signaling allow zone administrators to monitor the progress of rollovers +in a DNSSEC-signed zone. + +This mechanism serve to measure the acceptance and use of new DNSSEC +trust anchors and key signing keys (KSKs). This signaling data can be +used by zone administrators as a gauge to measure the successful deployment +of new keys. This is of particular interest for the DNS root zone in the event +of key and/or algorithm rollovers that rely on :rfc:`5011` to automatically +update a validating DNS resolver’s trust anchor. + +.. attention:: + Experience from root zone KSK rollover in 2018 shows that this mechanism + by itself is not sufficient to reliably measure acceptance of the new key. + Nevertheless, some DNS researchers found it is useful in combination + with other data so we left it enabled for now. This default might change + once more information is available. + +This module is enabled by default. You may use ``modules.unload('ta_signal_query')`` +in your configuration. diff --git a/modules/ta_signal_query/ta_signal_query.lua b/modules/ta_signal_query/ta_signal_query.lua new file mode 100644 index 0000000..72c1568 --- /dev/null +++ b/modules/ta_signal_query/ta_signal_query.lua @@ -0,0 +1,64 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- Module implementing RFC 8145 section 5 +-- Signaling Trust Anchor Knowledge in DNS using Key Tag Query +local kres = require('kres') +local ffi = require('ffi') + +local M = {} +M.layer = {} + +-- transform trust anchor keyset structure for one domain name (in wire format) +-- to signalling query name like _ta-keytag1-keytag2.example.com. +-- Returns: +-- string constructed from valid keytags +-- nil if no valid keytag is present in keyset +local function prepare_query_name(keyset, name) + if not keyset then return nil end + local keytags = {} + for _, key in ipairs(keyset) do + if key.state == "Valid" then + table.insert(keytags, key.key_tag) + end + end + if next(keytags) == nil then return nil end + + table.sort(keytags) + local query = "_ta" + for _, tag in pairs(keytags) do + query = string.format("%s-%04x", query, tag) + end + if name == "\0" then + return query .. "." + else + return query .. "." .. kres.dname2str(name) + end +end + +-- construct keytag query for valid keys and send it as asynchronous query +-- (does nothing if no valid keys are present at given domain name) +local function send_ta_query(domain) + local keyset = trust_anchors.keysets[domain] + local qname = prepare_query_name(keyset, domain) + if qname ~= nil then + log_info(ffi.C.LOG_GRP_TASIGNALING, "signalling query triggered: %s", qname) + -- asynchronous query + -- we do not care about result or from where it was obtained + event.after(0, function () + resolve(qname, kres.type.NULL, kres.class.IN, "NONAUTH") + end) + end +end + +-- act on DNSKEY queries which were not answered from cache +function M.layer.consume(state, req, pkt) + -- First check for standard "cached packets": PKT_SIZE_NOWIRE, for efficiency. + if pkt.size ~= -1 and pkt:qtype() == kres.type.DNSKEY then + local qry = req:current() + if not qry.flags.CACHED then + send_ta_query(qry:name()) + end + end + return state -- do not interfere with normal query processing +end + +return M |