]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
BIND: Add 'allow-proxy' and 'allow-proxy-on' options
authorArtem Boldariev <artem@boldariev.com>
Tue, 31 Oct 2023 10:39:04 +0000 (12:39 +0200)
committerArtem Boldariev <artem@boldariev.com>
Wed, 6 Dec 2023 13:15:25 +0000 (15:15 +0200)
The main intention of PROXY protocol is to pass endpoints information
to a back-end server (in our case - BIND). That means that it is a
valid way to spoof endpoints information, as the addresses and ports
extracted from PROXYv2 headers, from the point of view of BIND, are
used instead of the real connection addresses.

Of course, an ability to easily spoof endpoints information can be
considered a security issue when used uncontrollably. To resolve that,
we introduce 'allow-proxy' and 'allow-proxy-on' ACL options. These are
the only ACL options in BIND that work with real PROXY connections
addresses, allowing a DNS server operator to specify from what clients
and on which interfaces he or she is willing to accept PROXY
headers. By default, for security reasons we do not allow to accept
them.

bin/named/config.c
bin/named/server.c
doc/arm/reference.rst
doc/misc/options
lib/dns/include/dns/view.h
lib/dns/view.c
lib/isccfg/check.c
lib/isccfg/namedconf.c
lib/ns/client.c

