diff --git a/bin/named/server.c b/bin/named/server.c index c13ad71fce..30a115cfa0 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -2245,6 +2245,13 @@ configure_rpz(dns_view_t *view, dns_view_t *pview, const cfg_obj_t *rpz_obj, zones->p.nsip_wait_recurse = false; } + sub_obj = cfg_tuple_get(rpz_obj, "servfail-until-ready"); + if (!cfg_obj_isvoid(sub_obj) && cfg_obj_asboolean(sub_obj)) { + zones->p.servfail_until_ready = true; + } else { + zones->p.servfail_until_ready = false; + } + if (pview != NULL) { old = pview->rpzs; } else { diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index c0fb31ecad..d79c084ab4 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -5116,6 +5116,14 @@ the same zone files both inside and outside an :rfc:`1918` cloud and using RPZ to delete answers that would otherwise contain :rfc:`1918` values on the externally visible name server or view. +Also by default, when :iscman:`named` is started it may start answering to +queries before the response policy zones are completely loaded and processed. +This can be changed with the ``servfail-until-ready yes`` option, in which case +incoming requests will result in SERVFAIL answer, until all the response policy +zones are ready. Note that if one or more response policy zones fail to load, +:iscman:`named` starts responding to queries according to those zones that did +load. + Also by default, RPZ actions are applied only to DNS requests that either do not request DNSSEC metadata (DO=0) or when no DNSSEC records are available for the requested name in the original zone (not the response diff --git a/doc/misc/options b/doc/misc/options index 5bb047d95a..01b000e6cc 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -262,7 +262,7 @@ options { resolver-query-timeout ; resolver-use-dns64 ; response-padding { ; ... } block-size ; - response-policy { zone [ add-soa ] [ log ] [ max-policy-ttl ] [ min-update-interval ] [ policy ( cname | disabled | drop | given | no-op | nodata | nxdomain | passthru | tcp-only ) ] [ recursive-only ] [ nsip-enable ] [ nsdname-enable ] [ ede ]; ... } [ add-soa ] [ break-dnssec ] [ max-policy-ttl ] [ min-update-interval ] [ min-ns-dots ] [ nsip-wait-recurse ] [ nsdname-wait-recurse ] [ qname-wait-recurse ] [ recursive-only ] [ nsip-enable ] [ nsdname-enable ] [ dnsrps-enable ] [ dnsrps-options { } ]; + response-policy { zone [ add-soa ] [ log ] [ max-policy-ttl ] [ min-update-interval ] [ policy ( cname | disabled | drop | given | no-op | nodata | nxdomain | passthru | tcp-only ) ] [ recursive-only ] [ nsip-enable ] [ nsdname-enable ] [ ede ]; ... } [ add-soa ] [ break-dnssec ] [ max-policy-ttl ] [ min-update-interval ] [ min-ns-dots ] [ nsip-wait-recurse ] [ nsdname-wait-recurse ] [ qname-wait-recurse ] [ recursive-only ] [ servfail-until-ready ] [ nsip-enable ] [ nsdname-enable ] [ dnsrps-enable ] [ dnsrps-options { } ]; responselog ; reuseport ; root-key-sentinel ; @@ -634,7 +634,7 @@ view [ ] { resolver-query-timeout ; resolver-use-dns64 ; response-padding { ; ... } block-size ; - response-policy { zone [ add-soa ] [ log ] [ max-policy-ttl ] [ min-update-interval ] [ policy ( cname | disabled | drop | given | no-op | nodata | nxdomain | passthru | tcp-only ) ] [ recursive-only ] [ nsip-enable ] [ nsdname-enable ] [ ede ]; ... } [ add-soa ] [ break-dnssec ] [ max-policy-ttl ] [ min-update-interval ] [ min-ns-dots ] [ nsip-wait-recurse ] [ nsdname-wait-recurse ] [ qname-wait-recurse ] [ recursive-only ] [ nsip-enable ] [ nsdname-enable ] [ dnsrps-enable ] [ dnsrps-options { } ]; + response-policy { zone [ add-soa ] [ log ] [ max-policy-ttl ] [ min-update-interval ] [ policy ( cname | disabled | drop | given | no-op | nodata | nxdomain | passthru | tcp-only ) ] [ recursive-only ] [ nsip-enable ] [ nsdname-enable ] [ ede ]; ... } [ add-soa ] [ break-dnssec ] [ max-policy-ttl ] [ min-update-interval ] [ min-ns-dots ] [ nsip-wait-recurse ] [ nsdname-wait-recurse ] [ qname-wait-recurse ] [ recursive-only ] [ servfail-until-ready ] [ nsip-enable ] [ nsdname-enable ] [ dnsrps-enable ] [ dnsrps-options { } ]; root-key-sentinel ; rrset-order { [ class ] [ type ] [ name ] ; ... }; send-cookie ; diff --git a/lib/dns/include/dns/rpz.h b/lib/dns/include/dns/rpz.h index fa85ec568e..9406f69140 100644 --- a/lib/dns/include/dns/rpz.h +++ b/lib/dns/include/dns/rpz.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -151,6 +152,8 @@ struct dns_rpz_zone { dns_rpz_zones_t *rpzs; /* owner */ isc_time_t lastupdated; /* last time the zone was processed * */ + bool processed; /* the zone is processed. */ + bool dbregistered; /* db callback notify is registered. */ bool updatepending; /* there is an update pending */ bool updaterunning; /* there is an update running */ isc_result_t updateresult; /* result from the offloaded work */ @@ -201,6 +204,7 @@ struct dns_rpz_popt { bool qname_wait_recurse; bool nsip_wait_recurse; bool nsdname_wait_recurse; + bool servfail_until_ready; unsigned int min_ns_labels; dns_rpz_num_t num_zones; }; @@ -217,6 +221,9 @@ struct dns_rpz_zones { dns_rpz_zone_t *zones[DNS_RPZ_MAX_ZONES]; dns_rpz_triggers_t triggers[DNS_RPZ_MAX_ZONES]; + _Atomic(dns_rpz_num_t) zones_registered; + _Atomic(dns_rpz_num_t) zones_processed; + /* * RPZ policy version number. * It is initially 0 and it increases whenever the server is diff --git a/lib/dns/rpz.c b/lib/dns/rpz.c index 414a2fdec4..b59c560aca 100644 --- a/lib/dns/rpz.c +++ b/lib/dns/rpz.c @@ -1591,7 +1591,19 @@ dns_rpz_dbupdate_unregister(dns_db_t *db, dns_rpz_zone_t *rpz) { REQUIRE(DNS_DB_VALID(db)); REQUIRE(DNS_RPZ_ZONE_VALID(rpz)); + LOCK(&rpz->rpzs->maint_lock); dns_db_updatenotify_unregister(db, dns_rpz_dbupdate_callback, rpz); + if (rpz->processed) { + rpz->processed = false; + INSIST(atomic_fetch_sub_acq_rel(&rpz->rpzs->zones_processed, + 1) > 0); + } + if (rpz->dbregistered) { + rpz->dbregistered = false; + INSIST(atomic_fetch_sub_acq_rel(&rpz->rpzs->zones_registered, + 1) > 0); + } + UNLOCK(&rpz->rpzs->maint_lock); } void @@ -1599,8 +1611,15 @@ dns_rpz_dbupdate_register(dns_db_t *db, dns_rpz_zone_t *rpz) { REQUIRE(DNS_DB_VALID(db)); REQUIRE(DNS_RPZ_ZONE_VALID(rpz)); + LOCK(&rpz->rpzs->maint_lock); + if (!rpz->dbregistered) { + rpz->dbregistered = true; + atomic_fetch_add_acq_rel(&rpz->rpzs->zones_registered, 1); + } dns_db_updatenotify_register(db, dns_rpz_dbupdate_callback, rpz); + UNLOCK(&rpz->rpzs->maint_lock); } + static void dns__rpz_timer_start(dns_rpz_zone_t *rpz) { uint64_t tdiff; @@ -1665,6 +1684,11 @@ update_rpz_done_cb(void *data) { dns_db_closeversion(rpz->updb, &rpz->updbversion, false); dns_db_detach(&rpz->updb); + if (rpz->dbregistered && !rpz->processed) { + rpz->processed = true; + atomic_fetch_add_acq_rel(&rpz->rpzs->zones_processed, 1); + } + UNLOCK(&rpz->rpzs->maint_lock); isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_RPZ, ISC_LOG_INFO, diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 6d204ebeeb..6c7469e46d 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -1634,7 +1634,7 @@ static cfg_type_t cfg_type_dnstapoutput = { "dnstapoutput", parse_dtout, * } [ recursive-only yes|no ] [ max-policy-ttl number ] * [ min-update-interval number ] * [ break-dnssec yes|no ] [ min-ns-dots number ] - * [ qname-wait-recurse yes|no ] + * [ qname-wait-recurse yes|no ] [ servfail-until-ready yes|no ] * [ nsip-enable yes|no ] [ nsdname-enable yes|no ] */ @@ -1855,6 +1855,7 @@ static cfg_tuplefielddef_t rpz_fields[] = { { "nsdname-wait-recurse", &cfg_type_boolean, 0 }, { "qname-wait-recurse", &cfg_type_boolean, 0 }, { "recursive-only", &cfg_type_boolean, 0 }, + { "servfail-until-ready", &cfg_type_boolean, 0 }, { "nsip-enable", &cfg_type_boolean, 0 }, { "nsdname-enable", &cfg_type_boolean, 0 }, { "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, diff --git a/lib/ns/query.c b/lib/ns/query.c index 227a404778..60e748174d 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -3920,7 +3920,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, bool resuming, dns_rdataset_t *ordataset, dns_rdataset_t *osigset) { dns_rpz_zones_t *rpzs; dns_rpz_st_t *st; - dns_rdataset_t *rdataset; + dns_rdataset_t *rdataset = NULL; dns_fixedname_t nsnamef; dns_name_t *nsname; qresult_type_t qresult_type; @@ -3928,6 +3928,10 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, isc_result_t result = ISC_R_SUCCESS; dns_rpz_have_t have; dns_rpz_popt_t popt; + bool first_time; + dns_rpz_num_t zones_registered; + dns_rpz_num_t zones_processed; + int rpz_ver; unsigned int options; @@ -3956,6 +3960,9 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, } have = rpzs->have; popt = rpzs->p; + first_time = rpzs->first_time; + zones_registered = atomic_load_acquire(&rpzs->zones_registered); + zones_processed = atomic_load_acquire(&rpzs->zones_processed); rpz_ver = rpzs->rpz_ver; RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_read); @@ -3980,6 +3987,16 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, client->query.rpz_st = st; } + /* Check if the initial loading of RPZ is complete. */ + if (first_time && popt.servfail_until_ready && + zones_processed < zones_registered) + { + rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, NULL, + DNS_RPZ_TYPE_QNAME, "RPZ not ready yet", result); + st->m.policy = DNS_RPZ_POLICY_ERROR; + goto cleanup; + } + /* * There is nothing to rewrite if the main query failed. */ @@ -4029,8 +4046,6 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, return ISC_R_SUCCESS; } - rdataset = NULL; - if ((st->state & (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME)) != (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME)) {