]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
set up logging functionality using log-report-channel
authorEvan Hunt <each@isc.org>
Sun, 20 Oct 2024 01:16:35 +0000 (18:16 -0700)
committerEvan Hunt <each@isc.org>
Wed, 23 Oct 2024 21:29:32 +0000 (21:29 +0000)
the logging of error-report queries is no longer activated by
the view's "send-report-channel" option; that now only configures
the agent-domain value that is to be sent in authoritative
responses. the warning that was logged when "send-agent-domain"
was set to a value that is not a locally configured zone has
been removed.

error-report logging is now activated by the presence of an
authoritative zone with the "log-report-channel" option set to
"yes".  this is not permitted in the root zone.

NOTE: a zone with "log-report-channel yes;" should contain a
"*._er" wildcard, but that requirement is not yet enforced.

bin/tests/system/auth/ns1/named.conf.in
bin/tests/system/auth/ns1/rad.db [new file with mode: 0644]
bin/tests/system/auth/tests.sh
bin/tests/system/checkconf/bad-log-rc.conf [moved from bin/tests/system/checkconf/warn-rad.conf with 82% similarity]
bin/tests/system/checkconf/tests.sh
doc/arm/logging-categories.inc.rst
doc/arm/reference.rst
lib/isccfg/check.c
lib/ns/query.c

index 0f40bbbf4a9a4d8d5f29f1e802441d9e867c1e41..92fd707dbd1550fff41e7fdce1156bfd01aee27c 100644 (file)
@@ -38,6 +38,12 @@ view main in {
                file "example.com.db";
                send-report-channel "rad.example.com";
        };
