]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: ssl/ech: add logging and sample fetches for ECH status and outer SNI
authorsftcd <stephen.farrell@cs.tcd.ie>
Fri, 26 Sep 2025 21:17:13 +0000 (22:17 +0100)
committerWilliam Lallemand <wlallemand@haproxy.com>
Thu, 30 Oct 2025 09:37:30 +0000 (10:37 +0100)
This patch adds functions to expose Encrypted Client Hello (ECH) status
and outer SNI information for logging and sample fetching.

Two new helper functions are introduced in ech.c:
 - conn_get_ech_status() places the ECH processing status string into a
   buffer.
 - conn_get_ech_outer_sni() retrieves the outer SNI value if ECH
   succeeded.

Two new sample fetch keywords are added:
 - "ssl_fc_ech_status" returns the ECH status string.
 - "ssl_fc_ech_outer_sni" returns the outer SNI value seen during ECH.

These allow ECH information to be used in HAProxy logs, ACLs, and
captures.

include/haproxy/ech.h
src/ech.c
src/ssl_sample.c

index dac74cfba817c6ae44098c1e06b368e32d702d19..0772db9b523407fb4202fa0ee7036a13cf4f184f 100644 (file)
@@ -6,6 +6,8 @@
 #include <openssl/ech.h>
 
 int load_echkeys(SSL_CTX *ctx, char *dirname, int *loaded);
+int conn_get_ech_status(struct connection *conn, struct buffer *buf);
+int conn_get_ech_outer_sni(struct connection *conn, struct buffer *buf);
 
 # endif /* USE_ECH */
 #endif /* _HAPROXY_ECH_H */
index 69b522c3a80f52f616bbc5d7b0968e01eebca764..786a4e96d40e1625d3bb46fd5118515adadf563c 100644 (file)
--- a/src/ech.c
+++ b/src/ech.c
@@ -74,6 +74,58 @@ end:
        return rv;
 }
 
