string zonemdValidation = boost::get<string>(have.at("zonemd"));
auto it = nameToVal.find(zonemdValidation);
if (it == nameToVal.end()) {
- throw std::runtime_error(zonemdValidation + " is not a valid value for `zonemdValidation`");
+ throw std::runtime_error(zonemdValidation + " is not a valid value for `zonemd`");
}
else {
conf.d_zonemd = it->second;
}
}
- if (have.count("zonemdDNSSEC")) {
- string dnssec = boost::get<string>(have.at("zonemdDNSSEC"));
+ if (have.count("dnssec")) {
+ string dnssec = boost::get<string>(have.at("dnssec"));
auto it = nameToVal.find(dnssec);
if (it == nameToVal.end()) {
- throw std::runtime_error(dnssec + " is not a valid value for `zonemdDNSSEC`");
+ throw std::runtime_error(dnssec + " is not a valid value for `dnssec`");
}
else {
conf.d_dnssec = it->second;
zoneToCache(".", "url", "https://www.internic.net/domain/root.zone", { refreshPeriod = 0 })
+DNSSEC and ZONEMD validation
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Starting with version 4.7.0, the Recursor will do validation of the zone retrieved.
+Validation consists of two parts: ``DNSSEC`` and ``ZONEMD``.
+``ZONEMD`` is described in :rfc:`8976`.
+
+For the ``DNSSEC`` part, if the global :ref:`setting-dnssec` setting is not ``off`` or ``process-no-validate`` and the `DS` record from the parent zone or trust anchor indicates the zone is ``DNSSEC`` signed, the recursor will validate the ``DNSKEY`` records of the zone.
+If a ``ZONEMD`` record is present, it will also validate the ``ZONEMD`` record.
+If no ``ZONEMD`` is present, the ``NSEC`` or ``NSEC3`` denial of the ``ZONEMD`` record will be validated.
+Note that this is not a full validation of all signatures.
+The signatures of the remaining records will be verified on-demand once the records are inserted into the cache.
+
+For the ``ZONEMD`` part, if the zone has a ``ZONEMD`` record, the digest of the zone wil be verified.
+
+For both parts failure of validation will prevent the downloaded zone contents to be inserted into the cache.
+Absence of ``DNSSEC`` records is not considered a failure if the parent zone or negative trust anchor indicate the zone is ``Insecure``.
+Absence ``ZONEMD`` records is not considered a failure.
+This behaviour can be tuned with the ``zoneToCache`` specific `zonemd`_ and `dnssec`_ settings described below.
+
+
Configuration
^^^^^^^^^^^^^
.. function:: zoneToCache(zone, method, source [, settings ])
The source IP address to use when transferring using the ``axfr`` or ``url`` methods.
When unset, :ref:`setting-query-local-address` is used.
+zonemd
+~~~~~~
+
+.. versionadded:: 4.7.0
+
+A string. possible values: ``ignore``: ignore ZONEMD records, ``validate``: validate ``ZONEMD`` if present, ``require``: require valid ``ZONEMD`` record to be present.
+Default ``validate``.
+
+
+dnssec
+~~~~~~
+
+.. versionadded:: 4.7.0
+
+A string, possible values: ``ignore``: do not do ``DNSSEC`` validation, ``validate``: validate ``DNSSEC`` records as described above but accept an ``Insecure`` (unsigned) zone, ``require``: require ``DNSSEC`` validation, as described above.
+Default ``validate``.
+
+
File descriptor usage
^^^^^^^^^^^^^^^^^^^^^
The number of file descriptors used by the Recursor has increased because the Recursor now keeps idle outgoing TCP/DoT connections open for a while.
-The extra file descriptors used compared to previous versions of the Recursor is :ref:`setting-tcp-out-max-idle-per-thread` times the number of worker threads (:ref:`threads`).
+The extra file descriptors used compared to previous versions of the Recursor is :ref:`setting-tcp-out-max-idle-per-thread` times the number of worker threads (:ref:`setting-threads`).
New settings
^^^^^^^^^^^^
if (nsecs.records.size() > 0 && nsecs.signatures.size() > 0) {
nsecValidationStatus = validateWithKeySet(d_now, d_zone, nsecs.records, nsecs.signatures, validKeys);
csp.emplace(std::make_pair(d_zone, QType::NSEC), nsecs);
- } else if (nsec3s.records.size() > 0 && nsec3s.signatures.size() > 0) {
+ }
+ else if (nsec3s.records.size() > 0 && nsec3s.signatures.size() > 0) {
nsecValidationStatus = validateWithKeySet(d_now, d_zone, nsec3s.records, nsec3s.signatures, validKeys);
csp.emplace(std::make_pair(d_zone, QType::NSEC3), nsec3s);
- } else {
+ }
+ else {
d_log->info("No NSEC(3) records and/or RRSIGS found to deny ZONEMD");
return vState::BogusInvalidDenial;
}
}
if (config.d_dnssec == pdns::ZoneMD::Config::Require && g_dnssecmode == DNSSECMode::Off) {
- throw PDNSException("ZONEMD DNSSEC validation failure: dnssec is switched of but required by ZoneToCache");
+ throw PDNSException("ZONEMD DNSSEC validation failure: dnssec is switched off but required by ZoneToCache");
}
// Validate DNSKEYs and ZONEMD, rest of records are validated on-demand by SyncRes
- if (config.d_dnssec == pdns::ZoneMD::Config::Require || (g_dnssecmode != DNSSECMode::Off && config.d_dnssec != pdns::ZoneMD::Config::Ignore)) {
+ if (config.d_dnssec == pdns::ZoneMD::Config::Require || (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate && config.d_dnssec != pdns::ZoneMD::Config::Ignore)) {
size_t zonemdCount;
auto validationStatus = dnssecValidate(zonemd, zonemdCount);
d_log->info("ZONEMD record related DNSSEC validation", "validationStatus", Logging::Loggable(validationStatus),
BOOST_REQUIRE(fclose(fp) == 0);
RecZoneToCache::Config config{".", "file", {temp}, ComboAddress(), TSIGTriplet()};
- RecZoneToCache::State state;
config.d_refreshPeriod = 0;
config.d_retryOnError = 0;
config.d_zonemd = mode;
// Start with a new, empty cache
g_recCache = std::make_unique<MemRecursorCache>();
BOOST_CHECK_EQUAL(g_recCache->size(), 0U);
+ RecZoneToCache::State state;
RecZoneToCache::ZoneToCache(config, state);
unlink(temp);