+
+       zone rad.example.net {
+               type primary;
+               file "rad.db";
+               log-report-channel yes;
+       };
 };
 
 view alt chaos {
diff --git a/bin/tests/system/auth/ns1/rad.db b/bin/tests/system/auth/ns1/rad.db
new file mode 100644 (file)
index 0000000..08d3197
--- /dev/null
@@ -0,0 +1,23 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0.  If a copy of the MPL was not distributed with this
+; file, you can obtain one at https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 300        ; 5 minutes
+@                       IN SOA  ns root (
+                               2018010100 ; serial
+                               1800       ; refresh (30 minutes)
+                               1800       ; retry (30 minutes)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                      A       10.53.0.1
+server                 A       10.53.0.100
+*._er                   TXT     "Report received"
index 7900ae0010fa9c7a18bf4414dc7ba7750fd80136..38fb85d76f8b07e7146df02fe902fbc54b768528 100644 (file)
@@ -212,5 +212,15 @@ grep "; Report-Channel: rad.example.net" dig.out.test$n >/dev/null && ret=1
 [ $ret -eq 0 ] || echo_i "failed"
 status=$((status + ret))
 
+n=$((n + 1))
+echo_i "check that error report queries to non-logging zones are not logged ($n)"
+ret=0
+nextpart ns1/named.run >/dev/null
+$DIG $DIGOPTS @10.53.0.1 _er.0.example.1._er.example.com TXT >dig.out.test$n
+nextpart ns1/named.run | grep "dns-reporting-agent '_er.0.example.1._er.example.com/IN'" >/dev/null && ret=1
+grep "; Report-Channel: rad.example.com" dig.out.test$n >/dev/null || ret=1
+[ $ret -eq 0 ] || echo_i "failed"
+status=$((status + ret))
+
 echo_i "exit status: $status"
 [ $status -eq 0 ] || exit 1
similarity index 82%
rename from bin/tests/system/checkconf/warn-rad.conf
rename to bin/tests/system/checkconf/bad-log-rc.conf
index a6bfc33973a0764af2ff1d7f770c6267a821086c..9834f70a6c64b83a1ea62d154e1ec4030bdd4283 100644 (file)
@@ -11,7 +11,8 @@
  * information regarding copyright ownership.
  */
 
-options {
-       /* nonexistent zone used as agent-domain */
-       send-report-channel example.com;
+zone "." {
+       type primary;
+       file "root.db";
+       log-report-channel yes;
 };
index 3f83a131007d067b31d8070f6e4115f376d7d92a..461e4ef45146055a04599c54764e7d4f8d61e8cd 100644 (file)
@@ -772,16 +772,5 @@ if [ $ret != 0 ]; then
 fi
 status=$((status + ret))
 
-n=$((n + 1))
-echo_i "check that 'send-report-channel' warns if no matching zone exists ($n)"
-ret=0
-$CHECKCONF -z warn-rad.conf >checkconf.out$n 2>&1 || ret=1
-grep -F "send-report-channel 'example.com' is not a primary or secondary zone" checkconf.out$n >/dev/null || ret=1
-if [ $ret != 0 ]; then
-  echo_i "failed"
-  ret=1
-fi
-status=$((status + ret))
-
 echo_i "exit status: $status"
 [ $status -eq 0 ] || exit 1
index 91f82b36d2f48d330fbe81fe95ca8477a67f3f7a..88d4f8b180ef9c8525ad1019faf89aa615faaa71 100644 (file)
@@ -44,7 +44,7 @@
     A catch-all for many things that still are not classified into categories.
 
 ``dns-reporting-agent``
-    Logs reports from clients that the there is an error in our responses.
+    Reports from clients indicating that there is an error in our responses.
 
 ``lame-servers``
     Misconfigurations in remote servers, discovered by BIND 9 when trying to query those servers during resolution.
index ef2d15277a70d027a22cf285e6407d0117187305..4813ccd546179ff12e30e494e43256b76e35e84d 100644 (file)
@@ -1680,44 +1680,6 @@ default is used.
    If all supported digest types are disabled, the zones covered by
    :any:`disable-ds-digests` are treated as insecure.
 
-.. namedconf:statement:: send-report-channel
-   :tags: query
-   :short: Sets the Agent Domain value for the EDNS Report-Channel option
-
-   The EDNS Report-Channel option can be added to responses by an
-   authoritative server to inform clients of a domain name to which
-   operational and protocol errors may be reported. This can help
-   operators find out about configuration errors that are causing
-   problems with resolution or validation elsewhere (for example,
-   expired DNSSEC signatures).
-
-   When :any:`send-report-channel` is set in :namedconf:ref:`options`,
-   :namedconf:ref:`view`, or :namedconf:ref:`zone`, :iscman:`named` adds a
-   Report-Channel option to authoritative responses, using the specified
-   domain name as the Agent-Domain.
-
-   When it is set in :namedconf:ref:`options` or :namedconf:ref:`view` (but
-   *not* :namedconf:ref:`zone`), :iscman:`named` also logs any TXT queries
-   received for names matching the prescribed error-reporting format
-   (_er.<type>.<name>.<extended-rcode>._er.<agent-domain>) to the
-   ``dns-reporting-agent`` logging category at level ``info``.
-
-   There should be a zone delegated to respond to these queries with TXT
-   records, to avoid unnecessarily query repetition. For example:
-
-   ::
-
-      e.g.
-          $ORIGIN <agent-domain>
-          @ 600 SOA <namserver1> <contact-email> 0 0 0 0 600
-          @ 600 NS <nameserver1>
-          @ 600 NS <nameserver2>
-          *._er 600 TXT ""
-
-   If :any:`send-report-channel` is not set, or is set to ``.``, then
-   the EDNS option is not added to responses, and error-report queries are
-   not logged.
-
 .. namedconf:statement:: dnssec-must-be-secure
    :tags: deprecated
    :short: Defines hierarchies that must or may not be secure (signed and validated).
@@ -1957,6 +1919,32 @@ default is used.
    :namedconf:ref:`zone` blocks, setting :any:`max-zone-ttl` to zero
    is equivalent to "unlimited".
 
+.. namedconf:statement:: send-report-channel
+   :tags: query
+   :short: Sets the Agent Domain value for the EDNS Report-Channel option
+
+   The EDNS Report-Channel option can be added to responses by an
+   authoritative server to inform clients of a domain name to which
+   operational and protocol errors may be reported. This can help
+   operators find out about configuration errors that are causing
+   problems with resolution or validation elsewhere (for example,
+   expired DNSSEC signatures).
+
+   When :any:`send-report-channel` is set in :namedconf:ref:`options`,
+   :namedconf:ref:`view`, or :namedconf:ref:`zone`, :iscman:`named` adds a
+   Report-Channel option to authoritative responses, using the specified
+   domain name as the Agent-Domain.
+
+   If :any:`send-report-channel` is not set, or is set to ``.``, then
+   the EDNS option is not added to responses.
+
+   A client that wishes to report an error can send a query for type TXT
+   to a name matching the prescribed RFC 9567 error-reporting format:
+   ``_er.<type>.<name>.<extended-rcode>._er.<agent-domain>``.
+
+   There should be an authoritative zone configured to respond to such
+   queries, with the :any:`log-report-channel` option set to ``yes``.
+
 .. namedconf:statement:: stale-answer-ttl
    :tags: query
    :short: Specifies the time to live (TTL) to be returned on stale answers, in seconds.
@@ -7317,6 +7305,30 @@ Zone Options
    The use of this option in :any:`zone` blocks is deprecated and
    will be rendered non-operational in a future release.
 
+.. namedconf:statement:: log-report-channel
+   :tags: logging
+   :short: Specifies whether to log error-report queries.
+
+   When this option is set to ``yes``, TXT queries for names
+   matching the prescribed RFC 9567 error-reporting format
+   (``_er.<type>.<name>.<extended-rcode>._er.<zone-name>``)
+   are logged to the ``dns-reporting-agent`` logging category at
+   level ``info``.
+
+   The zone should have a wildcard record in place to respond to such
+   queries. For example:
+
+   ::
+
+          $ORIGIN <agent-domain>
+          @ 600 SOA <namserver1> <contact-email> 0 0 0 0 600
+          @ 600 NS <nameserver1>
+          @ 600 NS <nameserver2>
+          *._er 600 TXT "Report received"
+
+:any:`send-report-channel`
+   See the description of :any:`send-report-channel` in :namedconf:ref:`options`.
+
 .. _dynamic_update_policies:
 
 Dynamic Update Policies
index 524c2b122d89cbdcf4d5c044814ce3e044b37a44..a24297290ba869ed370e2ecb6ce6fc6f844db28a 100644 (file)
@@ -3910,6 +3910,22 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
                }
        }
 
+       /*
+        * "log-report-channel" cannot be set for the root zone.
+        */
+       if (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_SECONDARY) {
+               obj = NULL;
+               tresult = cfg_map_get(zoptions, "log-report-channel", &obj);
+               if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(obj) &&
+                   dns_name_equal(zname, dns_rootname))
+               {
+                       cfg_obj_log(zconfig, ISC_LOG_ERROR,
+                                   "'log-report-channel' cannot be set in "
+                                   "the root zone");
+                       result = ISC_R_FAILURE;
+               }
+       }
+
        /*
         * Check various options.
         */
