]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add 'proxy' option to 'listen-on' statement
authorArtem Boldariev <artem@boldariev.com>
Mon, 30 Oct 2023 15:03:30 +0000 (17:03 +0200)
committerArtem Boldariev <artem@boldariev.com>
Wed, 6 Dec 2023 13:15:25 +0000 (15:15 +0200)
This commit extends "listen-on" statement with "proxy" options that
allows one to enable PROXYv2 support on a dedicated listener. It can
have the following values:

- "plain" to send PROXYv2 headers without encryption, even in the case
of encrypted transports.
- "encrypted" to send PROXYv2 headers encrypted right after the TLS
handshake.

bin/named/server.c
doc/arm/reference.rst
doc/misc/options
lib/isccfg/aclconf.c
lib/isccfg/check.c
lib/isccfg/namedconf.c
lib/ns/include/ns/listenlist.h
lib/ns/interfacemgr.c
lib/ns/listenlist.c

index b57dc5058e55426732d1d532e669a1a6aa907113..5af25d1c091fd1ba102337724362dbb9dce01cae 100644 (file)
@@ -413,7 +413,8 @@ static isc_result_t
 listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls,
               const ns_listen_tls_params_t *tls_params,
               isc_tlsctx_cache_t *tlsctx_cache, in_port_t port,
-              isc_mem_t *mctx, ns_listenelt_t **target);
+              isc_mem_t *mctx, isc_nm_proxy_type_t proxy,
+              ns_listenelt_t **target);
 #endif
 
 static isc_result_t
@@ -10791,6 +10792,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
        const cfg_obj_t *tlsobj = NULL, *httpobj = NULL;
        const cfg_obj_t *portobj = NULL;
        const cfg_obj_t *http_server = NULL;
+       const cfg_obj_t *proxyobj = NULL;
        in_port_t port = 0;
        const char *key = NULL, *cert = NULL, *ca_file = NULL,
                   *dhparam_file = NULL, *ciphers = NULL;
@@ -10802,6 +10804,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
        uint32_t tls_protos = 0;
        ns_listen_tls_params_t tls_params = { 0 };
        const char *tlsname = NULL;
+       isc_nm_proxy_type_t proxy = ISC_NM_PROXY_NONE;
 
        REQUIRE(target != NULL && *target == NULL);
 
@@ -10985,16 +10988,31 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
                port = (in_port_t)cfg_obj_asuint32(portobj);
        }
 
+       proxyobj = cfg_tuple_get(ltup, "proxy");
+       if (proxyobj != NULL && cfg_obj_isstring(proxyobj)) {
+               const char *proxyval = cfg_obj_asstring(proxyobj);
+
+               if (strcasecmp(proxyval, "encrypted") == 0) {
+                       INSIST(do_tls == true);
+                       proxy = ISC_NM_PROXY_ENCRYPTED;
+               } else if (strcasecmp(proxyval, "plain") == 0) {
+                       proxy = ISC_NM_PROXY_PLAIN;
+               } else {
+                       UNREACHABLE();
+               }
+       }
+
 #ifdef HAVE_LIBNGHTTP2
        if (http) {
                CHECK(listenelt_http(http_server, family, do_tls, &tls_params,
-                                    tlsctx_cache, port, mctx, &delt));
+                                    tlsctx_cache, port, mctx, proxy, &delt));
        }
 #endif /* HAVE_LIBNGHTTP2 */
 
        if (!http) {
                CHECK(ns_listenelt_create(mctx, port, NULL, family, do_tls,
-                                         &tls_params, tlsctx_cache, &delt));
+                                         &tls_params, tlsctx_cache, proxy,
+                                         &delt));
        }
 
        result = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"), config,
@@ -11015,7 +11033,8 @@ static isc_result_t
 listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls,
               const ns_listen_tls_params_t *tls_params,
               isc_tlsctx_cache_t *tlsctx_cache, in_port_t port,
