diff options
Diffstat (limited to '')
-rw-r--r-- | modules/detect_time_skew/.packaging/test.config | 4 | ||||
-rw-r--r-- | modules/detect_time_skew/README.rst | 23 | ||||
-rw-r--r-- | modules/detect_time_skew/detect_time_skew.lua | 83 |
3 files changed, 110 insertions, 0 deletions
diff --git a/modules/detect_time_skew/.packaging/test.config b/modules/detect_time_skew/.packaging/test.config new file mode 100644 index 0000000..3a37907 --- /dev/null +++ b/modules/detect_time_skew/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('detect_time_skew') +assert(detect_time_skew) +quit() diff --git a/modules/detect_time_skew/README.rst b/modules/detect_time_skew/README.rst new file mode 100644 index 0000000..be66bd0 --- /dev/null +++ b/modules/detect_time_skew/README.rst @@ -0,0 +1,23 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-detect_time_skew: + +System time skew detector +========================= + +This module compares local system time with inception and expiration time +bounds in DNSSEC signatures for ``. NS`` records. If the local system time is +outside of these bounds, it is likely a misconfiguration which will cause +all DNSSEC validation (and resolution) to fail. + +In case of mismatch, a warning message will be logged to help with +further diagnostics. + +.. warning:: Information printed by this module can be forged by a network attacker! + System administrator MUST verify values printed by this module and + fix local system time using a trusted source. + +This module is useful for debugging purposes. It runs only once during resolver +start does not anything after that. It is enabled by default. +You may disable the module by appending +``modules.unload('detect_time_skew')`` to your configuration. diff --git a/modules/detect_time_skew/detect_time_skew.lua b/modules/detect_time_skew/detect_time_skew.lua new file mode 100644 index 0000000..6669ce8 --- /dev/null +++ b/modules/detect_time_skew/detect_time_skew.lua @@ -0,0 +1,83 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- Module interface +local ffi = require('ffi') + +local mod = {} +local event_id = nil + +-- Resolve callback +-- Check time validity of RRSIGs in priming query +-- luacheck: no unused args +local function check_time_callback(pkt, req) + if pkt == nil or pkt:rcode() ~= kres.rcode.NOERROR then + log_warn(ffi.C.LOG_GRP_DETECTTIMESKEW, "cannot resolve '.' NS") + return nil + end + local seen_rrsigs = 0 + local valid_rrsigs = 0 + local section = pkt:rrsets(kres.section.ANSWER) + local now = os.time() + local time_diff = 0 + local inception = 0 + local expiration = 0 + for i = 1, #section do + local rr = section[i] + assert(rr.type) + if rr.type == kres.type.RRSIG then + for k = 0, rr.rrs.count - 1 do + seen_rrsigs = seen_rrsigs + 1 + local rdata = rr:rdata_pt(k) + inception = ffi.C.kr_rrsig_sig_inception(rdata) + expiration = ffi.C.kr_rrsig_sig_expiration(rdata) + if now > expiration then + -- positive value = in the future + time_diff = now - expiration + elseif now < inception then + -- negative value = in the past + time_diff = now - inception + else + valid_rrsigs = valid_rrsigs + 1 + end + end + end + end + if seen_rrsigs == 0 then + log_info(ffi.C.LOG_GRP_DETECTTIMESKEW, "No RRSIGs received! ".. + "You really should configure DNSSEC trust anchor for the root.") + elseif valid_rrsigs == 0 then + log_warn(ffi.C.LOG_GRP_DETECTTIMESKEW, "Local system time %q seems to be at ".. + "least %u seconds in the %s. DNSSEC signatures for '.' NS ".. + "are not valid %s. Please check your system clock!", + os.date("%c", now), + math.abs(time_diff), + time_diff > 0 and "future" or "past", + time_diff > 0 and "anymore" or "yet") + else + log_info(ffi.C.LOG_GRP_DETECTTIMESKEW, "Local system time %q is within ".. + "RRSIG validity interval <%q,%q>.", os.date("%c", now), + os.date("%c", inception), os.date("%c", expiration)) + end +end + +-- Do uncached priming query and check time validity of RRSIGs. +local function check_time() + resolve(".", kres.type.NS, kres.class.IN, {"DNSSEC_WANT", "DNSSEC_CD", "NO_CACHE"}, + check_time_callback) +end + +function mod.init() + if event_id then + error("Module is already loaded.") + else + event_id = event.after(0 , check_time) + end +end + +function mod.deinit() + if event_id then + event.cancel(event_id) + event_id = nil + end +end + +return mod |