mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-31 14:35:26 +00:00
Add PROXY over TLS support to PROXY Stream
This commit makes it possible to use PROXY Stream not only over TCP, but also over TLS. That is, now PROXY Stream can work in two modes as far as TLS is involved: 1. PROXY over (plain) TCP - PROXYv2 headers are sent unencrypted before TLS handshake messages. That is the main mode as described in the PROXY protocol specification (as it is clearly stated there), and most of the software expects PROXYv2 support to be implemented that way (e.g. HAProxy); 2. PROXY over (encrypted) TLS - PROXYv2 headers are sent after the TLS handshake has happened. For example, this mode is being used (only ?) by "dnsdist". As far as I can see, that is, in fact, a deviation from the spec, but I can certainly see how PROXYv2 could end up being implemented this way elsewhere.
This commit is contained in:
@@ -436,11 +436,11 @@ isc_nm_listenstreamdns(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface,
|
||||
isc_result_t
|
||||
isc_nm_listenproxystream(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface,
|
||||
isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
|
||||
int backlog, isc_quota_t *quota,
|
||||
int backlog, isc_quota_t *quota, isc_tlsctx_t *tlsctx,
|
||||
isc_nmsocket_t **sockp);
|
||||
/*%<
|
||||
* Start listening for data preceded by a PROXYv2 header over the
|
||||
* TCP on interface 'iface', using net manager 'mgr'.
|
||||
* TCP or TLS on interface 'iface', using net manager 'mgr'.
|
||||
*
|
||||
* On success, 'sockp' will be updated to contain a new listening TCP
|
||||
* socket.
|
||||
@@ -450,13 +450,18 @@ isc_nm_listenproxystream(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface,
|
||||
*
|
||||
* If 'quota' is not NULL, then the socket is attached to the specified
|
||||
* quota. This allows us to enforce TCP client quota limits.
|
||||
*
|
||||
* If 'tlsctx' is not NULL, then listen for TLS connections. In that
|
||||
* case PROXYv2 headers are expected to be sent encrypted right after
|
||||
* the TLS handshake.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_nm_proxystreamconnect(isc_nm_t *mgr, isc_sockaddr_t *local,
|
||||
isc_sockaddr_t *peer, isc_nm_cb_t cb, void *cbarg,
|
||||
unsigned int timeout,
|
||||
isc_nm_proxyheader_info_t *proxy_info);
|
||||
unsigned int timeout, isc_tlsctx_t *tlsctx,
|
||||
isc_tlsctx_client_session_cache_t *client_sess_cache,
|
||||
isc_nm_proxyheader_info_t *proxy_info);
|
||||
/*%<
|
||||
* Create a TCP socket using netmgr 'mgr', bind it to the address
|
||||
* 'local', and connect it to the address 'peer'. Right after the
|
||||
|
@@ -1223,6 +1223,20 @@ void
|
||||
isc__nm_proxystream_senddns(isc_nmhandle_t *handle, isc_region_t *region,
|
||||
isc_nm_cb_t cb, void *cbarg);
|
||||
|
||||
void
|
||||
isc__nm_proxystream_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx);
|
||||
|
||||
bool
|
||||
isc__nm_proxystream_has_encryption(const isc_nmhandle_t *handle);
|
||||
|
||||
const char *
|
||||
isc__nm_proxystream_verify_tls_peer_result_string(const isc_nmhandle_t *handle);
|
||||
|
||||
void
|
||||
isc__nmhandle_proxystream_get_selected_alpn(isc_nmhandle_t *handle,
|
||||
const unsigned char **alpn,
|
||||
unsigned int *alpnlen);
|
||||
|
||||
void
|
||||
isc__nm_incstats(isc_nmsocket_t *sock, isc__nm_statid_t id);
|
||||
/*%<
|
||||
|
@@ -2353,6 +2353,8 @@ isc_nm_has_encryption(const isc_nmhandle_t *handle) {
|
||||
#endif /* HAVE_LIBNGHTTP2 */
|
||||
case isc_nm_streamdnssocket:
|
||||
return (isc__nm_streamdns_has_encryption(handle));
|
||||
case isc_nm_proxystreamsocket:
|
||||
return (isc__nm_proxystream_has_encryption(handle));
|
||||
default:
|
||||
return (false);
|
||||
};
|
||||
@@ -2456,6 +2458,9 @@ isc_nmsocket_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) {
|
||||
case isc_nm_streamdnslistener:
|
||||
isc__nm_streamdns_set_tlsctx(listener, tlsctx);
|
||||
break;
|
||||
case isc_nm_proxystreamlistener:
|
||||
isc__nm_proxystream_set_tlsctx(listener, tlsctx);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
@@ -2697,6 +2702,10 @@ isc__nmhandle_get_selected_alpn(isc_nmhandle_t *handle,
|
||||
case isc_nm_tlssocket:
|
||||
isc__nmhandle_tls_get_selected_alpn(handle, alpn, alpnlen);
|
||||
return;
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nmhandle_proxystream_get_selected_alpn(handle, alpn,
|
||||
alpnlen);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
@@ -316,7 +316,7 @@ proxystream_accept_cb(isc_nmhandle_t *handle, isc_result_t result,
|
||||
isc_result_t
|
||||
isc_nm_listenproxystream(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface,
|
||||
isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
|
||||
int backlog, isc_quota_t *quota,
|
||||
int backlog, isc_quota_t *quota, isc_tlsctx_t *tlsctx,
|
||||
isc_nmsocket_t **sockp) {
|
||||
isc_result_t result;
|
||||
isc_nmsocket_t *listener = NULL;
|
||||
@@ -335,8 +335,15 @@ isc_nm_listenproxystream(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface,
|
||||
listener->accept_cb = accept_cb;
|
||||
listener->accept_cbarg = accept_cbarg;
|
||||
|
||||
result = isc_nm_listentcp(mgr, workers, iface, proxystream_accept_cb,
|
||||
listener, backlog, quota, &listener->outer);
|
||||
if (tlsctx == NULL) {
|
||||
result = isc_nm_listentcp(mgr, workers, iface,
|
||||
proxystream_accept_cb, listener,
|
||||
backlog, quota, &listener->outer);
|
||||
} else {
|
||||
result = isc_nm_listentls(
|
||||
mgr, workers, iface, proxystream_accept_cb, listener,
|
||||
backlog, quota, tlsctx, false, &listener->outer);
|
||||
}
|
||||
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
listener->closed = true;
|
||||
@@ -451,7 +458,8 @@ error:
|
||||
void
|
||||
isc_nm_proxystreamconnect(isc_nm_t *mgr, isc_sockaddr_t *local,
|
||||
isc_sockaddr_t *peer, isc_nm_cb_t cb, void *cbarg,
|
||||
unsigned int timeout,
|
||||
unsigned int timeout, isc_tlsctx_t *tlsctx,
|
||||
isc_tlsctx_client_session_cache_t *client_sess_cache,
|
||||
isc_nm_proxyheader_info_t *proxy_info) {
|
||||
isc_result_t result = ISC_R_FAILURE;
|
||||
isc_nmsocket_t *nsock = NULL;
|
||||
@@ -487,8 +495,15 @@ isc_nm_proxystreamconnect(isc_nm_t *mgr, isc_sockaddr_t *local,
|
||||
&proxy_info->proxy_info.tlv_data);
|
||||
}
|
||||
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||
isc_nm_tcpconnect(mgr, local, peer, proxystream_connect_cb, nsock,
|
||||
nsock->connect_timeout);
|
||||
|
||||
if (tlsctx == NULL) {
|
||||
isc_nm_tcpconnect(mgr, local, peer, proxystream_connect_cb,
|
||||
nsock, nsock->connect_timeout);
|
||||
} else {
|
||||
isc_nm_tlsconnect(mgr, local, peer, proxystream_connect_cb,
|
||||
nsock, tlsctx, client_sess_cache,
|
||||
nsock->connect_timeout, false, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -537,6 +552,7 @@ void
|
||||
isc__nm_proxystream_cleanup_data(isc_nmsocket_t *sock) {
|
||||
switch (sock->type) {
|
||||
case isc_nm_tcpsocket:
|
||||
case isc_nm_tlssocket:
|
||||
if (sock->proxy.sock != NULL) {
|
||||
isc__nmsocket_detach(&sock->proxy.sock);
|
||||
}
|
||||
@@ -1087,3 +1103,64 @@ isc__nm_proxystream_senddns(isc_nmhandle_t *handle, isc_region_t *region,
|
||||
isc_nm_cb_t cb, void *cbarg) {
|
||||
proxystream_send(handle, region, cb, cbarg, true);
|
||||
}
|
||||
|
||||
void
|
||||
isc__nm_proxystream_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) {
|
||||
REQUIRE(VALID_NMSOCK(listener));
|
||||
REQUIRE(listener->type == isc_nm_proxystreamlistener);
|
||||
|
||||
if (listener->outer != NULL) {
|
||||
INSIST(VALID_NMSOCK(listener->outer));
|
||||
isc_nmsocket_set_tlsctx(listener->outer, tlsctx);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
isc__nm_proxystream_has_encryption(const isc_nmhandle_t *handle) {
|
||||
isc_nmsocket_t *sock = NULL;
|
||||
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
REQUIRE(VALID_NMSOCK(handle->sock));
|
||||
REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
|
||||
|
||||
sock = handle->sock;
|
||||
if (sock->outerhandle != NULL) {
|
||||
INSIST(VALID_NMHANDLE(sock->outerhandle));
|
||||
return (isc_nm_has_encryption(sock->outerhandle));
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
const char *
|
||||
isc__nm_proxystream_verify_tls_peer_result_string(const isc_nmhandle_t *handle) {
|
||||
isc_nmsocket_t *sock = NULL;
|
||||
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
REQUIRE(VALID_NMSOCK(handle->sock));
|
||||
REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
|
||||
|
||||
sock = handle->sock;
|
||||
if (sock->outerhandle != NULL) {
|
||||
INSIST(VALID_NMHANDLE(sock->outerhandle));
|
||||
return (isc_nm_verify_tls_peer_result_string(
|
||||
sock->outerhandle));
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void
|
||||
isc__nmhandle_proxystream_get_selected_alpn(isc_nmhandle_t *handle,
|
||||
const unsigned char **alpn,
|
||||
unsigned int *alpnlen) {
|
||||
isc_nmsocket_t *sock;
|
||||
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
sock = handle->sock;
|
||||
REQUIRE(VALID_NMSOCK(sock));
|
||||
REQUIRE(sock->type == isc_nm_proxystreamsocket);
|
||||
REQUIRE(sock->tid == isc_tid());
|
||||
|
||||
isc__nmhandle_get_selected_alpn(sock->outerhandle, alpn, alpnlen);
|
||||
}
|
||||
|
Reference in New Issue
Block a user