diff --git a/daemon/bindings.c b/daemon/bindings.c
index bde8d7a319afeb6035a26644f277fb28c92add3e..742510b16c75ad2ecbfe6e33c9af5b9c2aa4aecd 100644
--- a/daemon/bindings.c
+++ b/daemon/bindings.c
@@ -1226,6 +1226,10 @@ static int wrk_resolve(lua_State *L)
 	if (options->DNSSEC_WANT) {
 		knot_edns_set_do(pkt->opt_rr);
 	}
+	if (options->DNSSEC_CD) {
+		knot_wire_set_cd(pkt->wire);
+	}
+
 	if (lua_isfunction(L, 5)) {
 		/* Store callback in registry */
 		lua_pushvalue(L, 5);
diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua
index 8410f21bff306f12efd6133f4a08fff7413621a4..4dc309d5b436f6656a2e052a7bf9250f8751825e 100644
--- a/daemon/lua/kres-gen.lua
+++ b/daemon/lua/kres-gen.lua
@@ -77,6 +77,7 @@ struct kr_qflags {
 	_Bool DNSSEC_WANT : 1;
 	_Bool DNSSEC_BOGUS : 1;
 	_Bool DNSSEC_INSECURE : 1;
+	_Bool DNSSEC_CD : 1;
 	_Bool STUB : 1;
 	_Bool ALWAYS_CUT : 1;
 	_Bool DNSSEC_WEXPAND : 1;
@@ -217,6 +218,8 @@ void knot_rrset_init_empty(knot_rrset_t *);
 uint32_t knot_rrset_ttl(const knot_rrset_t *);
 int knot_rrset_txt_dump(const knot_rrset_t *, char **, size_t *, const knot_dump_style_t *);
 int knot_rrset_txt_dump_data(const knot_rrset_t *, const size_t, char *, const size_t, const knot_dump_style_t *);
+uint32_t knot_rrsig_sig_expiration(const knot_rdataset_t *, size_t);
+uint32_t knot_rrsig_sig_inception(const knot_rdataset_t *, size_t);
 const knot_dname_t *knot_pkt_qname(const knot_pkt_t *);
 uint16_t knot_pkt_qtype(const knot_pkt_t *);
 uint16_t knot_pkt_qclass(const knot_pkt_t *);
diff --git a/daemon/lua/kres-gen.sh b/daemon/lua/kres-gen.sh
index 865cd1ce6c706c6804db2b9e570659387e07f2d9..f6625c52b9a9a1114b9b25bc28428c58a640b04c 100755
--- a/daemon/lua/kres-gen.sh
+++ b/daemon/lua/kres-gen.sh
@@ -96,6 +96,8 @@ printf "\tchar _stub[];\n};\n"
 	knot_rrset_ttl
 	knot_rrset_txt_dump
 	knot_rrset_txt_dump_data
+	knot_rrsig_sig_expiration
+	knot_rrsig_sig_inception
 # Packet
 	knot_pkt_qname
 	knot_pkt_qtype
diff --git a/daemon/lua/sandbox.lua b/daemon/lua/sandbox.lua
index 08be5101750cd66cac66ceb67e61e42e97446035..f90473ffb1c2d6ac6602929aeb9831eefb30348b 100644
--- a/daemon/lua/sandbox.lua
+++ b/daemon/lua/sandbox.lua
@@ -205,6 +205,7 @@ end
 -- Load embedded modules
 modules.load('ta_signal_query')
 modules.load('priming')
+modules.load('detect_time_skew')
 
 -- Interactive command evaluation
 function eval_cmd(line, raw)
diff --git a/doc/modules.rst b/doc/modules.rst
index 709fee8ed0b3edec4d3999c46c7953e95ca0cb93..f8e69edc1963c9bc0c0dc7043f3a78deb0551832 100644
--- a/doc/modules.rst
+++ b/doc/modules.rst
@@ -27,3 +27,4 @@ Knot DNS Resolver modules
 .. include:: ../modules/dnstap/README.rst
 .. include:: ../modules/ta_signal_query/README.rst
 .. include:: ../modules/priming/README.rst
+.. include:: ../modules/detect_time_skew/README.rst
diff --git a/lib/rplan.h b/lib/rplan.h
index 9a0998474aa6b55e5207cbcf9bc7a147fc91adbf..f87d7f2535368e25595c7302f45f3ed074d80870 100644
--- a/lib/rplan.h
+++ b/lib/rplan.h
@@ -44,6 +44,7 @@ struct kr_qflags {
 				  * i.e. knot_wire_set_cd(request->answer->wire). */
 	bool DNSSEC_BOGUS : 1;   /**< Query response is DNSSEC bogus. */
 	bool DNSSEC_INSECURE : 1;/**< Query response is DNSSEC insecure. */
+	bool DNSSEC_CD : 1;      /**< CD bit in query */
 	bool STUB : 1;           /**< Stub resolution, accept received answer as solved. */
 	bool ALWAYS_CUT : 1;     /**< Always recover zone cut (even if cached). */
 	bool DNSSEC_WEXPAND : 1; /**< Query response has wildcard expansion. */
diff --git a/modules/detect_time_skew/README.rst b/modules/detect_time_skew/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..87e45fe6fc04fc817fc756eb269c15ddd34d0cc5
--- /dev/null
+++ b/modules/detect_time_skew/README.rst
@@ -0,0 +1,21 @@
+.. _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 0000000000000000000000000000000000000000..ec84e595baa212da675a9ab112430cf833215843
--- /dev/null
+++ b/modules/detect_time_skew/detect_time_skew.lua
@@ -0,0 +1,77 @@
+-- Module interface
+local ffi = require('ffi')
+local knot = ffi.load(libknot_SONAME)
+
+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)
+	pkt = kres.pkt_t(pkt)
+	if pkt:rcode() ~= kres.rcode.NOERROR then
+		warn("[detect_time_skew] cannot resolve '.' NS")
+		return nil
+	end
+	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]
+		if rr.type == kres.type.RRSIG then
+			for k = 0, rr.rrs.rr_count - 1 do
+				inception = knot.knot_rrsig_sig_inception(rr.rrs, k)
+				expiration = knot.knot_rrsig_sig_expiration(rr.rrs, k)
+				if now > expiration then
+					-- possitive 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 valid_rrsigs == 0 then
+		warn("[detect_time_skew] 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 "yet" or "anymore")
+	elseif verbose() then
+		log("[detect_time_skew] 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
+
+-- Make priming query and check time validty of RRSIGs.
+local function check_time()
+	resolve(".", kres.type.NS, kres.class.IN, {"DNSSEC_WANT", "DNSSEC_CD"},
+                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
diff --git a/modules/detect_time_skew/detect_time_skew.mk b/modules/detect_time_skew/detect_time_skew.mk
new file mode 100644
index 0000000000000000000000000000000000000000..bc29deb32d5a4620afafcad1cb19b741273c94ba
--- /dev/null
+++ b/modules/detect_time_skew/detect_time_skew.mk
@@ -0,0 +1,2 @@
+detect_time_skew_SOURCES := detect_time_skew.lua
+$(call make_lua_module,detect_time_skew)
diff --git a/modules/modules.mk b/modules/modules.mk
index 23d597d116d4cdb3757a85f330a17684116fdd93..3df953c0a623bf8eb50d9b83f05b18c677ee0325 100644
--- a/modules/modules.mk
+++ b/modules/modules.mk
@@ -34,7 +34,8 @@ modules_TARGETS += etcd \
                    workarounds \
                    version \
                    ta_signal_query \
-                   priming
+                   priming \
+                   detect_time_skew
 endif
 
 # Make C module
diff --git a/tests/deckard b/tests/deckard
index fcfade5d1805b7c1151523991e4d5ea68db03f03..a5cd67c98836690e00ab76b7bd220023f7993ee9 160000
--- a/tests/deckard
+++ b/tests/deckard
@@ -1 +1 @@
-Subproject commit fcfade5d1805b7c1151523991e4d5ea68db03f03
+Subproject commit a5cd67c98836690e00ab76b7bd220023f7993ee9