]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add a new 'servfail-until-ready' configuration option for RPZ
authorAram Sargsyan <aram@isc.org>
Mon, 19 May 2025 17:11:57 +0000 (17:11 +0000)
committerArаm Sаrgsyаn <aram@isc.org>
Fri, 22 Aug 2025 16:31:17 +0000 (16:31 +0000)
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.

bin/named/server.c
doc/arm/reference.rst
doc/misc/options
lib/dns/include/dns/rpz.h
lib/dns/rpz.c
lib/isccfg/namedconf.c
lib/ns/query.c

index c13ad71fce9a206b14ed57d13284d83c9ca5cd0d..30a115cfa05e93720b1fec135b396981432d9e15 100644 (file)
@@ -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 {
index c0fb31ecad98c6b7246770dbbeb432769f8e036a..d79c084ab4785dbd8ecd99cdcda08cbc1a06c283 100644 (file)
@@ -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
index 5bb047d95a6c0e0b9e463a489a7daf49db725cb5..01b000e6cc349f5d68c7177cd1cc99ab23c2b13b 100644 (file)
@@ -262,7 +262,7 @@ options {
        resolver-query-timeout <integer>;
        resolver-use-dns64 <boolean>;
        response-padding { <address_match_element>; ... } block-size <integer>;
-       response-policy { zone <string> [ add-soa <boolean> ] [ log <boolean> ] [ max-policy-ttl <duration> ] [ min-update-interval <duration> ] [ policy ( cname | disabled | drop | given | no-op | nodata | nxdomain | passthru | tcp-only <quoted_string> ) ] [ recursive-only <boolean> ] [ nsip-enable <boolean> ] [ nsdname-enable <boolean> ] [ ede <string> ]; ... } [ add-soa <boolean> ] [ break-dnssec <boolean> ] [ max-policy-ttl <duration> ] [ min-update-interval <duration> ] [ min-ns-dots <integer> ] [ nsip-wait-recurse <boolean> ] [ nsdname-wait-recurse <boolean> ] [ qname-wait-recurse <boolean> ] [ recursive-only <boolean> ] [ nsip-enable <boolean> ] [ nsdname-enable <boolean> ] [ dnsrps-enable <boolean> ] [ dnsrps-options { <unspecified-text> } ];
+       response-policy { zone <string> [ add-soa <boolean> ] [ log <boolean> ] [ max-policy-ttl <duration> ] [ min-update-interval <duration> ] [ policy ( cname | disabled | drop | given | no-op | nodata | nxdomain | passthru | tcp-only <quoted_string> ) ] [ recursive-only <boolean> ] [ nsip-enable <boolean> ] [ nsdname-enable <boolean> ] [ ede <string> ]; ... } [ add-soa <boolean> ] [ break-dnssec <boolean> ] [ max-policy-ttl <duration> ] [ min-update-interval <duration> ] [ min-ns-dots <integer> ] [ nsip-wait-recurse <boolean> ] [ nsdname-wait-recurse <boolean> ] [ qname-wait-recurse <boolean> ] [ recursive-only <boolean> ] [ servfail-until-ready <boolean> ] [ nsip-enable <boolean> ] [ nsdname-enable <boolean> ] [ dnsrps-enable <boolean> ] [ dnsrps-options { <unspecified-text> } ];
        responselog <boolean>;
        reuseport <boolean>;
        root-key-sentinel <boolean>;
@@ -634,7 +634,7 @@ view <string> [ <class> ] {
        resolver-query-timeout <integer>;
        resolver-use-dns64 <boolean>;
        response-padding { <address_match_element>; ... } block-size <integer>;
-       response-policy { zone <string> [ add-soa <boolean> ] [ log <boolean> ] [ max-policy-ttl <duration> ] [ min-update-interval <duration> ] [ policy ( cname | disabled | drop | given | no-op | nodata | nxdomain | passthru | tcp-only <quoted_string> ) ] [ recursive-only <boolean> ] [ nsip-enable <boolean> ] [ nsdname-enable <boolean> ] [ ede <string> ]; ... } [ add-soa <boolean> ] [ break-dnssec <boolean> ] [ max-policy-ttl <duration> ] [ min-update-interval <duration> ] [ min-ns-dots <integer> ] [ nsip-wait-recurse <boolean> ] [ nsdname-wait-recurse <boolean> ] [ qname-wait-recurse <boolean> ] [ recursive-only <boolean> ] [ nsip-enable <boolean> ] [ nsdname-enable <boolean> ] [ dnsrps-enable <boolean> ] [ dnsrps-options { <unspecified-text> } ];
+       response-policy { zone <string> [ add-soa <boolean> ] [ log <boolean> ] [ max-policy-ttl <duration> ] [ min-update-interval <duration> ] [ policy ( cname | disabled | drop | given | no-op | nodata | nxdomain | passthru | tcp-only <quoted_string> ) ] [ recursive-only <boolean> ] [ nsip-enable <boolean> ] [ nsdname-enable <boolean> ] [ ede <string> ]; ... } [ add-soa <boolean> ] [ break-dnssec <boolean> ] [ max-policy-ttl <duration> ] [ min-update-interval <duration> ] [ min-ns-dots <integer> ] [ nsip-wait-recurse <boolean> ] [ nsdname-wait-recurse <boolean> ] [ qname-wait-recurse <boolean> ] [ recursive-only <boolean> ] [ servfail-until-ready <boolean> ] [ nsip-enable <boolean> ] [ nsdname-enable <boolean> ] [ dnsrps-enable <boolean> ] [ dnsrps-options { <unspecified-text> } ];
        root-key-sentinel <boolean>;
        rrset-order { [ class <string> ] [ type <string> ] [ name <quoted_string> ] <string> <string>; ... };
        send-cookie <boolean>;
index fa85ec568e335fa44b3b271775ffe11bbc3ab20a..9406f691400515ff3fdaf53e55d191590e334a6f 100644 (file)
@@ -18,6 +18,7 @@
 #include <inttypes.h>
 #include <stdbool.h>
 
+#include <isc/atomic.h>
 #include <isc/ht.h>
 #include <isc/refcount.h>
 #include <isc/rwlock.h>
@@ -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
index 414a2fdec4ae9c0ca0fe47557ba5910e00c72653..b59c560acaaaa5bcc356b322db11586034b0a966 100644 (file)
@@ -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,
index 6d204ebeeb719606748f77696dcb4a8c59a7796f..6c7469e46d334fe82c8c1ae76331cdf1ec120c34 100644 (file)
@@ -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 },
index 227a404778154539b2bbb79cd106a6d983e627ca..60e748174dcd086db869c805a433c3476ebf3e5d 100644 (file)
@@ -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))
        {