Skip to content

Commit 23e2f2b

Browse files
tls: support TLS-in-TLS for HTTPS proxy tunnels
Two related changes: SNI priority fix: when the TLS context carries an explicit vhost (e.g. the proxy hostname on a proxy TLS context), that vhost now takes priority over proxied_host in flb_tls_session_create. Previously proxied_host was used unconditionally for upstream connections, causing the proxy TLS handshake to advertise the destination hostname instead of the proxy hostname. TLS-in-TLS chaining: add session_set_outer to struct flb_tls_backend and implement tls_session_set_outer in the OpenSSL backend using BIO_f_ssl. When flb_tls_session_create detects an existing tls_session on the connection (the proxy TLS session), it chains the new inner session's I/O through the outer session via SSL_set_bio, so that the destination TLS handshake travels inside the already-established proxy TLS tunnel rather than going directly to the raw socket. Signed-off-by: Antônio Franco <13881523+antoniomrfranco@users.noreply.github.com>
1 parent 0bd5c2e commit 23e2f2b

3 files changed

Lines changed: 99 additions & 4 deletions

File tree

include/fluent-bit/tls/flb_tls.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ struct flb_tls_backend {
8888
void (*session_invalidate) (void *);
8989
int (*session_destroy) (void *);
9090
const char *(*session_alpn_get) (void *);
91+
/*
92+
* Chain an inner TLS session's I/O through an outer TLS session.
93+
* Used for TLS-in-TLS when connecting through an HTTPS proxy: after
94+
* HTTP CONNECT is established over the proxy TLS, the destination TLS
95+
* handshake data must be sent through (and encrypted by) the proxy TLS.
96+
* Optional: may be NULL if the backend does not support it.
97+
*/
98+
int (*session_set_outer) (void *inner, void *outer);
9199

92100
/* I/O */
93101
int (*net_read) (struct flb_tls_session *, void *, size_t);

src/tls/flb_tls.c

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -617,13 +617,20 @@ int flb_tls_session_create(struct flb_tls *tls,
617617
vhost = NULL;
618618

619619
if (connection->type == FLB_UPSTREAM_CONNECTION) {
620-
if (connection->upstream->proxied_host != NULL) {
620+
if (tls->vhost != NULL) {
621+
/*
622+
* An explicit vhost in the TLS context takes priority. This
623+
* covers the HTTPS proxy case where the proxy TLS context has
624+
* its own vhost (= tcp_host) and must not fall through to
625+
* proxied_host which belongs to the inner destination.
626+
* Leave vhost as NULL so net_handshake() picks up tls->vhost.
627+
*/
628+
}
629+
else if (connection->upstream->proxied_host != NULL) {
621630
vhost = flb_rtrim(connection->upstream->proxied_host, '.');
622631
}
623632
else {
624-
if (tls->vhost == NULL) {
625-
vhost = flb_rtrim(connection->upstream->tcp_host, '.');
626-
}
633+
vhost = flb_rtrim(connection->upstream->tcp_host, '.');
627634
}
628635
}
629636

@@ -643,6 +650,40 @@ int flb_tls_session_create(struct flb_tls *tls,
643650
return -1;
644651
}
645652

653+
/*
654+
* If an existing TLS session is already active on this connection
655+
* (e.g. the proxy TLS session for an HTTPS proxy), chain the new
656+
* session's I/O through it. The inner (destination) TLS handshake
657+
* data must travel inside the outer (proxy) TLS tunnel rather than
658+
* going directly to the raw socket.
659+
*/
660+
if (connection->tls_session != NULL &&
661+
tls->api->session_set_outer != NULL) {
662+
result = tls->api->session_set_outer(session->ptr,
663+
connection->tls_session->ptr);
664+
if (result != 0) {
665+
flb_error("[tls] failed to chain TLS session over proxy tunnel for %s",
666+
flb_connection_get_remote_address(connection));
667+
668+
if (vhost != NULL) {
669+
flb_free(vhost);
670+
}
671+
672+
tls->api->session_destroy(session->ptr);
673+
flb_free(session);
674+
return -1;
675+
}
676+
677+
/*
678+
* The outer backend session ptr is now owned by the inner session
679+
* (via outer_session). Release the outer flb_tls_session wrapper
680+
* without going through flb_tls_session_destroy, which would free
681+
* the backend ptr we just transferred.
682+
*/
683+
flb_free(connection->tls_session);
684+
connection->tls_session = NULL;
685+
}
686+
646687
session->tls = tls;
647688
session->connection = connection;
648689

src/tls/openssl.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ struct tls_session {
7676
char alpn[FLB_TLS_ALPN_MAX_LENGTH];
7777
int continuation_flag;
7878
struct tls_context *parent; /* parent struct tls_context ref */
79+
struct tls_session *outer_session; /* outer TLS session for TLS-in-TLS (HTTPS proxy) */
7980
};
8081

8182
static int tls_init(void)
@@ -1279,10 +1280,40 @@ static void *tls_session_create(struct flb_tls *tls,
12791280
return session;
12801281
}
12811282

1283+
/*
1284+
* Chain inner TLS session I/O through an outer TLS session.
1285+
* Used for TLS-in-TLS when connecting through an HTTPS proxy: the inner
1286+
* (destination) SSL object's BIO is replaced with a BIO_f_ssl wrapper
1287+
* around the outer (proxy) SSL object, so all inner TLS bytes flow
1288+
* through the already-established outer TLS tunnel.
1289+
*/
1290+
static int tls_session_set_outer(void *inner_ptr, void *outer_ptr)
1291+
{
1292+
struct tls_session *inner = (struct tls_session *) inner_ptr;
1293+
struct tls_session *outer = (struct tls_session *) outer_ptr;
1294+
BIO *bio;
1295+
1296+
bio = BIO_new(BIO_f_ssl());
1297+
if (!bio) {
1298+
flb_error("[tls] could not create BIO for TLS-in-TLS tunnel");
1299+
return -1;
1300+
}
1301+
1302+
/*
1303+
* BIO_NOCLOSE: the outer SSL object must NOT be freed when this BIO
1304+
* is freed; we manage its lifecycle via inner->outer_session.
1305+
*/
1306+
BIO_set_ssl(bio, outer->ssl, BIO_NOCLOSE);
1307+
SSL_set_bio(inner->ssl, bio, bio);
1308+
inner->outer_session = outer;
1309+
return 0;
1310+
}
1311+
12821312
static int tls_session_destroy(void *session)
12831313
{
12841314
struct tls_session *ptr = session;
12851315
struct tls_context *ctx;
1316+
struct tls_context *outer_ctx;
12861317

12871318
if (!ptr) {
12881319
return 0;
@@ -1296,6 +1327,20 @@ static int tls_session_destroy(void *session)
12961327
}
12971328

12981329
SSL_free(ptr->ssl);
1330+
1331+
/*
1332+
* If this session was chained over an outer TLS session (HTTPS proxy),
1333+
* BIO_NOCLOSE ensured SSL_free above did not free the outer SSL object.
1334+
* Free it explicitly now, under its own context mutex.
1335+
*/
1336+
if (ptr->outer_session != NULL) {
1337+
outer_ctx = ptr->outer_session->parent;
1338+
pthread_mutex_lock(&outer_ctx->mutex);
1339+
SSL_free(ptr->outer_session->ssl);
1340+
flb_free(ptr->outer_session);
1341+
pthread_mutex_unlock(&outer_ctx->mutex);
1342+
}
1343+
12991344
flb_free(ptr);
13001345

13011346
pthread_mutex_unlock(&ctx->mutex);
@@ -1688,6 +1733,7 @@ static struct flb_tls_backend tls_openssl = {
16881733
.session_create = tls_session_create,
16891734
.session_invalidate = tls_session_invalidate,
16901735
.session_destroy = tls_session_destroy,
1736+
.session_set_outer = tls_session_set_outer,
16911737
.net_read = tls_net_read,
16921738
.net_write = tls_net_write,
16931739
.net_handshake = tls_net_handshake,

0 commit comments

Comments
 (0)