index 9daa0c079be29cd9a747c9ff4dd2b7a7db525b39..57932107a6716b48500375ed7b9bd7b4dcfaad1c 100644 (file)
@@ -132,6 +132,8 @@ options {\n\
        /* view */\n\
        allow-new-zones no;\n\
        allow-notify {none;};\n\
+       allow-proxy {none;};\n\
+       allow-proxy-on {any;};\n\
        allow-query-cache { localnets; localhost; };\n\
        allow-query-cache-on { any; };\n\
        allow-recursion { localnets; localhost; };\n\
index 5af25d1c091fd1ba102337724362dbb9dce01cae..a8d39608b680dffe4268d078bfbbf63c0a0acf3c 100644 (file)
@@ -5205,6 +5205,13 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
        CHECK(configure_view_acl(vconfig, config, NULL, "allow-query-cache-on",
                                 NULL, actx, named_g_mctx, &view->cacheonacl));
 
+       CHECK(configure_view_acl(vconfig, config, named_g_config, "allow-proxy",
+                                NULL, actx, named_g_mctx, &view->proxyacl));
+
+       CHECK(configure_view_acl(vconfig, config, named_g_config,
+                                "allow-proxy-on", NULL, actx, named_g_mctx,
+                                &view->proxyonacl));
+
        if (strcmp(view->name, "_bind") != 0 &&
            view->rdclass != dns_rdataclass_chaos)
        {
index 16c46183c654538bf9ef9238748322155ca9c373..3434bc70fdbab4487f1cc4fc8760f268c3bdf564 100644 (file)
@@ -2833,6 +2833,44 @@ for details on how to specify IP address lists.
    the configured :any:`primaries` for the zone. :any:`allow-notify` can be used
    to expand the list of permitted hosts, not to reduce it.
 
+.. namedconf:statement:: allow-proxy
+   :tags: server
+   :short: Defines an :any:`address_match_list` for the client addresses allowed to send PROXYv2 headers.
+
+   The default :any:`address_match_list` is `none`, which means that
+   no client is allowed to do that by default for security reasons, as
+   the PROXYv2 protocol provides an easy way to spoof both source and
+   destination addresses.
+
+   This :any:`address_match_list` is primarily meant to have addresses
+   and subnets of the proxies that are allowed to send PROXYv2 headers
+   to BIND. In most cases, we do not recommend setting this
+   :any:`address_match_list` to be very allowing, in particular, to
+   set it to `any`, especially in the cases when PROXYv2 headers can be
+   accepted on publically available networking interfaces.
+
+   The specified option is the only option that matches against real
+   peer addresses when PROXYv2 headers are used. Most of the options
+   that work with peer addresses, use the ones extracted from PROXYv2
+   headers.
+
+   Also, see: :namedconf:ref:`allow-proxy-on`
+
+.. namedconf:statement:: allow-proxy-on
+   :tags: server
+   :short: Defines an :any:`address_match_list` for the interface addresses allowed to accept PROXYv2 headers. The option is mostly intended for multi-homed configurations.
+
+   The default :any:`address_match_list` is `any`, which means that
+   accepting PROXYv2 is allowed on any interface.
+
+   The option is useful in cases when you need to have precise control
+   over which interfaces PROXYv2 is allowed, as it is the only one
+   that matches against real interface addresses when PROXYv2 headers
+   are used. Most of the options that work with interface addresses
+   will use the ones extracted from PROXYv2 headers.
+
+   You may want to set :namedconf:ref:`allow-proxy` first.
+
 .. namedconf:statement:: allow-query
    :tags: query
    :short: Specifies which hosts (an IP address list) are allowed to send queries to this resolver.
@@ -3128,6 +3166,14 @@ queries may be specified using the :any:`listen-on` and :any:`listen-on-v6` opti
    headers. Apart from that, this additional data, while accepted, is
    not currently used by BIND for anything else.
 
+   By default, no client is allowed to send queries that contain
+   PROXYv2 protocol headers, even when support for the protocol is
+   enabled in a :any:`listen-on` statement. If you are interested in
+   enabling the PROXYv2 protocol support, you may also want to take a
+   look at :namedconf:ref:`allow-proxy` and
+   :namedconf:ref:`allow-proxy-on` options to adjust the corresponding
+   ACLs.
+
    If a TLS configuration is specified, :iscman:`named` will listen for DNS-over-TLS
    (DoT) connections, using the key and certificate specified in the
    referenced :any:`tls` statement. If the name ``ephemeral`` is used,
index 4055c9596ccc1cbf911ed17d40da90320774e832..0842327a7333cd9e7d69cabb15af8a31d059fec2 100644 (file)
@@ -62,6 +62,8 @@ managed-keys { <string> ( static-key | initial-key | static-ds | initial-ds ) <i
 options {
        allow-new-zones <boolean>;
        allow-notify { <address_match_element>; ... };
+       allow-proxy { <address_match_element>; ... }; // experimental
+       allow-proxy-on { <address_match_element>; ... }; // experimental
        allow-query { <address_match_element>; ... };
        allow-query-cache { <address_match_element>; ... };
        allow-query-cache-on { <address_match_element>; ... };
@@ -366,6 +368,8 @@ trusted-keys { <string> <integer> <integer> <integer> <quoted_string>; ... }; //
 view <string> [ <class> ] {
        allow-new-zones <boolean>;
        allow-notify { <address_match_element>; ... };
+       allow-proxy { <address_match_element>; ... }; // experimental
+       allow-proxy-on { <address_match_element>; ... }; // experimental
        allow-query { <address_match_element>; ... };
        allow-query-cache { <address_match_element>; ... };
        allow-query-cache-on { <address_match_element>; ... };
index ab7098af7c395aa3cd0c4bf004d5abd10f549e1f..dea045fc3a686728ad1a8129b4b40e4616538b77 100644 (file)
@@ -139,6 +139,8 @@ struct dns_view {
        dns_acl_t            *upfwdacl;
        dns_acl_t            *denyansweracl;
        dns_acl_t            *nocasecompress;
+       dns_acl_t            *proxyacl;
+       dns_acl_t            *proxyonacl;
        bool                  msgcompression;
        dns_nametree_t       *answeracl_exclude;
        dns_nametree_t       *denyanswernames;
index d71892721afe6b252b0a53cc3f32be35532805ca..d277816252ba2548e3e867777061fcea5c96cca5 100644 (file)
@@ -339,6 +339,12 @@ destroy(dns_view_t *view) {
        if (view->pad_acl != NULL) {
                dns_acl_detach(&view->pad_acl);
        }
+       if (view->proxyacl != NULL) {
+               dns_acl_detach(&view->proxyacl);
+       }
+       if (view->proxyonacl != NULL) {
+               dns_acl_detach(&view->proxyonacl);
+       }
        if (view->answeracl_exclude != NULL) {
                dns_nametree_detach(&view->answeracl_exclude);
        }
index cd5df28c3384f012736b61832e9980eed3b19db9..476f3fe04db7fb9e468074d3a95a5c6bde85a0b9 100644 (file)
@@ -595,11 +595,17 @@ check_viewacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
        isc_result_t result = ISC_R_SUCCESS, tresult;
        int i = 0;
 
-       static const char *acls[] = {
-               "allow-query",          "allow-query-on", "allow-query-cache",
-               "allow-query-cache-on", "blackhole",      "match-clients",
-               "match-destinations",   "sortlist",       NULL
-       };
+       static const char *acls[] = { "allow-proxy",
+                                     "allow-proxy-on",
+                                     "allow-query",
+                                     "allow-query-on",
+                                     "allow-query-cache",
+                                     "allow-query-cache-on",
+                                     "blackhole",
+                                     "match-clients",
+                                     "match-destinations",
+                                     "sortlist",
+                                     NULL };
 
        while (acls[i] != NULL) {
                tresult = checkacl(acls[i++], actx, NULL, voptions, config,
index b042d5a620e1fbe1dd5c022fcd7cc9777c56be33..c3f2a7d03b5616ebe6084959b9cedefb6a299365 100644 (file)
@@ -2026,6 +2026,9 @@ static cfg_clausedef_t view_clauses[] = {
        { "additional-from-auth", NULL, CFG_CLAUSEFLAG_ANCIENT },
        { "additional-from-cache", NULL, CFG_CLAUSEFLAG_ANCIENT },
        { "allow-new-zones", &cfg_type_boolean, 0 },
+       { "allow-proxy", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_EXPERIMENTAL },
+       { "allow-proxy-on", &cfg_type_bracketed_aml,
+         CFG_CLAUSEFLAG_EXPERIMENTAL },
        { "allow-query-cache", &cfg_type_bracketed_aml, 0 },
        { "allow-query-cache-on", &cfg_type_bracketed_aml, 0 },
        { "allow-recursion", &cfg_type_bracketed_aml, 0 },
index 6a363c4509eec78ceb94b0ce9cb93e1bb415af2c..34ab13caa333768c0989b6aa667e2d66fea05a11 100644 (file)
@@ -2001,6 +2001,60 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
                return;
        }
 
+       if (isc_nm_is_proxy_handle(client->handle)) {
+               char fmtbuf[ISC_SOCKADDR_FORMATSIZE] = { 0 };
+               isc_netaddr_t real_local_addr, real_peer_addr;
+               isc_sockaddr_t real_local, real_peer;
+               int log_level = ISC_LOG_DEBUG(10);
+
+               real_peer = isc_nmhandle_real_peeraddr(client->handle);
+               isc_netaddr_fromsockaddr(&real_peer_addr, &real_peer);
+               real_local = isc_nmhandle_real_localaddr(client->handle);
+               isc_netaddr_fromsockaddr(&real_local_addr, &real_local);
+
+               /* do not allow by default */
+               if (ns_client_checkaclsilent(client, &real_peer_addr,
+                                            client->view->proxyacl,
+                                            false) != ISC_R_SUCCESS)
+               {
+                       if (isc_log_wouldlog(ns_lctx, log_level)) {
+                               isc_sockaddr_format(&real_peer, fmtbuf,
+                                                   sizeof(fmtbuf));
+                               ns_client_log(
+                                       client, DNS_LOGCATEGORY_SECURITY,
+                                       NS_LOGMODULE_CLIENT, log_level,
+                                       "dropped request: PROXY is not allowed "
+                                       "for that client (real client address: "
+                                       "%s). Rejected by the 'allow-proxy' "
+                                       "ACL",
+                                       fmtbuf);
+                       }
+                       isc_nm_bad_request(handle);
+                       return;
+               }
+
+               /* allow by default */
+               if (ns_client_checkaclsilent(client, &real_local_addr,
+                                            client->view->proxyonacl,
+                                            true) != ISC_R_SUCCESS)
+               {
+                       if (isc_log_wouldlog(ns_lctx, log_level)) {
+                               isc_sockaddr_format(&real_local, fmtbuf,
+                                                   sizeof(fmtbuf));
+                               ns_client_log(
+                                       client, DNS_LOGCATEGORY_SECURITY,
+                                       NS_LOGMODULE_CLIENT, log_level,
+                                       "dropped request: PROXY is not allowed "
+                                       "on the interface (real interface "
+                                       "address: %s). Rejected by the "
+                                       "'allow-proxy-on' ACL",
+                                       fmtbuf);
+                       }
+                       isc_nm_bad_request(handle);
+                       return;
+               }
+       }
+
        ns_client_log(client, NS_LOGCATEGORY_CLIENT, NS_LOGMODULE_CLIENT,
                      ISC_LOG_DEBUG(5), "using view '%s'", client->view->name);