@@ -5070,73 +5086,28 @@ cleanup:
 
 typedef enum { special_zonetype_rpz, special_zonetype_catz } special_zonetype_t;
 
-static bool
-iszone(const cfg_obj_t *nameobj, const char *what, const char *forview,
-       const char *viewname, int level, isc_symtab_t *symtab) {
-       char namebuf[DNS_NAME_FORMATSIZE];
-       const cfg_obj_t *obj = NULL;
-       const char *zonename = cfg_obj_asstring(nameobj);
-       const char *zonetype = "";
-       dns_fixedname_t fixed;
-       dns_name_t *name = dns_fixedname_initname(&fixed);
-       isc_result_t result;
-       isc_symvalue_t value;
-
-       if (viewname == NULL) {
-               viewname = "";
-               forview = "";
-       }
-
-       result = dns_name_fromstring(name, zonename, dns_rootname, 0, NULL);
-       if (result != ISC_R_SUCCESS) {
-               cfg_obj_log(nameobj, ISC_LOG_ERROR, "bad domain name '%s'",
-                           zonename);
-               return (false);
-       }
-
-       dns_name_format(name, namebuf, sizeof(namebuf));
-       result = isc_symtab_lookup(symtab, namebuf, 3, &value);
-       if (result == ISC_R_SUCCESS) {
-               const cfg_obj_t *zoneobj = value.as_cpointer;
-               if (zoneobj != NULL && cfg_obj_istuple(zoneobj)) {
-                       zoneobj = cfg_tuple_get(zoneobj, "options");
-               }
-               if (zoneobj != NULL && cfg_obj_ismap(zoneobj)) {
-                       (void)cfg_map_get(zoneobj, "type", &obj);
-               }
-               if (obj != NULL) {
-                       zonetype = cfg_obj_asstring(obj);
-               }
-       }
-
-       if (strcasecmp(zonetype, "primary") != 0 &&
-           strcasecmp(zonetype, "master") != 0 &&
-           strcasecmp(zonetype, "secondary") != 0 &&
-           strcasecmp(zonetype, "slave") != 0)
-       {
-               cfg_obj_log(nameobj, level,
-                           "%s '%s'%s%s is not a primary or secondary zone",
-                           what, zonename, forview, viewname);
-               return (false);
-       }
-       return (true);
-}
-
 static isc_result_t
 check_rpz_catz(const char *rpz_catz, const cfg_obj_t *rpz_obj,
               const char *viewname, isc_symtab_t *symtab,
               special_zonetype_t specialzonetype) {
        const cfg_listelt_t *element;
-       const cfg_obj_t *obj, *nameobj;
+       const cfg_obj_t *obj, *nameobj, *zoneobj;
+       const char *zonename, *zonetype;
        const char *forview = " for view ";
-       isc_result_t result = ISC_R_SUCCESS;
+       isc_symvalue_t value;
+       isc_result_t result, tresult;
+       dns_fixedname_t fixed;
+       dns_name_t *name;
+       char namebuf[DNS_NAME_FORMATSIZE];
        unsigned int num_zones = 0;
 
        if (viewname == NULL) {
                viewname = "";
                forview = "";
        }
+       result = ISC_R_SUCCESS;
 
+       name = dns_fixedname_initname(&fixed);
        obj = cfg_tuple_get(rpz_obj, "zone list");
 
        for (element = cfg_list_first(obj); element != NULL;
@@ -5144,21 +5115,56 @@ check_rpz_catz(const char *rpz_catz, const cfg_obj_t *rpz_obj,
        {
                obj = cfg_listelt_value(element);
                nameobj = cfg_tuple_get(obj, "zone name");
+               zonename = cfg_obj_asstring(nameobj);
+               zonetype = "";
 
                if (specialzonetype == special_zonetype_rpz) {
                        if (++num_zones > 64) {
                                cfg_obj_log(nameobj, ISC_LOG_ERROR,
                                            "more than 64 response policy "
-                                           "zones%s'%s'",
-                                           forview, viewname);
+                                           "zones in view '%s'",
+                                           viewname);
                                return (ISC_R_FAILURE);
                        }
                }
 
-               if (!iszone(nameobj, rpz_catz, forview, viewname, ISC_LOG_ERROR,
-                           symtab))
+               tresult = dns_name_fromstring(name, zonename, dns_rootname, 0,
+                                             NULL);
+               if (tresult != ISC_R_SUCCESS) {
+                       cfg_obj_log(nameobj, ISC_LOG_ERROR,
+                                   "bad domain name '%s'", zonename);
+                       if (result == ISC_R_SUCCESS) {
+                               result = tresult;
+                       }
+                       continue;
+               }
+               dns_name_format(name, namebuf, sizeof(namebuf));
+               tresult = isc_symtab_lookup(symtab, namebuf, 3, &value);
+               if (tresult == ISC_R_SUCCESS) {
+                       obj = NULL;
+                       zoneobj = value.as_cpointer;
+                       if (zoneobj != NULL && cfg_obj_istuple(zoneobj)) {
+                               zoneobj = cfg_tuple_get(zoneobj, "options");
+                       }
+                       if (zoneobj != NULL && cfg_obj_ismap(zoneobj)) {
+                               (void)cfg_map_get(zoneobj, "type", &obj);
+                       }
+                       if (obj != NULL) {
+                               zonetype = cfg_obj_asstring(obj);
+                       }
+               }
+               if (strcasecmp(zonetype, "primary") != 0 &&
+                   strcasecmp(zonetype, "master") != 0 &&
+                   strcasecmp(zonetype, "secondary") != 0 &&
+                   strcasecmp(zonetype, "slave") != 0)
                {
-                       result = ISC_R_FAILURE;
+                       cfg_obj_log(nameobj, ISC_LOG_ERROR,
+                                   "%s '%s'%s%s is not a primary or secondary "
+                                   "zone",
+                                   rpz_catz, zonename, forview, viewname);
+                       if (result == ISC_R_SUCCESS) {
+                               result = ISC_R_FAILURE;
+                       }
                }
        }
        return (result);
@@ -5438,8 +5444,7 @@ check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions,
 
        /*
         * Check that the response-policy and catalog-zones options
-        * refer to zones that exist.  Also warn if send-report-channel
-        * is not a zone.
+        * refer to zones that exist.
         */
        if (opts != NULL) {
                obj = NULL;
@@ -5461,14 +5466,6 @@ check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions,
                {
                        result = ISC_R_FAILURE;
                }
-
-               obj = NULL;
-               if ((cfg_map_get(opts, "send-report-channel", &obj) ==
-                    ISC_R_SUCCESS))
-               {
-                       (void)iszone(obj, "send-report-channel", " for view ",
-                                    viewname, ISC_LOG_WARNING, symtab);
-               }
        }
 
        /*
index ed2662b0a71919b5fbdb9fdea0edf6a0f54f5c16..6dc2048fc0d099f000d7fec05e252ea4e3a85d60 100644 (file)
@@ -5332,6 +5332,75 @@ root_key_sentinel_detect(query_ctx_t *qctx) {
        }
 }
 
+static void
+qctx_reportquery(query_ctx_t *qctx) {
+       ns_client_t *client = qctx->client;
+
+       client->attributes |= NS_CLIENTATTR_WANTRC;
+
+       /* If this isn't a report-logging zone, there's no more to do */
+       dns_zoneopt_t opts = dns_zone_getoptions(qctx->zone);
+       if ((opts & DNS_ZONEOPT_LOGREPORTS) == 0) {
+               return;
+       }
+
+       /*
+        * Suppress EDNS Report-Channel in responses from report-
+        * logging zones; this prevents infinite loops.
+        */
+       client->attributes &= ~NS_CLIENTATTR_WANTRC;
+
+       /* If this isn't an error-report query, there's nothing more to do */
+       if (client->query.qtype != dns_rdatatype_txt ||
+           !dns_name_israd(client->query.qname,
+                           dns_zone_getorigin(qctx->zone)))
+       {
+               return;
+       }
+
+       /*
+        * Check for TCP or a good server cookie.  If neither, send
+        * back BADCOOKIE or TC=1.
+        */
+       if (!TCP(client) && !HAVECOOKIE(client)) {
+               if (WANTCOOKIE(client)) {
+                       client->attributes |= NS_CLIENTATTR_BADCOOKIE;
+               } else {
+                       client->attributes |= NS_CLIENTATTR_NEEDTCP;
+               }
+       }
+
+       if (isc_log_wouldlog(ISC_LOG_INFO)) {
+               char classbuf[DNS_RDATACLASS_FORMATSIZE];
+               char namebuf[DNS_NAME_FORMATSIZE];
+
+               dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
+               dns_rdataclass_format(client->view->rdclass, classbuf,
+                                     sizeof(classbuf));
+
+               isc_log_write(NS_LOGCATEGORY_DRA, NS_LOGMODULE_QUERY,
+                             ISC_LOG_INFO, "dns-reporting-agent '%s/%s'",
+                             namebuf, classbuf);
+       }
+}
+
+static void
+qctx_setrad(query_ctx_t *qctx) {
+       ns_client_t *client = qctx->client;
+
+       /* Set the client to send a Report-Channel option when replying */
+       if ((client->attributes & NS_CLIENTATTR_WANTRC) != 0) {
+               dns_fixedname_t fixed;
+               dns_name_t *rad = dns_fixedname_initname(&fixed);
+
+               if (!dns_name_dynamic(&client->rad) &&
+                   dns_zone_getrad(qctx->zone, rad) == ISC_R_SUCCESS)
+               {
+                       dns_name_dup(rad, client->manager->mctx, &client->rad);
+               }
+       }
+}
+
 /*%
  * Starting point for a client query or a chaining query.
  *
@@ -5518,8 +5587,6 @@ ns__query_start(query_ctx_t *qctx) {
        if (qctx->is_zone) {
                qctx->authoritative = true;
                if (qctx->zone != NULL) {
-                       dns_fixedname_t fixed;
-                       dns_name_t *rad;
                        switch (dns_zone_gettype(qctx->zone)) {
                        case dns_zone_mirror:
                                qctx->authoritative = false;
@@ -5529,16 +5596,8 @@ ns__query_start(query_ctx_t *qctx) {
                                break;
                        case dns_zone_primary:
                        case dns_zone_secondary:
-                               rad = dns_fixedname_initname(&fixed);
-                               if (!dns_name_dynamic(&qctx->client->rad) &&
-                                   dns_zone_getrad(qctx->zone, rad) ==
-                                           ISC_R_SUCCESS)
-                               {
-                                       dns_name_dup(
-                                               rad,
-                                               qctx->client->manager->mctx,
-                                               &qctx->client->rad);
-                               }
+                               qctx_reportquery(qctx);
+                               qctx_setrad(qctx);
                                break;
                        default:
                                break;
@@ -11493,54 +11552,6 @@ cleanup:
        return (result);
 }
 
-static void
-log_reportchannel(ns_client_t *client) {
-       char classbuf[DNS_RDATACLASS_FORMATSIZE];
-       char namebuf[DNS_NAME_FORMATSIZE];
-
-       client->attributes |= NS_CLIENTATTR_WANTRC;
-
-       if (client->view->rad != NULL &&
-           dns_name_issubdomain(client->query.qname, client->view->rad))
-       {
-               /*
-                * Don't add Report-Channel to responses at or below the
-                * reporting agent domain to prevent infinite loops.
-                */
-               client->attributes &= ~NS_CLIENTATTR_WANTRC;
-       }
-
-       if (client->query.qtype != dns_rdatatype_txt ||
-           client->view->rad == NULL ||
-           !dns_name_israd(client->query.qname, client->view->rad))
-       {
-               return;
-       }
-
-       /*
-        * Check for TCP or a good server cookie.  If neither send
-        * back BADCOOKIE or TC=1.
-        */
-       if (!TCP(client) && !HAVECOOKIE(client)) {
-               if (WANTCOOKIE(client)) {
-                       client->attributes |= NS_CLIENTATTR_BADCOOKIE;
-               } else {
-                       client->attributes |= NS_CLIENTATTR_NEEDTCP;
-               }
-       }
-
-       if (!isc_log_wouldlog(ISC_LOG_INFO)) {
-               return;
-       }
-
-       dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
-       dns_rdataclass_format(client->view->rdclass, classbuf,
-                             sizeof(classbuf));
-
-       isc_log_write(NS_LOGCATEGORY_DRA, NS_LOGMODULE_QUERY, ISC_LOG_INFO,
-                     "dns-reporting-agent '%s/%s'", namebuf, classbuf);
-}
-
 static void
 log_tat(ns_client_t *client) {
        char namebuf[DNS_NAME_FORMATSIZE];
@@ -11796,7 +11807,6 @@ ns_query_start(ns_client_t *client, isc_nmhandle_t *handle) {
        dns_rdatatypestats_increment(client->manager->sctx->rcvquerystats,
                                     qtype);
 
-       log_reportchannel(client);
        log_tat(client);
 
        if (dns_rdatatype_ismeta(qtype)) {