-              isc_mem_t *mctx, ns_listenelt_t **target) {
+              isc_mem_t *mctx, isc_nm_proxy_type_t proxy,
+              ns_listenelt_t **target) {
        isc_result_t result = ISC_R_SUCCESS;
        ns_listenelt_t *delt = NULL;
        char **endpoints = NULL;
@@ -11080,9 +11099,9 @@ listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls,
 
        INSIST(i == len);
 
-       result = ns_listenelt_create_http(mctx, port, NULL, family, tls,
-                                         tls_params, tlsctx_cache, endpoints,
-                                         len, max_clients, max_streams, &delt);
+       result = ns_listenelt_create_http(
+               mctx, port, NULL, family, tls, tls_params, tlsctx_cache, proxy,
+               endpoints, len, max_clients, max_streams, &delt);
        if (result != ISC_R_SUCCESS) {
                goto error;
        }
index eb72f1e5aedc13a67651546e6d046e29ea07d623..16c46183c654538bf9ef9238748322155ca9c373 100644 (file)
@@ -3066,9 +3066,10 @@ queries may be specified using the :any:`listen-on` and :any:`listen-on-v6` opti
    :tags: server
    :short: Specifies the IPv6 addresses on which a server listens for DNS queries.
 
-   The :any:`listen-on` and :any:`listen-on-v6` statements can each take an optional
-   port, TLS configuration identifier, and/or HTTP configuration identifier,
-   in addition to an :term:`address_match_list`.
+   The :any:`listen-on` and :any:`listen-on-v6` statements can each
+   take an optional port, PROXYv2 support switch, TLS configuration
+   identifier, and/or HTTP configuration identifier, in addition to an
+   :term:`address_match_list`.
 
    The :term:`address_match_list` in :any:`listen-on` specifies the IPv4 addresses
    on which the server will listen. (IPv6 addresses are ignored, with a
@@ -3081,6 +3082,52 @@ queries may be specified using the :any:`listen-on` and :any:`listen-on-v6` opti
    If no :any:`listen-on-v6` is specified, the default is to listen for standard
    DNS queries on port 53 of all IPv6 interfaces.
 
+   When specified, the PROXYv2 support switch ``proxy`` allows
+   enabling the PROXYv2 protocol support. The PROXYv2 protocol
+   provides the means for passing connection information, such as a
+   client's source and destination addresses and ports, across
+   multiple layers of NAT or TCP/UDP proxies to back-end servers. The
+   addresses passed to by the PROXYv2 protocol are then used instead
+   of the peer and interface addresses provided by the operating
+   system.
+
+   The ``proxy`` switch can have the following values:
+
+   * ``plain`` - accept plain PROXYv2 headers. It is the only valid
+     option for transports that do not employ encryption. In the case
+     of transports that employ encryption, it instructs BIND that
+     PROXYv2 headers are sent without encryption before the TLS
+     handshake. In that case, only PROXYv2 headers are not encrypted.
+   * ``encrypted`` - accept encrypted PROXYv2 headers. In the case of
+     transports that employ encryption, it instructs BIND that PROXYv2
+     headers are sent encrypted immediately after the TLS
+     handshake. The option is valid only for the transports that employ
+     encryption.
+
+   You must consult your proxying front-end software documentation to
+   decide which value you need to use. If in doubt, use ``plain`` for
+   encrypted transports, especially for DNS-over-HTTPS (DoH), but
+   DNS-specific software is likely to need ``encrypted``.
+
+   It should be noted that when PROXYv2 is enabled on a listener, it
+   loses the ability to accept regular DNS queries without associated
+   PROXYv2 headers.
+
+   In some cases, PROXYv2 headers might not contain usable source and
+   destination addresses. In particular, that happens when the headers
+   use ``LOCAL`` command or the headers that use unspecified or
+   unsupported by BIND address types. If otherwise correct, such
+   headers are accepted by BIND and the real endpoint addresses are
+   used in these cases.
+
+   The PROXYv2 protocol is designed to be extensible and can carry
+   additional information in the form of type-length-values
+   (TLVs). Many of the types are defined in the protocol
+   specification, and for some of these, we do a reasonable amount of
+   validation in order to detect and reject ill-formed or hand-crafted
+   headers. Apart from that, this additional data, while accepted, is
+   not currently used by BIND for anything else.
+
    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,
@@ -3110,6 +3157,9 @@ queries may be specified using the :any:`listen-on` and :any:`listen-on-v6` opti
       listen-on port 1234 { !1.2.3.4; 1.2/16; };
       listen-on port 8853 tls ephemeral { 4.3.2.1; };
       listen-on port 8453 tls ephemeral http myserver { 8.7.6.5; };
+      listen-on port 5300 proxy plain { !1.2.3.4; 1.2/16; };
+      listen-on port 8953 proxy encrypted tls ephemeral { 4.3.2.1; };
+      listen-on port 8553 proxy plain tls ephemeral http myserver { 8.7.6.5; };
 
    The first two lines instruct the name server to listen for standard DNS
    queries on port 53 of the IP address 5.6.7.8 and on port 1234 of an address
@@ -3126,9 +3176,12 @@ queries may be specified using the :any:`listen-on` and :any:`listen-on-v6` opti
 
       listen-on-v6 { any; };
       listen-on-v6 port 1234 { !2001:db8::/32; any; };
-      listen-on port 8853 tls example-tls { 2001:db8::100; };
-      listen-on port 8453 tls example-tls http default { 2001:db8::100; };
-      listen-on port 8000 tls none http myserver { 2001:db8::100; };
+      listen-on-v6 port 8853 tls example-tls { 2001:db8::100; };
+      listen-on-v6 port 8453 tls example-tls http default { 2001:db8::100; };
+      listen-on-v6 port 8000 tls none http myserver { 2001:db8::100; };
+      listen-on-v6 port 53000 proxy plain { !2001:db8::/32; any; };
+      listen-on-v6 port 8953 proxy encrypted tls example-tls { 2001:db8::100; };
+      listen-on-v6 port 8553 proxy plain tls example-tls http default { 2001:db8::100; };
 
    The first two lines instruct the name server to listen for standard DNS
    queries on port 53 of any IPv6 addresses, and on port 1234 of IPv6
index 337744fdf05944bb41151d675e3c0c98506ad452..4055c9596ccc1cbf911ed17d40da90320774e832 100644 (file)
@@ -161,8 +161,8 @@ options {
        keep-response-order { <address_match_element>; ... }; // obsolete
        key-directory <quoted_string>;
        lame-ttl <duration>;
-       listen-on [ port <integer> ] [ tls <string> ] [ http <string> ] { <address_match_element>; ... }; // may occur multiple times
-       listen-on-v6 [ port <integer> ] [ tls <string> ] [ http <string> ] { <address_match_element>; ... }; // may occur multiple times
+       listen-on [ port <integer> ] [ proxy <string> ] [ tls <string> ] [ http <string> ] { <address_match_element>; ... }; // may occur multiple times
+       listen-on-v6 [ port <integer> ] [ proxy <string> ] [ tls <string> ] [ http <string> ] { <address_match_element>; ... }; // may occur multiple times
        lmdb-mapsize <sizeval>;
        managed-keys-directory <quoted_string>;
        masterfile-format ( raw | text );
index 2914106482bcfd63b972555bdc62cc3914500681..e2b5b065b49a92db5e7a7e2a93cfe1d5d8b68585 100644 (file)
@@ -701,7 +701,8 @@ cfg_acl_fromconfig(const cfg_obj_t *acl_data, const cfg_obj_t *cctx,
                        if (strcasecmp(cfg_obj_asstring(obj_transport),
                                       "udp") == 0)
                        {
-                               transports = isc_nm_udpsocket;
+                               transports = isc_nm_udpsocket |
+                                            isc_nm_proxyudpsocket;
                                encrypted = false;
                        } else if (strcasecmp(cfg_obj_asstring(obj_transport),
                                              "tcp") == 0)
@@ -713,7 +714,8 @@ cfg_acl_fromconfig(const cfg_obj_t *acl_data, const cfg_obj_t *cctx,
                        {
                                /* Good ol' DNS over port 53 */
                                transports = isc_nm_streamdnssocket |
-                                            isc_nm_udpsocket;
+                                            isc_nm_udpsocket |
+                                            isc_nm_proxyudpsocket;
                                encrypted = false;
                        } else if (strcasecmp(cfg_obj_asstring(obj_transport),
                                              "tls") == 0)
index 4e167ba4449c4726038d4f94117dc73dae85c225..cd5df28c3384f012736b61832e9980eed3b19db9 100644 (file)
@@ -1033,6 +1033,7 @@ check_listener(const cfg_obj_t *listener, const cfg_obj_t *config,
        const cfg_obj_t *tlsobj = NULL, *httpobj = NULL;
        const cfg_obj_t *portobj = NULL;
        const cfg_obj_t *http_server = NULL;
+       const cfg_obj_t *proxyobj = NULL;
        bool do_tls = false, no_tls = false;
        dns_acl_t *acl = NULL;
 
@@ -1097,6 +1098,36 @@ check_listener(const cfg_obj_t *listener, const cfg_obj_t *config,
                }
        }
 
+       proxyobj = cfg_tuple_get(ltup, "proxy");
+       if (proxyobj != NULL && cfg_obj_isstring(proxyobj)) {
+               const char *proxyval = cfg_obj_asstring(proxyobj);
+               if (proxyval == NULL ||
+                   (strcasecmp(proxyval, "encrypted") != 0 &&
+                    strcasecmp(proxyval, "plain") != 0))
+               {
+                       cfg_obj_log(proxyobj, logctx, ISC_LOG_ERROR,
+                                   "'proxy' must have one of the following "
+                                   "values: 'plain', 'encrypted'");
+
+                       if (result == ISC_R_SUCCESS) {
+                               result = ISC_R_FAILURE;
+                       }
+               }
+
+               if (proxyval != NULL &&
+                   strcasecmp(proxyval, "encrypted") == 0 && !do_tls)
+               {
+                       cfg_obj_log(proxyobj, logctx, ISC_LOG_ERROR,
+                                   "'proxy encrypted' can be used only when "
+                                   "encryption is enabled by setting 'tls' to "
+                                   "a defined value or to 'ephemeral'");
+
+                       if (result == ISC_R_SUCCESS) {
+                               result = ISC_R_FAILURE;
+                       }
+               }
+       }
+
        tresult = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"), config,
                                     logctx, actx, mctx, 0, &acl);
        if (result == ISC_R_SUCCESS) {
index 5554b3f20860ec1a7e153de09ade5072e884fe67..b042d5a620e1fbe1dd5c022fcd7cc9777c56be33 100644 (file)
@@ -150,6 +150,11 @@ static cfg_type_t cfg_type_zone;
 
 static cfg_tuplefielddef_t listenon_tuple_fields[] = {
        { "port", &cfg_type_optional_port, 0 },
+       /*
+        * Let's follow the protocols encapsulation order (lower->upper), at
+        * least roughly.
+        */
+       { "proxy", &cfg_type_astring, CFG_CLAUSEFLAG_EXPERIMENTAL },
        { "tls", &cfg_type_astring, 0 },
 #if HAVE_LIBNGHTTP2
        { "http", &cfg_type_astring, 0 },
index 5d0a19b285110053de49ac1a55ca39417b7b55d3..0f063470584dd434fa51691e3e07496137d4ca3b 100644 (file)
@@ -29,6 +29,7 @@
 #include <stdbool.h>
 
 #include <isc/net.h>
+#include <isc/netmgr.h>
 #include <isc/tls.h>
 
 #include <dns/types.h>
@@ -51,6 +52,7 @@ struct ns_listenelt {
        size_t              http_endpoints_number;
        uint32_t            http_max_clients;
        uint32_t            max_concurrent_streams;
+       isc_nm_proxy_type_t proxy;
        ISC_LINK(ns_listenelt_t) link;
 };
 
@@ -82,7 +84,8 @@ isc_result_t
 ns_listenelt_create(isc_mem_t *mctx, in_port_t port, dns_acl_t *acl,
                    const uint16_t family, bool tls,
                    const ns_listen_tls_params_t *tls_params,
-                   isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target);
+                   isc_tlsctx_cache_t *tlsctx_cache, isc_nm_proxy_type_t proxy,
+                   ns_listenelt_t **target);
 /*%<
  * Create a listen-on list element.
  *
@@ -96,7 +99,8 @@ isc_result_t
 ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, dns_acl_t *acl,
                         const uint16_t family, bool tls,
                         const ns_listen_tls_params_t *tls_params,
-                        isc_tlsctx_cache_t *tlsctx_cache, char **endpoints,
+                        isc_tlsctx_cache_t           *tlsctx_cache,
+                        isc_nm_proxy_type_t proxy, char **endpoints,
                         size_t nendpoints, const uint32_t max_clients,
                         const uint32_t max_streams, ns_listenelt_t **target);
 /*%<
index 39c4bca9f1980603f1d250f5590c047c6cfb84be..b94d8600c2df8c5406aa0471d406f3d033e99535 100644 (file)
@@ -471,25 +471,31 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
 }
 
 static isc_result_t
-ns_interface_listenudp(ns_interface_t *ifp) {
+ns_interface_listenudp(ns_interface_t *ifp, isc_nm_proxy_type_t proxy) {
        isc_result_t result;
 
        /* Reserve space for an ns_client_t with the netmgr handle */
-       result = isc_nm_listenudp(ifp->mgr->nm, ISC_NM_LISTEN_ALL, &ifp->addr,
-                                 ns_client_request, ifp,
-                                 &ifp->udplistensocket);
+       if (proxy == ISC_NM_PROXY_NONE) {
+               result = isc_nm_listenudp(ifp->mgr->nm, ISC_NM_LISTEN_ALL,
+                                         &ifp->addr, ns_client_request, ifp,
+                                         &ifp->udplistensocket);
+       } else {
+               INSIST(proxy == ISC_NM_PROXY_PLAIN);
+               result = isc_nm_listenproxyudp(ifp->mgr->nm, ISC_NM_LISTEN_ALL,
+                                              &ifp->addr, ns_client_request,
+                                              ifp, &ifp->udplistensocket);
+       }
        return (result);
 }
 
 static isc_result_t
-ns_interface_listentcp(ns_interface_t *ifp) {
+ns_interface_listentcp(ns_interface_t *ifp, isc_nm_proxy_type_t proxy) {
        isc_result_t result;
 
        result = isc_nm_listenstreamdns(
                ifp->mgr->nm, ISC_NM_LISTEN_ALL, &ifp->addr, ns_client_request,
                ifp, ns__client_tcpconn, ifp, ifp->mgr->backlog,
-               &ifp->mgr->sctx->tcpquota, NULL, ISC_NM_PROXY_NONE,
-               &ifp->tcplistensocket);
+               &ifp->mgr->sctx->tcpquota, NULL, proxy, &ifp->tcplistensocket);
        if (result != ISC_R_SUCCESS) {
                isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
                              "creating TCP socket: %s",
@@ -516,13 +522,14 @@ ns_interface_listentcp(ns_interface_t *ifp) {
  * TLS related options.
  */
 static isc_result_t
-ns_interface_listentls(ns_interface_t *ifp, isc_tlsctx_t *sslctx) {
+ns_interface_listentls(ns_interface_t *ifp, isc_nm_proxy_type_t proxy,
+                      isc_tlsctx_t *sslctx) {
        isc_result_t result;
 
        result = isc_nm_listenstreamdns(
                ifp->mgr->nm, ISC_NM_LISTEN_ALL, &ifp->addr, ns_client_request,
                ifp, ns__client_tcpconn, ifp, ifp->mgr->backlog,
-               &ifp->mgr->sctx->tcpquota, sslctx, ISC_NM_PROXY_NONE,
+               &ifp->mgr->sctx->tcpquota, sslctx, proxy,
                &ifp->tcplistensocket);
 
        if (result != ISC_R_SUCCESS) {
@@ -566,9 +573,9 @@ load_http_endpoints(isc_nm_http_endpoints_t *epset, ns_interface_t *ifp,
 #endif /* HAVE_LIBNGHTTP2 */
 
 static isc_result_t
-ns_interface_listenhttp(ns_interface_t *ifp, isc_tlsctx_t *sslctx, char **eps,
-                       size_t neps, uint32_t max_clients,
-                       uint32_t max_concurrent_streams) {
+ns_interface_listenhttp(ns_interface_t *ifp, isc_nm_proxy_type_t proxy,
+                       isc_tlsctx_t *sslctx, char **eps, size_t neps,
+                       uint32_t max_clients, uint32_t max_concurrent_streams) {
 #if HAVE_LIBNGHTTP2
        isc_result_t result = ISC_R_FAILURE;
        isc_nmsocket_t *sock = NULL;
@@ -585,7 +592,7 @@ ns_interface_listenhttp(ns_interface_t *ifp, isc_tlsctx_t *sslctx, char **eps,
                result = isc_nm_listenhttp(
                        ifp->mgr->nm, ISC_NM_LISTEN_ALL, &ifp->addr,
                        ifp->mgr->backlog, quota, sslctx, epset,
-                       max_concurrent_streams, ISC_NM_PROXY_NONE, &sock);
+                       max_concurrent_streams, proxy, &sock);
        }
 
        isc_nm_http_endpoints_detach(&epset);
@@ -629,6 +636,7 @@ ns_interface_listenhttp(ns_interface_t *ifp, isc_tlsctx_t *sslctx, char **eps,
        return (result);
 #else
        UNUSED(ifp);
+       UNUSED(proxy);
        UNUSED(sslctx);
        UNUSED(eps);
        UNUSED(neps);
@@ -660,7 +668,7 @@ interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, const char *name,
 
        if (elt->is_http) {
                result = ns_interface_listenhttp(
-                       ifp, elt->sslctx, elt->http_endpoints,
+                       ifp, elt->proxy, elt->sslctx, elt->http_endpoints,
                        elt->http_endpoints_number, elt->http_max_clients,
                        elt->max_concurrent_streams);
                if (result != ISC_R_SUCCESS) {
@@ -671,7 +679,7 @@ interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, const char *name,
        }
 
        if (elt->sslctx != NULL) {
-               result = ns_interface_listentls(ifp, elt->sslctx);
+               result = ns_interface_listentls(ifp, elt->proxy, elt->sslctx);
                if (result != ISC_R_SUCCESS) {
                        goto cleanup_interface;
                }
@@ -679,7 +687,7 @@ interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, const char *name,
                return (result);
        }
 
-       result = ns_interface_listenudp(ifp);
+       result = ns_interface_listenudp(ifp, elt->proxy);
        if (result != ISC_R_SUCCESS) {
                if ((result == ISC_R_ADDRINUSE) && (addr_in_use != NULL)) {
                        *addr_in_use = true;
@@ -688,7 +696,7 @@ interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, const char *name,
        }
 
        if (((mgr->sctx->options & NS_SERVER_NOTCP) == 0)) {
-               result = ns_interface_listentcp(ifp);
+               result = ns_interface_listentcp(ifp, elt->proxy);
                if (result != ISC_R_SUCCESS) {
                        if ((result == ISC_R_ADDRINUSE) &&
                            (addr_in_use != NULL))
index a4a7c2b2e4def514d6b56c0f9cdd585b358b3216..d267ebe1eae68085cd8e2c10d1a3a6b2dc8dbcb3 100644 (file)
@@ -31,7 +31,8 @@ static isc_result_t
 listenelt_create(isc_mem_t *mctx, in_port_t port, dns_acl_t *acl,
                 const uint16_t family, const bool is_http, bool tls,
                 const ns_listen_tls_params_t *tls_params,
-                isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target) {
+                isc_tlsctx_cache_t *tlsctx_cache, isc_nm_proxy_type_t proxy,
+                ns_listenelt_t **target) {
        ns_listenelt_t *elt = NULL;
        isc_result_t result = ISC_R_SUCCESS;
        isc_tlsctx_t *sslctx = NULL;
@@ -192,6 +193,7 @@ listenelt_create(isc_mem_t *mctx, in_port_t port, dns_acl_t *acl,
        elt->http_endpoints_number = 0;
        elt->http_max_clients = 0;
        elt->max_concurrent_streams = 0;
+       elt->proxy = proxy;
 
        *target = elt;
        return (ISC_R_SUCCESS);
@@ -210,16 +212,18 @@ isc_result_t
 ns_listenelt_create(isc_mem_t *mctx, in_port_t port, dns_acl_t *acl,
                    const uint16_t family, bool tls,
                    const ns_listen_tls_params_t *tls_params,
-                   isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target) {
+                   isc_tlsctx_cache_t *tlsctx_cache, isc_nm_proxy_type_t proxy,
+                   ns_listenelt_t **target) {
        return listenelt_create(mctx, port, acl, family, false, tls, tls_params,
-                               tlsctx_cache, target);
+                               tlsctx_cache, proxy, target);
 }
 
 isc_result_t
 ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, dns_acl_t *acl,
                         const uint16_t family, bool tls,
                         const ns_listen_tls_params_t *tls_params,
-                        isc_tlsctx_cache_t *tlsctx_cache, char **endpoints,
+                        isc_tlsctx_cache_t *tlsctx_cache,
+                        isc_nm_proxy_type_t proxy, char **endpoints,
                         size_t nendpoints, const uint32_t max_clients,
                         const uint32_t max_streams, ns_listenelt_t **target) {
        isc_result_t result;
@@ -229,7 +233,7 @@ ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, dns_acl_t *acl,
        REQUIRE(nendpoints > 0);
 
        result = listenelt_create(mctx, http_port, acl, family, true, tls,
-                                 tls_params, tlsctx_cache, target);
+                                 tls_params, tlsctx_cache, proxy, target);
        if (result == ISC_R_SUCCESS) {
                (*target)->is_http = true;
                (*target)->http_endpoints = endpoints;
@@ -334,7 +338,7 @@ ns_listenlist_default(isc_mem_t *mctx, in_port_t port, bool enabled,
        }
 
        result = ns_listenelt_create(mctx, port, acl, family, false, NULL, NULL,
-                                    &elt);
+                                    ISC_NM_PROXY_NONE, &elt);
        if (result != ISC_R_SUCCESS) {
                goto cleanup_acl;
        }