From: Aram Sargsyan Date: Mon, 19 May 2025 17:11:57 +0000 (+0000) Subject: Add a new 'servfail-until-ready' configuration option for RPZ X-Git-Tag: v9.21.12~29^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=41387b8d30cd1555d030ef69480a575c2148490a;p=thirdparty%2Fbind9.git Add a new 'servfail-until-ready' configuration option for RPZ By default, when named is started it may start answering to queries before the response policy zones are completely loaded and processed. This new feature gives an option to the users to tell named that incoming requests should result in SERVFAIL anwser until all the response policy zones are procesed and ready. --- diff --git a/bin/named/server.c b/bin/named/server.c index c13ad71fce9..30a115cfa05 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 c0fb31ecad9..d79c084ab47 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 5bb047d95a6..01b000e6cc3 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 fa85ec568e3..9406f691400 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 414a2fdec4a..b59c560acaa 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 6d204ebeeb7..6c7469e46d3 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 227a4047781..60e748174dc 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)) {