::arg().set("single-socket", "If set, only use a single socket for outgoing queries")="off";
::arg().set("auth-zones", "Zones for which we have authoritative data, comma separated domain=file pairs ")="";
::arg().set("lua-config-file", "More powerful configuration options")="";
+ ::arg().setSwitch("allow-trust-anchor-query", "Allow queries for trustanchor.server CH TXT")="yes";
::arg().set("forward-zones", "Zones for which we forward queries, comma separated domain=ip pairs")="";
::arg().set("forward-zones-recurse", "Zones for which we forward queries with recursion bit, comma separated domain=ip pairs")="";
In the PowerDNS Recursor, both positive and negative trust anchors can be configured during startup (from a persistent configuration file) and at runtime (which is volatile).
However, all trust anchors are configurable.
+Current trust anchors can be queried from the recursor by sending a query for "trustanchor.server CH TXT".
+This query will (if :ref:`setting-allow-trust-anchor-query` is enabled) return a TXT record per trust-anchor in the format ``"DOMAIN KEYTAG [KEYTAG]..."``.
+
Trust Anchors
^^^^^^^^^^^^^
The PowerDNS Recursor ships with the DNSSEC Root key built-in.
Answer questions for the ANY type on UDP with a truncated packet that refers the remote server to TCP.
Useful for mitigating ANY reflection attacks.
+.. _setting-allow-trust-anchor-query:
+
+``allow-trust-anchor-query``
+----------------------------
+.. versionadded:: 4.1.0
+
+- Boolean
+- Default: yes
+
+Allow ``trustanchor.server CH TXT`` queries to view the configured :doc:`DNSSEC <dnssec>` trust anchors.
+
.. _setting-api-config-dir:
``api-config-dir``
* - version.bind. CH TXT
* - version.pdns. CH TXT
* - id.server. CH TXT
+ * - trustanchor.server CH TXT
*/
bool SyncRes::doSpecialNamesResolve(const DNSName &qname, const QType &qtype, const uint16_t qclass, vector<DNSRecord> &ret)
{
static const DNSName arpa("1.0.0.127.in-addr.arpa."), ip6_arpa("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa."),
- localhost("localhost."), versionbind("version.bind."), idserver("id.server."), versionpdns("version.pdns.");
+ localhost("localhost."), versionbind("version.bind."), idserver("id.server."), versionpdns("version.pdns."), trustanchorserver("trustanchor.server.");
bool handled = false;
vector<pair<QType::typeenum, string> > answers;
}
}
+ if (qname == trustanchorserver && qclass == QClass::CHAOS &&
+ ::arg().mustDo("allow-trust-anchor-query")) {
+ handled = true;
+ if (qtype == QType::TXT || qtype == QType::ANY) {
+ auto luaLocal = g_luaconfs.getLocal();
+ for (auto const &dsAnchor : luaLocal->dsAnchors) {
+ ostringstream ans;
+ ans<<"\"";
+ ans<<dsAnchor.first.toString(); // Explicit toString to have a trailing dot
+ for (auto const &dsRecord : dsAnchor.second) {
+ ans<<" ";
+ ans<<dsRecord.d_tag;
+ }
+ ans << "\"";
+ answers.push_back({QType::TXT, ans.str()});
+ }
+ }
+ }
+
if (handled && !answers.empty()) {
ret.clear();
d_wasOutOfBand=true;
--- /dev/null
+import dns
+from recursortests import RecursorTest
+
+
+class testTrustAnchorsEnabled(RecursorTest):
+ """This test will do a query for "trustanchor.server CH TXT" and hopes to get
+ a proper answer"""
+
+ _auth_zones = None
+ _confdir = 'TrustAnchorsEnabled'
+ _roothints = None
+ _root_DS = None
+ _lua_config_file = """
+addDS("powerdns.com", "44030 8 1 B763646757DF621DD1204AD3BFA0675B49BE3279")
+"""
+
+ def testTrustanchorDotServer(self):
+ expected = dns.rrset.from_text_list(
+ 'trustanchor.server.', 86400, dns.rdataclass.CH, 'TXT',
+ ['". 19036 20326"', '"powerdns.com. 44030"'])
+ query = dns.message.make_query('trustanchor.server', 'TXT',
+ dns.rdataclass.CH)
+ result = self.sendUDPQuery(query)
+
+ self.assertRcodeEqual(result, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(result, expected)
+
+
+class testTrustAnchorsDisabled(RecursorTest):
+ """This test will do a query for "trustanchor.server CH TXT" and hopes to get
+ a proper answer"""
+
+ _auth_zones = None
+ _confdir = 'TrustAnchorsDisabled'
+ _roothints = None
+ _root_DS = None
+ _config_template = """
+ allow-trust-anchor-query=no
+"""
+
+ def testTrustanchorDotServer(self):
+ query = dns.message.make_query('trustanchor.server', 'TXT',
+ dns.rdataclass.CH)
+ result = self.sendUDPQuery(query)
+
+ self.assertRcodeEqual(result, dns.rcode.SERVFAIL)