]> granicus.if.org Git - pdns/commitdiff
rec: automatically reload trust anchors from file
authorPieter Lexis <pieter.lexis@powerdns.com>
Mon, 15 Oct 2018 10:24:59 +0000 (12:24 +0200)
committerPieter Lexis <pieter.lexis@powerdns.com>
Wed, 17 Oct 2018 08:28:02 +0000 (10:28 +0200)
This allows external processec to update TA's without having to restart
the recursor.

pdns/pdns_recursor.cc
pdns/rec-lua-conf.cc
pdns/rec-lua-conf.hh
pdns/recursordist/docs/dnssec.rst
pdns/recursordist/docs/lua-config/dnssec.rst
pdns/validate-recursor.cc
pdns/validate-recursor.hh
regression-tests.recursor-dnssec/test_ReadTrustAnchorsFromFile.py

index 3c7ec0ee31e843d1c59d7453022af3fc568fac76..3b765c304b8d760ee9728236e4fb64e3f6550225 100644 (file)
@@ -2516,9 +2516,16 @@ static void doStats(void)
 
 static void houseKeeping(void *)
 {
-  static thread_local time_t last_rootupdate, last_prune, last_secpoll;
+  static thread_local time_t last_rootupdate, last_prune, last_secpoll, last_trustAnchorUpdate{0};
   static thread_local int cleanCounter=0;
   static thread_local bool s_running;  // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
+  auto luaconfsLocal = g_luaconfs.getLocal();
+
+  if (last_trustAnchorUpdate == 0 && !luaconfsLocal->trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) {
+    // Loading the Lua config file already "refreshed" the TAs
+    last_trustAnchorUpdate = g_now.tv_sec + luaconfsLocal->trustAnchorFileInfo.interval * 3600;
+  }
+
   try {
     if(s_running)
       return;
@@ -2571,8 +2578,24 @@ static void houseKeeping(void *)
           g_log<<Logger::Error<<"Exception while performing security poll"<<endl;
         }
       }
+
+      if (!luaconfsLocal->trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0 &&
+          g_now.tv_sec - last_trustAnchorUpdate >= (luaconfsLocal->trustAnchorFileInfo.interval * 3600)) {
+        g_log<<Logger::Debug<<"Refreshing Trust Anchors from file"<<endl;
+        try {
+          map<DNSName, dsmap_t> dsAnchors;
+          if (updateTrustAnchorsFromFile(luaconfsLocal->trustAnchorFileInfo.fname, dsAnchors)) {
+            g_luaconfs.modify([&dsAnchors](LuaConfigItems& lci) {
+                lci.dsAnchors = dsAnchors;
+            });
+          }
+          last_trustAnchorUpdate = now.tv_sec;
+        } catch (const PDNSException &pe) {
+          g_log<<Logger::Error<<"Unable to update Trust Anchors: "<<pe.reason<<endl;
+        }
+      }
+      s_running=false;
     }
-    s_running=false;
   }
   catch(PDNSException& ae)
     {
index 76e12228368bb78f7d39d5e685be99e850b40f10..dfa539841e2dbc1df3911a06f33d6f4b7e8c3286 100644 (file)
@@ -15,9 +15,6 @@
 #include "validate.hh"
 #include "validate-recursor.hh"
 #include "root-dnssec.hh"
-#include "dnssecinfra.hh"
-#include "dnsseckeeper.hh"
-#include "zoneparser-tng.hh"
 
 GlobalStateHolder<LuaConfigItems> g_luaconfs; 
 
@@ -411,37 +408,15 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de
         lci.negAnchors.clear();
     });
 
