diff --git a/bin/named/server.c b/bin/named/server.c index b606d87412..9628883a90 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -2618,6 +2618,13 @@ configure_rpz(dns_view_t *view, dns_view_t *pview, const cfg_obj_t **maps, 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 f3e34e0309..b1d7443351 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -5331,6 +5331,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 6ad86116fd..0ff82f283d 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -266,7 +266,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 ; @@ -552,7 +552,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 110259fed0..e5c25cec50 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 @@ -154,6 +155,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 */ @@ -205,6 +208,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; }; @@ -222,6 +226,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 ae65e98798..aa7b857bcc 100644 --- a/lib/dns/rpz.c +++ b/lib/dns/rpz.c @@ -1670,7 +1670,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 @@ -1678,8 +1690,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; @@ -1744,6 +1763,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_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 2696145fbd..26c1ce7022 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -1756,7 +1756,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 ] * [ dnsrps-enable yes|no ] * [ dnsrps-options { DNSRPS configuration string } ]; @@ -1979,6 +1979,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 }, #ifdef USE_DNSRPS diff --git a/lib/ns/query.c b/lib/ns/query.c index 7b94aad066..e9e9ae2dc3 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -4262,7 +4262,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; @@ -4270,6 +4270,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; #ifdef USE_DNSRPS @@ -4301,6 +4305,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); @@ -4348,6 +4355,16 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, #endif /* ifdef USE_DNSRPS */ } + /* 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. */ @@ -4397,8 +4414,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)) {