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
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;
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);
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,
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;
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;
}
: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
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,
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
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
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 );
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)
{
/* 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)
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;
}
}
+ 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) {
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 },
#include <stdbool.h>
#include <isc/net.h>
+#include <isc/netmgr.h>
#include <isc/tls.h>
#include <dns/types.h>
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;
};
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.
*
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);
/*%<
}
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",
* 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) {
#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;
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);
return (result);
#else
UNUSED(ifp);
+ UNUSED(proxy);
UNUSED(sslctx);
UNUSED(eps);
UNUSED(neps);
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) {
}
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;
}
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;
}
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))
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;
elt->http_endpoints_number = 0;
elt->http_max_clients = 0;
elt->max_concurrent_streams = 0;
+ elt->proxy = proxy;
*target = elt;
return (ISC_R_SUCCESS);
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;
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;
}
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;
}