mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-05-13 13:24:50 +00:00
MINOR: server: Add "alpn" and "npn" keywords.
Add new keywords to "server" lines, alpn and npn. If set, when connecting through SSL, those alpn/npn will be negociated during the SSL handshake.
This commit is contained in:
parent
beb859abce
commit
c756600103
@ -11513,6 +11513,20 @@ agent-port <port>
|
|||||||
|
|
||||||
See also the "agent-check" and "agent-inter" parameters.
|
See also the "agent-check" and "agent-inter" parameters.
|
||||||
|
|
||||||
|
alpn <protocols>
|
||||||
|
This enables the TLS ALPN extension and advertises the specified protocol
|
||||||
|
list as supported on top of ALPN. The protocol list consists in a comma-
|
||||||
|
delimited list of protocol names, for instance: "http/1.1,http/1.0" (without
|
||||||
|
quotes). This requires that the SSL library is build with support for TLS
|
||||||
|
extensions enabled (check with haproxy -vv). The ALPN extension replaces the
|
||||||
|
initial NPN extension. ALPN is required to connect to HTTP/2 servers.
|
||||||
|
Versions of OpenSSL prior to 1.0.2 didn't support ALPN and only supposed the
|
||||||
|
now obsolete NPN extension.
|
||||||
|
If both HTTP/2 and HTTP/1.1 are expected to be supported, both versions can
|
||||||
|
be advertised, in order of preference, like below :
|
||||||
|
|
||||||
|
server 127.0.0.1:443 ssl crt pub.pem alpn h2,http/1.1
|
||||||
|
|
||||||
backup
|
backup
|
||||||
When "backup" is present on a server line, the server is only used in load
|
When "backup" is present on a server line, the server is only used in load
|
||||||
balancing when all other non-backup servers are unavailable. Requests coming
|
balancing when all other non-backup servers are unavailable. Requests coming
|
||||||
@ -11890,6 +11904,15 @@ non-stick
|
|||||||
This may be used in conjunction with backup to ensure that
|
This may be used in conjunction with backup to ensure that
|
||||||
stick-table persistence is disabled for backup servers.
|
stick-table persistence is disabled for backup servers.
|
||||||
|
|
||||||
|
npn <protocols>
|
||||||
|
This enables the NPN TLS extension and advertises the specified protocol list
|
||||||
|
as supported on top of NPN. The protocol list consists in a comma-delimited
|
||||||
|
list of protocol names, for instance: "http/1.1,http/1.0" (without quotes).
|
||||||
|
This requires that the SSL library is build with support for TLS extensions
|
||||||
|
enabled (check with haproxy -vv). Note that the NPN extension has been
|
||||||
|
replaced with the ALPN extension (see the "alpn" keyword), though this one is
|
||||||
|
only available starting with OpenSSL 1.0.2.
|
||||||
|
|
||||||
observe <mode>
|
observe <mode>
|
||||||
This option enables health adjusting based on observing communication with
|
This option enables health adjusting based on observing communication with
|
||||||
the server. By default this functionality is disabled and enabling it also
|
the server. By default this functionality is disabled and enabling it also
|
||||||
|
@ -299,6 +299,14 @@ struct server {
|
|||||||
char *crl_file; /* CRLfile to use on verify */
|
char *crl_file; /* CRLfile to use on verify */
|
||||||
char *client_crt; /* client certificate to send */
|
char *client_crt; /* client certificate to send */
|
||||||
struct sample_expr *sni; /* sample expression for SNI */
|
struct sample_expr *sni; /* sample expression for SNI */
|
||||||
|
#ifdef OPENSSL_NPN_NEGOTIATED
|
||||||
|
char *npn_str; /* NPN protocol string */
|
||||||
|
int npn_len; /* NPN protocol string length */
|
||||||
|
#endif
|
||||||
|
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
||||||
|
char *alpn_str; /* ALPN protocol string */
|
||||||
|
int alpn_len; /* ALPN protocol string length */
|
||||||
|
#endif
|
||||||
} ssl_ctx;
|
} ssl_ctx;
|
||||||
#endif
|
#endif
|
||||||
struct dns_srvrq *srvrq; /* Pointer representing the DNS SRV requeest, if any */
|
struct dns_srvrq *srvrq; /* Pointer representing the DNS SRV requeest, if any */
|
||||||
|
21
src/server.c
21
src/server.c
@ -1486,6 +1486,27 @@ static void srv_ssl_settings_cpy(struct server *srv, struct server *src)
|
|||||||
#endif
|
#endif
|
||||||
if (src->sni_expr != NULL)
|
if (src->sni_expr != NULL)
|
||||||
srv->sni_expr = strdup(src->sni_expr);
|
srv->sni_expr = strdup(src->sni_expr);
|
||||||
|
|
||||||
|
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
||||||
|
if (src->ssl_ctx.alpn_str) {
|
||||||
|
srv->ssl_ctx.alpn_str = malloc(src->ssl_ctx.alpn_len);
|
||||||
|
if (srv->ssl_ctx.alpn_str) {
|
||||||
|
memcpy(srv->ssl_ctx.alpn_str, src->ssl_ctx.alpn_str,
|
||||||
|
src->ssl_ctx.alpn_len);
|
||||||
|
srv->ssl_ctx.alpn_len = src->ssl_ctx.alpn_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef OPENSSL_NPN_NEGOTIATED
|
||||||
|
if (src->ssl_ctx.npn_str) {
|
||||||
|
srv->ssl_ctx.npn_str = malloc(src->ssl_ctx.npn_len);
|
||||||
|
if (srv->ssl_ctx.npn_str) {
|
||||||
|
memcpy(srv->ssl_ctx.npn_str, src->ssl_ctx.npn_str,
|
||||||
|
src->ssl_ctx.npn_len);
|
||||||
|
srv->ssl_ctx.npn_len = src->ssl_ctx.npn_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
139
src/ssl_sock.c
139
src/ssl_sock.c
@ -1634,6 +1634,20 @@ void ssl_sock_msgcbk(int write_p, int version, int content_type, const void *buf
|
|||||||
ssl_sock_parse_clienthello(write_p, version, content_type, buf, len, ssl);
|
ssl_sock_parse_clienthello(write_p, version, content_type, buf, len, ssl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
|
||||||
|
static int ssl_sock_srv_select_protos(SSL *s, unsigned char **out, unsigned char *outlen,
|
||||||
|
const unsigned char *in, unsigned int inlen,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
struct server *srv = arg;
|
||||||
|
|
||||||
|
if (SSL_select_next_proto(out, outlen, in, inlen, (unsigned char *)srv->ssl_ctx.npn_str,
|
||||||
|
srv->ssl_ctx.npn_len) == OPENSSL_NPN_NEGOTIATED)
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
return SSL_TLSEXT_ERR_NOACK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
|
#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
|
||||||
/* This callback is used so that the server advertises the list of
|
/* This callback is used so that the server advertises the list of
|
||||||
* negociable protocols for NPN.
|
* negociable protocols for NPN.
|
||||||
@ -4701,6 +4715,15 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
|
|||||||
cfgerr++;
|
cfgerr++;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
|
||||||
|
if (srv->ssl_ctx.npn_str)
|
||||||
|
SSL_CTX_set_next_proto_select_cb(ctx, ssl_sock_srv_select_protos, srv);
|
||||||
|
#endif
|
||||||
|
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
||||||
|
if (srv->ssl_ctx.alpn_str)
|
||||||
|
SSL_CTX_set_alpn_protos(ctx, (unsigned char *)srv->ssl_ctx.alpn_str, srv->ssl_ctx.alpn_len);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
return cfgerr;
|
return cfgerr;
|
||||||
}
|
}
|
||||||
@ -4815,8 +4838,16 @@ int ssl_sock_prepare_bind_conf(struct bind_conf *bind_conf)
|
|||||||
/* release ssl context allocated for servers. */
|
/* release ssl context allocated for servers. */
|
||||||
void ssl_sock_free_srv_ctx(struct server *srv)
|
void ssl_sock_free_srv_ctx(struct server *srv)
|
||||||
{
|
{
|
||||||
|
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
||||||
|
if (srv->ssl_ctx.alpn_str)
|
||||||
|
free(srv->ssl_ctx.alpn_str);
|
||||||
|
#endif
|
||||||
|
if (srv->ssl_ctx.npn_str)
|
||||||
|
free(srv->ssl_ctx.npn_str);
|
||||||
|
#ifdef OPENSSL_NPN_NEGOTIATED
|
||||||
if (srv->ssl_ctx.ctx)
|
if (srv->ssl_ctx.ctx)
|
||||||
SSL_CTX_free(srv->ssl_ctx.ctx);
|
SSL_CTX_free(srv->ssl_ctx.ctx);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Walks down the two trees in bind_conf and frees all the certs. The pointer may
|
/* Walks down the two trees in bind_conf and frees all the certs. The pointer may
|
||||||
@ -7853,6 +7884,112 @@ static int bind_parse_no_ca_names(char **args, int cur_arg, struct proxy *px, st
|
|||||||
|
|
||||||
/************** "server" keywords ****************/
|
/************** "server" keywords ****************/
|
||||||
|
|
||||||
|
/* parse the "npn" bind keyword */
|
||||||
|
static int srv_parse_npn(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
|
||||||
|
{
|
||||||
|
#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
|
||||||
|
char *p1, *p2;
|
||||||
|
|
||||||
|
if (!*args[*cur_arg + 1]) {
|
||||||
|
memprintf(err, "'%s' : missing the comma-delimited NPN protocol suite", args[*cur_arg]);
|
||||||
|
return ERR_ALERT | ERR_FATAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(newsrv->ssl_ctx.npn_str);
|
||||||
|
|
||||||
|
/* the NPN string is built as a suite of (<len> <name>)*,
|
||||||
|
* so we reuse each comma to store the next <len> and need
|
||||||
|
* one more for the end of the string.
|
||||||
|
*/
|
||||||
|
newsrv->ssl_ctx.npn_len = strlen(args[*cur_arg + 1]) + 1;
|
||||||
|
newsrv->ssl_ctx.npn_str = calloc(1, newsrv->ssl_ctx.npn_len + 1);
|
||||||
|
memcpy(newsrv->ssl_ctx.npn_str + 1, args[*cur_arg + 1],
|
||||||
|
newsrv->ssl_ctx.npn_len);
|
||||||
|
|
||||||
|
/* replace commas with the name length */
|
||||||
|
p1 = newsrv->ssl_ctx.npn_str;
|
||||||
|
p2 = p1 + 1;
|
||||||
|
while (1) {
|
||||||
|
p2 = memchr(p1 + 1, ',', newsrv->ssl_ctx.npn_str +
|
||||||
|
newsrv->ssl_ctx.npn_len - (p1 + 1));
|
||||||
|
if (!p2)
|
||||||
|
p2 = p1 + 1 + strlen(p1 + 1);
|
||||||
|
|
||||||
|
if (p2 - (p1 + 1) > 255) {
|
||||||
|
*p2 = '\0';
|
||||||
|
memprintf(err, "'%s' : NPN protocol name too long : '%s'", args[*cur_arg], p1 + 1);
|
||||||
|
return ERR_ALERT | ERR_FATAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p1 = p2 - (p1 + 1);
|
||||||
|
p1 = p2;
|
||||||
|
|
||||||
|
if (!*p2)
|
||||||
|
break;
|
||||||
|
|
||||||
|
*(p2++) = '\0';
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
if (err)
|
||||||
|
memprintf(err, "'%s' : library does not support TLS NPN extension", args[*cur_arg]);
|
||||||
|
return ERR_ALERT | ERR_FATAL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse the "alpn" bind keyword */
|
||||||
|
static int srv_parse_alpn(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
|
||||||
|
{
|
||||||
|
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
||||||
|
char *p1, *p2;
|
||||||
|
|
||||||
|
if (!*args[*cur_arg + 1]) {
|
||||||
|
memprintf(err, "'%s' : missing the comma-delimited ALPN protocol suite", args[*cur_arg]);
|
||||||
|
return ERR_ALERT | ERR_FATAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(newsrv->ssl_ctx.alpn_str);
|
||||||
|
|
||||||
|
/* the ALPN string is built as a suite of (<len> <name>)*,
|
||||||
|
* so we reuse each comma to store the next <len> and need
|
||||||
|
* one more for the end of the string.
|
||||||
|
*/
|
||||||
|
newsrv->ssl_ctx.alpn_len = strlen(args[*cur_arg + 1]) + 1;
|
||||||
|
newsrv->ssl_ctx.alpn_str = calloc(1, newsrv->ssl_ctx.alpn_len + 1);
|
||||||
|
memcpy(newsrv->ssl_ctx.alpn_str + 1, args[*cur_arg + 1],
|
||||||
|
newsrv->ssl_ctx.alpn_len);
|
||||||
|
|
||||||
|
/* replace commas with the name length */
|
||||||
|
p1 = newsrv->ssl_ctx.alpn_str;
|
||||||
|
p2 = p1 + 1;
|
||||||
|
while (1) {
|
||||||
|
p2 = memchr(p1 + 1, ',', newsrv->ssl_ctx.alpn_str +
|
||||||
|
newsrv->ssl_ctx.alpn_len - (p1 + 1));
|
||||||
|
if (!p2)
|
||||||
|
p2 = p1 + 1 + strlen(p1 + 1);
|
||||||
|
|
||||||
|
if (p2 - (p1 + 1) > 255) {
|
||||||
|
*p2 = '\0';
|
||||||
|
memprintf(err, "'%s' : ALPN protocol name too long : '%s'", args[*cur_arg], p1 + 1);
|
||||||
|
return ERR_ALERT | ERR_FATAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p1 = p2 - (p1 + 1);
|
||||||
|
p1 = p2;
|
||||||
|
|
||||||
|
if (!*p2)
|
||||||
|
break;
|
||||||
|
|
||||||
|
*(p2++) = '\0';
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
if (err)
|
||||||
|
memprintf(err, "'%s' : library does not support TLS ALPN extension", args[*cur_arg]);
|
||||||
|
return ERR_ALERT | ERR_FATAL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* parse the "ca-file" server keyword */
|
/* parse the "ca-file" server keyword */
|
||||||
static int srv_parse_ca_file(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
|
static int srv_parse_ca_file(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
|
||||||
{
|
{
|
||||||
@ -8949,6 +9086,7 @@ static struct bind_kw_list bind_kws = { "SSL", { }, {
|
|||||||
*/
|
*/
|
||||||
static struct srv_kw_list srv_kws = { "SSL", { }, {
|
static struct srv_kw_list srv_kws = { "SSL", { }, {
|
||||||
{ "allow-0rtt", srv_parse_allow_0rtt, 0, 1 }, /* Allow using early data on this server */
|
{ "allow-0rtt", srv_parse_allow_0rtt, 0, 1 }, /* Allow using early data on this server */
|
||||||
|
{ "alpn", srv_parse_alpn, 1, 1 }, /* Set ALPN supported protocols */
|
||||||
{ "ca-file", srv_parse_ca_file, 1, 1 }, /* set CAfile to process verify server cert */
|
{ "ca-file", srv_parse_ca_file, 1, 1 }, /* set CAfile to process verify server cert */
|
||||||
{ "check-sni", srv_parse_check_sni, 1, 1 }, /* set SNI */
|
{ "check-sni", srv_parse_check_sni, 1, 1 }, /* set SNI */
|
||||||
{ "check-ssl", srv_parse_check_ssl, 0, 1 }, /* enable SSL for health checks */
|
{ "check-ssl", srv_parse_check_ssl, 0, 1 }, /* enable SSL for health checks */
|
||||||
@ -8974,6 +9112,7 @@ static struct srv_kw_list srv_kws = { "SSL", { }, {
|
|||||||
{ "no-tlsv12", srv_parse_tls_method_options, 0, 0 }, /* disable TLSv12 */
|
{ "no-tlsv12", srv_parse_tls_method_options, 0, 0 }, /* disable TLSv12 */
|
||||||
{ "no-tlsv13", srv_parse_tls_method_options, 0, 0 }, /* disable TLSv13 */
|
{ "no-tlsv13", srv_parse_tls_method_options, 0, 0 }, /* disable TLSv13 */
|
||||||
{ "no-tls-tickets", srv_parse_no_tls_tickets, 0, 1 }, /* disable session resumption tickets */
|
{ "no-tls-tickets", srv_parse_no_tls_tickets, 0, 1 }, /* disable session resumption tickets */
|
||||||
|
{ "npn", srv_parse_npn, 1, 1 }, /* Set NPN supported protocols */
|
||||||
{ "send-proxy-v2-ssl", srv_parse_send_proxy_ssl, 0, 1 }, /* send PROXY protocol header v2 with SSL info */
|
{ "send-proxy-v2-ssl", srv_parse_send_proxy_ssl, 0, 1 }, /* send PROXY protocol header v2 with SSL info */
|
||||||
{ "send-proxy-v2-ssl-cn", srv_parse_send_proxy_cn, 0, 1 }, /* send PROXY protocol header v2 with CN */
|
{ "send-proxy-v2-ssl-cn", srv_parse_send_proxy_cn, 0, 1 }, /* send PROXY protocol header v2 with CN */
|
||||||
{ "sni", srv_parse_sni, 1, 1 }, /* send SNI extension */
|
{ "sni", srv_parse_sni, 1, 1 }, /* send SNI extension */
|
||||||
|
Loading…
Reference in New Issue
Block a user