-  Lua.writeFunction("readTrustAnchorsFromFile", [&lci](const std::string& fname) {
-      warnIfDNSSECDisabled("Warning: reading Trust Anchors from file (readTrustAnchorsFromFile), but dnssec is set to 'off'!");
-      auto zp = ZoneParserTNG(fname);
-      DNSResourceRecord rr;
-      DNSRecord dr;
-      try {
-        while(zp.get(rr)) {
-          dr = DNSRecord(rr);
-          if (rr.qtype == QType::DS) {
-            auto dsr = getRR<DSRecordContent>(dr);
-            if (dsr == nullptr) {
-              throw PDNSException("Unable to parse DS record '" + rr.qname.toString() + " " + rr.getZoneRepresentation() + "'");
-            }
-            lci.dsAnchors[rr.qname].insert(*dsr);
-          }
-          if (rr.qtype == QType::DNSKEY) {
-            auto dnskeyr = getRR<DNSKEYRecordContent>(dr);
-            if (dnskeyr == nullptr) {
-              throw PDNSException("Unable to parse DNSKEY record '" + rr.qname.toString() + " " + rr.getZoneRepresentation() +"'");
-            }
-            auto dsr = makeDSFromDNSKey(rr.qname, *dnskeyr, DNSSECKeeper::SHA256);
-            lci.dsAnchors[rr.qname].insert(dsr);
-          }
-        }
-      }
-      catch (const std::exception &e) {
-        throw PDNSException("Error while reading Trust Anchors from file (readTrustAnchorsFromFile) '" + fname + "': " + e.what());
-      }
-      catch (...) {
-        throw PDNSException("Error while reading Trust Anchors from file (readTrustAnchorsFromFile) '" + fname + "'");
+  Lua.writeFunction("readTrustAnchorsFromFile", [&lci](const std::string& fname, const boost::optional<uint32_t> interval) {
+      uint32_t realInterval = 24;
+      if (interval) {
+        realInterval = static_cast<uint32_t>(*interval);
       }
+      warnIfDNSSECDisabled("Warning: reading Trust Anchors from file (readTrustAnchorsFromFile), but dnssec is set to 'off'!");
+      lci.trustAnchorFileInfo.fname = fname;
+      lci.trustAnchorFileInfo.interval = realInterval;
+      updateTrustAnchorsFromFile(fname, lci.dsAnchors);
     });
 
 #if HAVE_PROTOBUF
index fad49ddd19f313d8e9b00374ac166fe6ca2f3174..a47077a2804df4e6c0fdd6cd1fc2114555d4b872 100644 (file)
@@ -41,12 +41,18 @@ struct ProtobufExportConfig
   bool taggedOnly{false};
 };
 
+struct TrustAnchorFileInfo {
+  uint32_t interval{24};
+  std::string fname;
+};
+
 class LuaConfigItems 
 {
 public:
   LuaConfigItems();
   SortList sortlist;
   DNSFilterEngine dfe;
+  TrustAnchorFileInfo trustAnchorFileInfo; // Used to update the Trust Anchors from file periodically
   map<DNSName,dsmap_t> dsAnchors;
   map<DNSName,std::string> negAnchors;
   ProtobufExportConfig protobufExportConfig;
index b5bd5b9a41d2668b54d9b9e475d2c31f91e80f01..7c54102c644fe024e0922111d629b0be29c747e7 100644 (file)
@@ -105,6 +105,7 @@ Reading trust anchors from files
 
 Since version 4.2.0 of the PowerDNS Recursor, it is also possible to read the Trust Anchors from a BIND-style zonefile.
 Only the DS and DNSKEY records from this file are read.
+This file is (by default) re-read every 24 hours for updates.
 Debian and its derivatives ship the ``dns-root-data`` package that contains the DNSSEC root trust anchors in ``/usr/share/dns/root.key``.
 
 To only use the distribution-provided Trust Anchors, add the following to the :ref:`setting-lua-config-file`:
index 17b189d339d358cc9a397ec1bac61f15e3b5cc60..f278c9af6ccb7ef2c8bc8f6a44443064f251058b 100644 (file)
@@ -57,7 +57,7 @@ This page only documents the Lua functions for DNSSEC configuration
 
   :param str name: The name in the DNS tree from where this NTA should be removed
 
-.. function:: readTrustAnchorsFromFile(fname)
+.. function:: readTrustAnchorsFromFile(fname[, interval])
 
   .. versionadded:: 4.2.0
 
@@ -65,3 +65,4 @@ This page only documents the Lua functions for DNSSEC configuration
   This function can be used to read distribution provided trust anchors, as for instance ``/usr/share/dns/root.key`` from Debian's ``dns-root-data`` package.
 
   :param str fname: Path to a zone file with Trust Anchors
+  :param int interval: Re-read this file every ``interval`` hours. By default this is set to 24. Set to 0 to disable automatic re-reads.
index 3299631318a1598f928db2ebb00c8d205ab19195..6b62e1b8b2ad1a7d554ecdb8559b718cc2e34e89 100644 (file)
@@ -2,6 +2,10 @@
 #include "validate-recursor.hh"
 #include "syncres.hh"
 #include "logger.hh"
+#include "rec-lua-conf.hh"
+#include "dnssecinfra.hh"
+#include "dnsseckeeper.hh"
+#include "zoneparser-tng.hh"
 
 DNSSECMode g_dnssecmode{DNSSECMode::ProcessNoValidate};
 bool g_dnssecLogBogus;
@@ -24,3 +28,44 @@ vState increaseDNSSECStateCounter(const vState& state)
   g_stats.dnssecResults[state]++;
   return state;
 }
+
+// Returns true if dsAnchors were modified
+bool updateTrustAnchorsFromFile(const std::string &fname, map<DNSName, dsmap_t> &dsAnchors) {
+  map<DNSName,dsmap_t> newDSAnchors;
+  try {
+    auto zp = ZoneParserTNG(fname);
+    DNSResourceRecord rr;
+    DNSRecord dr;
+    while(zp.get(rr)) {
+      dr = DNSRecord(rr);
+      if (rr.qtype == QType::DS) {
+        auto dsr = getRR<DSRecordContent>(dr);
+        if (dsr == nullptr) {
+          throw PDNSException("Unable to parse DS record '" + rr.qname.toString() + " " + rr.getZoneRepresentation() + "'");
+        }
+        newDSAnchors[rr.qname].insert(*dsr);
+      }
+      if (rr.qtype == QType::DNSKEY) {
+        auto dnskeyr = getRR<DNSKEYRecordContent>(dr);
+        if (dnskeyr == nullptr) {
+          throw PDNSException("Unable to parse DNSKEY record '" + rr.qname.toString() + " " + rr.getZoneRepresentation() +"'");
+        }
+        auto dsr = makeDSFromDNSKey(rr.qname, *dnskeyr, DNSSECKeeper::SHA256);
+        newDSAnchors[rr.qname].insert(dsr);
+      }
+    }
+    if (dsAnchors == newDSAnchors) {
+      g_log<<Logger::Debug<<"Read Trust Anchors from file, no changes detected"<<endl;
+      return false;
+    }
+    g_log<<Logger::Info<<"Read changed Trust Anchors from file, updating"<<endl;
+    dsAnchors = newDSAnchors;
+    return true;
+  }
+  catch (const std::exception &e) {
+    throw PDNSException("Error while reading Trust Anchors from file '" + fname + "': " + e.what());
+  }
+  catch (...) {
+    throw PDNSException("Error while reading Trust Anchors from file '" + fname + "'");
+  }
+}
index bc589ba1f1d9e3aaf4050bc52ebc3fb648432490..88ed15e66c0832c04bfeeb73bbc8a331586e7d73 100644 (file)
@@ -38,3 +38,4 @@ extern bool g_dnssecLogBogus;
 bool checkDNSSECDisabled();
 bool warnIfDNSSECDisabled(const string& msg);
 vState increaseDNSSECStateCounter(const vState& state);
+bool updateTrustAnchorsFromFile(const std::string &fname, map<DNSName, dsmap_t> &dsAnchors);
index c150703ee9a9d8007cf3e0d7683f51a74d3ce952..913aa3ca6fdf8ad71ad9deaa720463042dddd453 100644 (file)
@@ -1,3 +1,4 @@
+from __future__ import print_function
 import os
 import subprocess
 from recursortests import RecursorTest
@@ -25,6 +26,6 @@ readTrustAnchorsFromFile('root.keys')"""
             self.assertEqual(ret, expected)
 
         except subprocess.CalledProcessError as e:
-            print e.output
+            print(e.output)
             raise