]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add PROXY over TLS support to PROXY Stream
authorArtem Boldariev <artem@boldariev.com>
Tue, 17 Oct 2023 17:36:58 +0000 (20:36 +0300)
committerArtem Boldariev <artem@boldariev.com>
Wed, 6 Dec 2023 13:15:24 +0000 (15:15 +0200)
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.

lib/isc/include/isc/netmgr.h
lib/isc/netmgr/netmgr-int.h
lib/isc/netmgr/netmgr.c
lib/isc/netmgr/proxystream.c

index 8002884aea9edf49c94e28c3cfd0dd6990e259ca..e6058227f7acb6c7370d91cabda38d3529002e60 100644 (file)
@@ -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
index 34c4982c0e29334d06ad893331cddefbecc5b55a..3493e31d305f9b76d88ddad1db5651526b496ec4 100644 (file)
@@ -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);
 /*%<
index 0ac2529c886aa0da3bd5643e74aa4580349fc581..3b2b2bb22fc2d64d857ffabf9d69643705a79449 100644 (file)
@@ -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;
        };
index 39b154d06f50a936b32697cba586cabfcacf9275..893f1ccf0db6840b0b828373d31d96b0551217ec 100644 (file)
@@ -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);
+}