+/*
+ * Place an ECH status string into a trash buffer
+ * ECH status string examples:
+ *      SSL_ECH_STATUS_GREASE
+ *      SSL_ECH_STATUS_NOT_TRIED
+ *      SSL_ECH_STATUS_SUCCESS
+ * The status values are those defined in <openssl/ech.h>
+ * as the define'd returns from `SSL_ech_get1_status()`
+ */
+int conn_get_ech_status(struct connection *conn, struct buffer *buf)
+{
+       struct ssl_sock_ctx *ctx = conn_get_ssl_sock_ctx(conn);
+       char *sni_ech = NULL;
+       char *sni_clr = NULL;
+       const char *lstr = NULL;
+
+       if (!ctx)
+               return 0;
+#define s(x) #x
+       switch (SSL_ech_get1_status(ctx->ssl, &sni_ech, &sni_clr)) {
+               case SSL_ECH_STATUS_SUCCESS:   lstr = s(SSL_ECH_STATUS_SUCCESS);   break;
+               case SSL_ECH_STATUS_NOT_TRIED: lstr = s(SSL_ECH_STATUS_NOT_TRIED); break;
+               case SSL_ECH_STATUS_FAILED:    lstr = s(SSL_ECH_STATUS_FAILED);    break;
+               case SSL_ECH_STATUS_BAD_NAME:  lstr = s(SSL_ECH_STATUS_BAD_NAME);  break;
+               case SSL_ECH_STATUS_BAD_CALL:  lstr = s(SSL_ECH_STATUS_BAD_CALL);  break;
+               case SSL_ECH_STATUS_GREASE:    lstr = s(SSL_ECH_STATUS_GREASE);    break;
+               case SSL_ECH_STATUS_BACKEND:   lstr = s(SSL_ECH_STATUS_BACKEND);   break;
+               default:                       lstr = "";                         break;
+       }
+#undef s
+       chunk_printf(buf, "%s", lstr);
+       OPENSSL_free(sni_ech);
+       OPENSSL_free(sni_clr);
+       return 1;
+}
+
+/* If ECH succeeded, return the outer SNI value seen */
+int conn_get_ech_outer_sni(struct connection *conn, struct buffer *buf)
+{
+       struct ssl_sock_ctx *ctx = conn_get_ssl_sock_ctx(conn);
+       char *sni_ech = NULL;
+       char *sni_clr = NULL;
+
+       if (!ctx)
+               return 0;
+       if (SSL_ech_get1_status(ctx->ssl, &sni_ech, &sni_clr)
+           == SSL_ECH_STATUS_SUCCESS && sni_clr != NULL)
+               chunk_printf(buf, "%s", sni_clr);
+       OPENSSL_free(sni_ech);
+       OPENSSL_free(sni_clr);
+       return 1;
+}
 
 static int bind_parse_ech(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
@@ -97,4 +149,5 @@ static struct bind_kw_list bind_kws = { "SSL", { }, {
 
 
 INITCALL1(STG_REGISTER, bind_register_keywords, &bind_kws);
+
 #endif
index 4746b8c0c2844ea35945166c4db5dfff3576fde2..4cf1c6d1d80b6c422090d4cfe1051430c2aa02cb 100644 (file)
@@ -33,6 +33,9 @@
 #include <haproxy/stconn.h>
 #include <haproxy/tools.h>
 #include <haproxy/vars.h>
+#ifdef USE_ECH
+#include <haproxy/ech.h>
+#endif
 
 
 /***** Below are some sample fetching functions for ACL/patterns *****/
@@ -1880,6 +1883,48 @@ smp_fetch_ssl_fc_sni(const struct arg *args, struct sample *smp, const char *kw,
 #endif
 }
 
+#ifdef USE_ECH
+static int
+smp_fetch_ssl_fc_ech_status(const struct arg *args, struct sample *smp,
+                            const char *kw, void *private)
+{
+       struct buffer *smp_trash;
+       struct connection *conn;
+
+       smp->flags = SMP_F_VOL_SESS | SMP_F_CONST;
+       smp->data.type = SMP_T_STR;
+       conn = objt_conn(smp->sess->origin);
+       if (!conn)
+               return 0;
+       smp_trash = get_trash_chunk();
+       if (conn_get_ech_status(conn, smp_trash) == 1) {
+               smp->data.u.str.area = smp_trash->area;
+               smp->data.u.str.data = smp_trash->data;
+       }
+       return 1;
+}
+
+static int
+smp_fetch_ssl_fc_ech_outer_sni(const struct arg *args, struct sample *smp,
+                               const char *kw, void *private)
+{
+       struct buffer *smp_trash;
+       struct connection *conn;
+
+       smp->flags = SMP_F_VOL_SESS | SMP_F_CONST;
+       smp->data.type = SMP_T_STR;
+       conn = objt_conn(smp->sess->origin);
+       if (!conn)
+               return 0;
+       smp_trash = get_trash_chunk();
+       if (conn_get_ech_outer_sni(conn, smp_trash) == 1) {
+               smp->data.u.str.area = smp_trash->area;
+               smp->data.u.str.data = smp_trash->data;
+       }
+       return 1;
+}
+#endif
+
 /* binary, returns tls client hello cipher list.
  * Arguments: filter_option (0,1)
  */
@@ -2572,6 +2617,10 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
 #endif
 
        { "ssl_fc_sni",             smp_fetch_ssl_fc_sni,         0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+#ifdef USE_ECH
+       { "ssl_fc_ech_status",      smp_fetch_ssl_fc_ech_status,  0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+       { "ssl_fc_ech_outer_sni",   smp_fetch_ssl_fc_ech_outer_sni, 0,                 NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+#endif
        { "ssl_fc_cipherlist_bin",  smp_fetch_ssl_fc_cl_bin,      ARG1(0,SINT),        NULL,    SMP_T_STR,  SMP_USE_L5CLI },
        { "ssl_fc_cipherlist_hex",  smp_fetch_ssl_fc_cl_hex,      ARG1(0,SINT),        NULL,    SMP_T_BIN,  SMP_USE_L5CLI },
        { "ssl_fc_cipherlist_str",  smp_fetch_ssl_fc_cl_str,      ARG1(0,SINT),        NULL,    SMP_T_STR,  SMP_USE_L5CLI },