From: Remi Tricot-Le Breton Date: Tue, 20 Oct 2026 14:16:40 +0000 (+0200) Subject: MINOR: ssl: Add new 'pha' server option X-Git-Tag: v3.5-dev10~91 X-Git-Url: https://git.kaiwu.me/$%7BGITURL%7D$%7Bcid%7D?a=commitdiff_plain;h=d66d5d94fce5b2cc9294a2bbc9e79579fd94b5ce;p=haproxy.git MINOR: ssl: Add new 'pha' server option This backend option allows post handshake authentication for the given backend. This simply consists in adding a "post_handshake_auth" extension in the ClientHello that tells the server that PHA is supported. We only enable it for backends that have an explicit certificate provided so that this certificate gets always used in subsequent CertificateRequest messages sent by the server. This does not work with AWS-LC (PHA unsupported) or WolfSSL which would support it but only when WolfSSL is built with WOLFSSL_POST_HANDSHAKE_AUTH defined (disabled by default). --- diff --git a/doc/configuration.txt b/doc/configuration.txt index a2f0e56fd..20ef09f42 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -19858,6 +19858,15 @@ no-check-ssl It may also be used as "default-server" setting to reset any previous "default-server" "check-ssl" setting. +no-pha + May be used in the following contexts: tcp, http, log, peers, ring + + This option may be used as "server" setting to reset any "pha" setting + which would have been inherited from "default-server" directive as default + value. + It may also be used as "default-server" setting to reset any previous + "default-server" "pha" setting. + no-renegotiate May be used in the following contexts: tcp, http, log @@ -20105,6 +20114,31 @@ on-marked-up Actions are disabled by default +pha + May be used in the following contexts: tcp, http, log, peers, ring + + This setting is only available when support for OpenSSL was built in. It is + not supported by AWS-LC nor by WolfSSL. It enables post handshake + authentication (RFC 8446) for the given backend. It is a TLS1.3 feature that + consists in advertising the "post_handshake_auth" extension in the + ClientHello sent to the server, telling it that post handshake authentication + is supported. The certificate provided via the server's "crt" parameter is + then used to answer the "CertificateRequest" messages sent by the server + after the handshake completes, so this option requires that a certificate be + explicitly provided on the server line. + Considering the limited support of this option, it is advised to enable it + only when strictly necessary (to communicate with Windows Server 2022 IIS web + site for instance) + + Post handshake authentication is not supported by HTTP/2 nor HTTP/3, so a + warning will be raised if an alpn containing other protocols than "http/1.1" + is specified, or if a "proto" option different than "h1" is used. + This warning also applies to QUIC servers using the default "h3" ALPN. + + This option is disabled by default. + + See also "no-pha", "crt", "alpn" and "proto". + pool-conn-name May be used in the following contexts: http diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h index 0c6eda227..40bfa910e 100644 --- a/include/haproxy/server-t.h +++ b/include/haproxy/server-t.h @@ -206,7 +206,8 @@ enum srv_init_state { /* server ssl options */ #define SRV_SSL_O_NONE 0x0000 -#define SRV_SSL_O_NO_AUTO_SNI 0x0001 /* disable automatic SNI */ +#define SRV_SSL_O_NO_AUTO_SNI 0x0001 /* disable automatic SNI */ +#define SRV_SSL_O_PHA 0x0002 /* Post Handshake Authentication */ #define SRV_SSL_O_NO_TLS_TICKETS 0x0100 /* disable session resumption tickets */ #define SRV_SSL_O_NO_REUSE 0x200 /* disable session reuse */ #define SRV_SSL_O_EARLY_DATA 0x400 /* Allow using early data */ diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c index 961794515..d35e4ac52 100644 --- a/src/cfgparse-ssl.c +++ b/src/cfgparse-ssl.c @@ -2148,6 +2148,24 @@ static int srv_parse_no_tls_tickets(char **args, int *cur_arg, struct proxy *px, newsrv->ssl_ctx.options |= SRV_SSL_O_NO_TLS_TICKETS; return 0; } + +/* parse the "pha" server keyword */ +static int srv_parse_pha(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err) +{ +#if !defined(OPENSSL_IS_AWSLC) && !defined(USE_OPENSSL_WOLFSSL) + if (*args[*cur_arg] == 'n') { + /* "no-pha" */ + newsrv->ssl_ctx.options &= ~SRV_SSL_O_PHA; + } else { + /* "pha" */ + newsrv->ssl_ctx.options |= SRV_SSL_O_PHA; + } + return 0; +#else + memprintf(err, "'%s' : library does not support post handshake authentication", args[*cur_arg]); + return ERR_ALERT | ERR_FATAL; +#endif +} /* parse the "send-proxy-v2-ssl" server keyword */ static int srv_parse_send_proxy_ssl(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err) { @@ -2817,6 +2835,7 @@ static struct srv_kw_list srv_kws = { "SSL", { }, { { "ktls", srv_parse_ktls, 1, 1, 1 }, /* enable or disable kTLS */ { "no-check-sni-auto", srv_parse_no_check_sni_auto, 0, 1, 1 }, /* disable automatic SNI selection for health checks */ { "no-check-ssl", srv_parse_no_check_ssl, 0, 1, 0 }, /* disable SSL for health checks */ + { "no-pha", srv_parse_pha, 0, 1, 1 }, /* Disable Post Handshake Authentication */ { "no-renegotiate", srv_parse_renegotiate, 0, 1, 1 }, /* Disable renegotiation */ { "no-send-proxy-v2-ssl", srv_parse_no_send_proxy_ssl, 0, 1, 0 }, /* do not send PROXY protocol header v2 with SSL info */ { "no-send-proxy-v2-ssl-cn", srv_parse_no_send_proxy_cn, 0, 1, 0 }, /* do not send PROXY protocol header v2 with CN */ @@ -2830,6 +2849,7 @@ static struct srv_kw_list srv_kws = { "SSL", { }, { { "no-tlsv13", srv_parse_tls_method_options, 0, 0, 1 }, /* disable TLSv13 */ { "no-tls-tickets", srv_parse_no_tls_tickets, 0, 1, 1 }, /* disable session resumption tickets */ { "npn", srv_parse_npn, 1, 1, 1 }, /* Set NPN supported protocols */ + { "pha", srv_parse_pha, 0, 1, 1 }, /* Allow Post Handshake Authentication */ { "renegotiate", srv_parse_renegotiate, 0, 1, 1 }, /* Allow secure renegotiation */ { "send-proxy-v2-ssl", srv_parse_send_proxy_ssl, 0, 1, 1 }, /* send PROXY protocol header v2 with SSL info */ { "send-proxy-v2-ssl-cn", srv_parse_send_proxy_cn, 0, 1, 1 }, /* send PROXY protocol header v2 with CN */ diff --git a/src/server.c b/src/server.c index 76b0d9bfa..4b5d2a201 100644 --- a/src/server.c +++ b/src/server.c @@ -4191,6 +4191,7 @@ static int _srv_parse_finalize(char **args, int cur_arg, int parse_flags) { int ret; + int retval = 0; char *errmsg = NULL; struct srv_pp_tlv_list *srv_tlv = NULL; @@ -4281,6 +4282,20 @@ static int _srv_parse_finalize(char **args, int cur_arg, } } + if (srv->use_ssl && (srv->ssl_ctx.options & SRV_SSL_O_PHA)) { + if (srv->ssl_ctx.alpn_str && + strcmp(srv->ssl_ctx.alpn_str, "\010http/1.1") != 0) { + ha_warning("Post Handshake Authentication can only be used " + "with 'http/1.1' protocol (wrong 'alpn' value).\n"); + retval |= ERR_WARN; + } else if (srv->mux_proto && + !isteq(srv->mux_proto->mux_proto, ist("h1"))) { + ha_warning("Post Handshake Authentication can only be used " + "with 'http/1.1' protocol (wrong 'proto' value).\n"); + retval |= ERR_WARN; + } + } + if (!(srv->proxy->cap & PR_CAP_LB)) { /* No need to wait for effective proxy mode, it is already known: * Only general purpose user-declared proxies ("listen", "frontend", "backend") @@ -4292,7 +4307,7 @@ static int _srv_parse_finalize(char **args, int cur_arg, srv_lb_commit_status(srv); - return 0; + return retval; } int parse_server(const char *file, int linenum, char **args, diff --git a/src/ssl_sock.c b/src/ssl_sock.c index f34ea6ddb..08968b3ae 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -5722,6 +5722,16 @@ static int ssl_sock_prepare_srv_ssl_ctx(const struct server *srv, SSL_CTX *ctx) #endif #endif /* defined(SSL_CTX_set1_curves_list) */ +#if !defined(OPENSSL_IS_AWSLC) && !defined(USE_OPENSSL_WOLFSSL) + if (srv->ssl_ctx.options & SRV_SSL_O_PHA) { + if (!srv->ssl_ctx.client_crt) { + ha_alert("Proxy '%s': 'pha' option requires a 'crt' parameter.\n", curproxy->id); + cfgerr++; + } else + SSL_CTX_set_post_handshake_auth(ctx, 1); + } +#endif + return cfgerr; }