MEDIUM: config: set useful ALPN defaults for HTTPS and QUIC

This commit makes sure that if three is no "alpn", "npn" nor "no-alpn"
setting on a "bind" line which corresponds to an HTTPS or QUIC frontend,
we automatically turn on "h2,http/1.1" as an ALPN default for an HTTP
listener, and "h3" for a QUIC listener. This simplifies the configuration
for end users since they won't have to explicitly configure the ALPN
string to enable H2, considering that at the time of writing, HTTP/1.1
represents less than 7% of the traffic on large infrastructures. The
doc and regtests were updated. For more info, refer to the following
thread:

  https://www.mail-archive.com/haproxy@formilux.org/msg43410.html
This commit is contained in:
Willy Tarreau 2023-04-19 09:12:33 +02:00
parent de85de69ec
commit 5003ac7fe9
3 changed files with 58 additions and 15 deletions

View File

@ -4875,7 +4875,7 @@ bind /<path> [, ...] [param*]
bind "fd@${FD_APP1}"
listen h3_quic_proxy
bind quic4@10.0.0.1:8888 ssl crt /etc/mycrt alpn h3
bind quic4@10.0.0.1:8888 ssl crt /etc/mycrt
Note: regarding Linux's abstract namespace sockets, HAProxy uses the whole
sun_path length is used for the address length. Some other programs
@ -14606,21 +14606,30 @@ alpn <protocols>
delimited list of protocol names, for instance: "http/1.1,http/1.0" (without
quotes). This requires that the SSL library is built with support for TLS
extensions enabled (check with haproxy -vv). The ALPN extension replaces the
initial NPN extension. ALPN is required to enable HTTP/2 on an HTTP frontend.
Versions of OpenSSL prior to 1.0.2 didn't support ALPN and only supposed the
now obsolete NPN extension. At the time of writing this, most browsers still
support both ALPN and NPN for HTTP/2 so a fallback to NPN may still work for
a while. But ALPN must be used whenever possible. If both HTTP/2 and HTTP/1.1
are expected to be supported, both versions can be advertised, in order of
preference, like below :
initial NPN extension. At the protocol layer, ALPN is required to enable
HTTP/2 on an HTTPS frontend and HTTP/3 on a QUIC frontend. However, when such
frontends have none of "npn", "alpn" and "no-alpn" set, a default value of
"h2,http/1.1" will be used for a regular HTTPS frontend, and "h3" for a QUIC
frontend. Versions of OpenSSL prior to 1.0.2 didn't support ALPN and only
supposed the now obsolete NPN extension. At the time of writing this, most
browsers still support both ALPN and NPN for HTTP/2 so a fallback to NPN may
still work for a while. But ALPN must be used whenever possible. Protocols
not advertised are not negotiated. For example it is possible to only accept
HTTP/2 connections with this:
bind :443 ssl crt pub.pem alpn h2,http/1.1
bind :443 ssl crt pub.pem alpn h2 # explicitly disable HTTP/1.1
QUIC supports only h3 and hq-interop as ALPN. h3 is for HTTP/3 and hq-interop
is used for http/0.9 and QUIC interop runner (see https://interop.seemann.io).
Each "alpn" statement will replace a previous one. In order to remove them,
use "no-alpn".
Note that some old browsers such as Firefox 88 used to experience issues with
WebSocket over H2, and in case such a setup is encountered, it may be needed
to either explicitly disable HTTP/2 in the "alpn" string by forcing it to
"http/1.1" or "no-alpn", or to enable "h2-workaround-bogus-websocket-clients"
globally.
backlog <backlog>
Sets the socket's backlog to this value. If unspecified or 0, the frontend's
backlog is used instead, which generally defaults to the maxconn value.
@ -14828,6 +14837,12 @@ crt-list <file>
never match except if no other certificate matches. This way the first
declared certificate act as a fallback.
When no ALPN is set, the "bind" line's default one is used. If a "bind" line
has no "no-alpn", "alpn" nor "npn" set, a default value will be used
depending on the protocol (see "alpn" above). However if the "bind" line has
a different default, or explicitly disables ALPN using "no-alpn", it is
possible to force a specific value for a certificate.
crt-list file example:
cert1.pem !*
# comment
@ -15013,7 +15028,11 @@ no-alpn
Disables ALPN processing (technically speaking this sets the ALPN string to
an empty string that will not be advertised). It permits to cancel a previous
occurrence of an "alpn" setting and to disable application protocol
negotiation. See also "alpn".
negotiation. It may also be used to prevent a listener from negotiating ALPN
with a client on an HTTPS or QUIC listener; by default, HTTPS listeners will
advertise "h2,http/1.1" and QUIC listeners will advertise "h3". See also
"alpn" bove. Note that when using "crt-list", a certificate may override the
"alpn" setting and re-enable its processing.
no-ca-names
This setting is only available when support for OpenSSL was built in. It

View File

@ -117,7 +117,7 @@ client c1 -connect ${h1_clearfe_sock} {
txreq -url "/10"
rxresp
expect resp.status == 200
expect resp.http.x-alpn == "_"
expect resp.http.x-alpn == "_http/1.1"
expect resp.http.x-ver == "_1.1"
txreq -url "/11"
@ -150,8 +150,8 @@ client c1 -connect ${h1_clearfe_sock} {
txreq -url "/20"
rxresp
expect resp.status == 200
expect resp.http.x-alpn == "_"
expect resp.http.x-ver == "_1.1"
expect resp.http.x-alpn == "_h2"
expect resp.http.x-ver == "_2.0"
txreq -url "/21"
rxresp
@ -183,8 +183,8 @@ client c1 -connect ${h1_clearfe_sock} {
txreq -url "/30"
rxresp
expect resp.status == 200
expect resp.http.x-alpn == "_"
expect resp.http.x-ver == "_1.1"
expect resp.http.x-alpn == "_h2"
expect resp.http.x-ver == "_2.0"
txreq -url "/31"
rxresp

View File

@ -2937,6 +2937,30 @@ int check_config_validity()
free(bind_conf->ssl_conf.alpn_str);
bind_conf->ssl_conf.alpn_str = NULL;
}
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
else if (!bind_conf->ssl_conf.alpn_str && !bind_conf->ssl_conf.npn_str &&
((bind_conf->options & BC_O_USE_SSL) || bind_conf->xprt == xprt_get(XPRT_QUIC)) &&
curproxy->mode == PR_MODE_HTTP && global.tune.bufsize >= 16384) {
/* Neither ALPN nor NPN were explicitly set nor disabled, we're
* in HTTP mode with an SSL or QUIC listener, we can enable ALPN.
* Note that it's in binary form.
*/
if (bind_conf->xprt == xprt_get(XPRT_QUIC))
bind_conf->ssl_conf.alpn_str = strdup("\002h3");
else
bind_conf->ssl_conf.alpn_str = strdup("\002h2\010http/1.1");
if (!bind_conf->ssl_conf.alpn_str) {
ha_alert("Proxy '%s': out of memory while trying to allocate a default alpn string in 'bind %s' at [%s:%d].\n",
curproxy->id, bind_conf->arg, bind_conf->file, bind_conf->line);
cfgerr++;
err_code |= ERR_FATAL | ERR_ALERT;
goto out;
}
bind_conf->ssl_conf.alpn_len = strlen(bind_conf->ssl_conf.alpn_str);
}
#endif
if (curproxy->mode == PR_MODE_HTTP && global.tune.bufsize < 16384) {
#ifdef OPENSSL_NPN_NEGOTIATED