MINOR: ssl: add support for the "alpn" bind keyword
The ALPN extension is meant to replace the now deprecated NPN extension. This patch implements support for it. It requires a version of openssl with support for this extension. Patches are available here right now : http://html5labs.interopbridges.com/media/167447/alpn_patches.zip
This commit is contained in:
parent
a4312fa28e
commit
ab861d3856
|
@ -7141,6 +7141,14 @@ accept-proxy
|
||||||
X-Forwarded-For mechanism which is not always reliable and not even always
|
X-Forwarded-For mechanism which is not always reliable and not even always
|
||||||
usable.
|
usable.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
backlog <backlog>
|
backlog <backlog>
|
||||||
Sets the socket's backlog to this value. If unspecified, the frontend's
|
Sets the socket's backlog to this value. If unspecified, the frontend's
|
||||||
backlog is used instead, which generally defaults to the maxconn value.
|
backlog is used instead, which generally defaults to the maxconn value.
|
||||||
|
@ -7384,7 +7392,8 @@ npn <protocols>
|
||||||
as supported on top of NPN. The protocol list consists in a comma-delimited
|
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).
|
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
|
This requires that the SSL library is build with support for TLS extensions
|
||||||
enabled (check with haproxy -vv).
|
enabled (check with haproxy -vv). Note that the NPN extension has been
|
||||||
|
replaced with the ALPN extension (see the "alpn" keyword).
|
||||||
|
|
||||||
ssl
|
ssl
|
||||||
This setting is only available when support for OpenSSL was built in. It
|
This setting is only available when support for OpenSSL was built in. It
|
||||||
|
@ -9016,6 +9025,16 @@ ssl_fc_alg_keysize <integer>
|
||||||
Returns true when the incoming connection was made over an SSL/TLS transport
|
Returns true when the incoming connection was made over an SSL/TLS transport
|
||||||
layer and the symmetric cipher key size supported in bits matches the value.
|
layer and the symmetric cipher key size supported in bits matches the value.
|
||||||
|
|
||||||
|
ssl_fc_alpn <string>
|
||||||
|
Returns true when the incoming connection was made over an SSL/TLS transport
|
||||||
|
layer which deciphered it and found a Next Protocol Negociation TLS extension
|
||||||
|
sent by the client, matching the specified string. This requires that the SSL
|
||||||
|
library is build with support for TLS extensions enabled (check haproxy -vv).
|
||||||
|
Note that the TLS ALPN extension is not advertised unless the "alpn" keyword
|
||||||
|
on the "bind" line specifies a protocol list. Also, nothing forces the client
|
||||||
|
to pick a protocol from this list, any other one may be requested. The TLS
|
||||||
|
ALPN extension is meant to replace the TLS NPN extension.
|
||||||
|
|
||||||
ssl_fc_cipher <string>
|
ssl_fc_cipher <string>
|
||||||
returns true when the incoming connection was made over an ssl/tls transport
|
returns true when the incoming connection was made over an ssl/tls transport
|
||||||
layer and the name of the used cipher matches the string.
|
layer and the name of the used cipher matches the string.
|
||||||
|
@ -9042,7 +9061,8 @@ ssl_fc_npn <string>
|
||||||
library is build with support for TLS extensions enabled (check haproxy -vv).
|
library is build with support for TLS extensions enabled (check haproxy -vv).
|
||||||
Note that the TLS NPN extension is not advertised unless the "npn" keyword on
|
Note that the TLS NPN extension is not advertised unless the "npn" keyword on
|
||||||
the "bind" line specifies a protocol list. Also, nothing forces the client to
|
the "bind" line specifies a protocol list. Also, nothing forces the client to
|
||||||
pick a protocol from this list, any other one may be requested.
|
pick a protocol from this list, any other one may be requested. Please note
|
||||||
|
that the TLS NPN extension was replaced with ALPN.
|
||||||
|
|
||||||
ssl_fc_protocol <string>
|
ssl_fc_protocol <string>
|
||||||
Returns true when the incoming connection was made over an SSL/TLS transport
|
Returns true when the incoming connection was made over an SSL/TLS transport
|
||||||
|
|
|
@ -127,6 +127,8 @@ struct bind_conf {
|
||||||
SSL_CTX *default_ctx; /* SSL context of first/default certificate */
|
SSL_CTX *default_ctx; /* SSL context of first/default certificate */
|
||||||
char *npn_str; /* NPN protocol string */
|
char *npn_str; /* NPN protocol string */
|
||||||
int npn_len; /* NPN protocol string length */
|
int npn_len; /* NPN protocol string length */
|
||||||
|
char *alpn_str; /* ALPN protocol string */
|
||||||
|
int alpn_len; /* ALPN protocol string length */
|
||||||
int strict_sni; /* refuse negotiation if sni doesn't match a certificate */
|
int strict_sni; /* refuse negotiation if sni doesn't match a certificate */
|
||||||
struct eb_root sni_ctx; /* sni_ctx tree of all known certs full-names sorted by name */
|
struct eb_root sni_ctx; /* sni_ctx tree of all known certs full-names sorted by name */
|
||||||
struct eb_root sni_w_ctx; /* sni_ctx tree of all known certs wildcards sorted by name */
|
struct eb_root sni_w_ctx; /* sni_ctx tree of all known certs wildcards sorted by name */
|
||||||
|
|
|
@ -165,6 +165,21 @@ static int ssl_sock_advertise_npn_protos(SSL *s, const unsigned char **data,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef OPENSSL_ALPN_NEGOTIATED
|
||||||
|
/* This callback is used so that the server advertises the list of
|
||||||
|
* negociable protocols for ALPN.
|
||||||
|
*/
|
||||||
|
static int ssl_sock_advertise_alpn_protos(SSL *s, const unsigned char **data,
|
||||||
|
unsigned int *len, void *arg)
|
||||||
|
{
|
||||||
|
struct bind_conf *conf = arg;
|
||||||
|
|
||||||
|
*data = (const unsigned char *)conf->alpn_str;
|
||||||
|
*len = conf->alpn_len;
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
||||||
/* Sets the SSL ctx of <ssl> to match the advertised server name. Returns a
|
/* Sets the SSL ctx of <ssl> to match the advertised server name. Returns a
|
||||||
* warning when no match is found, which implies the default (first) cert
|
* warning when no match is found, which implies the default (first) cert
|
||||||
|
@ -692,6 +707,10 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx, struct proxy
|
||||||
if (bind_conf->npn_str)
|
if (bind_conf->npn_str)
|
||||||
SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_sock_advertise_npn_protos, bind_conf);
|
SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_sock_advertise_npn_protos, bind_conf);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef OPENSSL_ALPN_NEGOTIATED
|
||||||
|
if (bind_conf->alpn_str)
|
||||||
|
SSL_CTX_set_alpn_advertised_cb(ctx, ssl_sock_advertise_alpn_protos, bind_conf);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
||||||
SSL_CTX_set_tlsext_servername_callback(ctx, ssl_sock_switchctx_cbk);
|
SSL_CTX_set_tlsext_servername_callback(ctx, ssl_sock_switchctx_cbk);
|
||||||
|
@ -2253,6 +2272,28 @@ smp_fetch_ssl_fc_npn(struct proxy *px, struct session *l4, void *l7, unsigned in
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef OPENSSL_ALPN_NEGOTIATED
|
||||||
|
static int
|
||||||
|
smp_fetch_ssl_fc_alpn(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
|
||||||
|
const struct arg *args, struct sample *smp)
|
||||||
|
{
|
||||||
|
smp->flags = 0;
|
||||||
|
smp->type = SMP_T_CSTR;
|
||||||
|
|
||||||
|
if (!l4 || !l4->si[0].conn->xprt_ctx || l4->si[0].conn->xprt != &ssl_sock)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
smp->data.str.str = NULL;
|
||||||
|
SSL_get0_alpn_negotiated(l4->si[0].conn->xprt_ctx,
|
||||||
|
(const unsigned char **)&smp->data.str.str, (unsigned *)&smp->data.str.len);
|
||||||
|
|
||||||
|
if (!smp->data.str.str)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
smp_fetch_ssl_fc_protocol(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
|
smp_fetch_ssl_fc_protocol(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
|
||||||
const struct arg *args, struct sample *smp)
|
const struct arg *args, struct sample *smp)
|
||||||
|
@ -2687,6 +2728,54 @@ static int bind_parse_npn(char **args, int cur_arg, struct proxy *px, struct bin
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* parse the "alpn" bind keyword */
|
||||||
|
static int bind_parse_alpn(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
|
||||||
|
{
|
||||||
|
#ifdef OPENSSL_ALPN_NEGOTIATED
|
||||||
|
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(conf->alpn_str);
|
||||||
|
|
||||||
|
/* the ALPN string is built as a suite of (<len> <name>)* */
|
||||||
|
conf->alpn_len = strlen(args[cur_arg + 1]) + 1;
|
||||||
|
conf->alpn_str = calloc(1, conf->alpn_len);
|
||||||
|
memcpy(conf->alpn_str + 1, args[cur_arg + 1], conf->alpn_len);
|
||||||
|
|
||||||
|
/* replace commas with the name length */
|
||||||
|
p1 = conf->alpn_str;
|
||||||
|
p2 = p1 + 1;
|
||||||
|
while (1) {
|
||||||
|
p2 = memchr(p1 + 1, ',', conf->alpn_str + conf->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 "ssl" bind keyword */
|
/* parse the "ssl" bind keyword */
|
||||||
static int bind_parse_ssl(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
|
static int bind_parse_ssl(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
|
||||||
{
|
{
|
||||||
|
@ -2955,6 +3044,9 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {{ },{
|
||||||
{ "ssl_fc_has_sni", smp_fetch_ssl_fc_has_sni, 0, NULL, SMP_T_BOOL, SMP_USE_L5CLI },
|
{ "ssl_fc_has_sni", smp_fetch_ssl_fc_has_sni, 0, NULL, SMP_T_BOOL, SMP_USE_L5CLI },
|
||||||
#ifdef OPENSSL_NPN_NEGOTIATED
|
#ifdef OPENSSL_NPN_NEGOTIATED
|
||||||
{ "ssl_fc_npn", smp_fetch_ssl_fc_npn, 0, NULL, SMP_T_CSTR, SMP_USE_L5CLI },
|
{ "ssl_fc_npn", smp_fetch_ssl_fc_npn, 0, NULL, SMP_T_CSTR, SMP_USE_L5CLI },
|
||||||
|
#endif
|
||||||
|
#ifdef OPENSSL_ALPN_NEGOTIATED
|
||||||
|
{ "ssl_fc_alpn", smp_fetch_ssl_fc_alpn, 0, NULL, SMP_T_CSTR, SMP_USE_L5CLI },
|
||||||
#endif
|
#endif
|
||||||
{ "ssl_fc_protocol", smp_fetch_ssl_fc_protocol, 0, NULL, SMP_T_CSTR, SMP_USE_L5CLI },
|
{ "ssl_fc_protocol", smp_fetch_ssl_fc_protocol, 0, NULL, SMP_T_CSTR, SMP_USE_L5CLI },
|
||||||
{ "ssl_fc_use_keysize", smp_fetch_ssl_fc_use_keysize, 0, NULL, SMP_T_UINT, SMP_USE_L5CLI },
|
{ "ssl_fc_use_keysize", smp_fetch_ssl_fc_use_keysize, 0, NULL, SMP_T_UINT, SMP_USE_L5CLI },
|
||||||
|
@ -2995,6 +3087,9 @@ static struct acl_kw_list acl_kws = {{ },{
|
||||||
{ "ssl_fc_has_sni", NULL, acl_parse_nothing, acl_match_nothing },
|
{ "ssl_fc_has_sni", NULL, acl_parse_nothing, acl_match_nothing },
|
||||||
#ifdef OPENSSL_NPN_NEGOTIATED
|
#ifdef OPENSSL_NPN_NEGOTIATED
|
||||||
{ "ssl_fc_npn", NULL, acl_parse_str, acl_match_str },
|
{ "ssl_fc_npn", NULL, acl_parse_str, acl_match_str },
|
||||||
|
#endif
|
||||||
|
#ifdef OPENSSL_ALPN_NEGOTIATED
|
||||||
|
{ "ssl_fc_alpn", NULL, acl_parse_str, acl_match_str },
|
||||||
#endif
|
#endif
|
||||||
{ "ssl_fc_protocol", NULL, acl_parse_str, acl_match_str },
|
{ "ssl_fc_protocol", NULL, acl_parse_str, acl_match_str },
|
||||||
{ "ssl_fc_use_keysize", NULL, acl_parse_int, acl_match_int },
|
{ "ssl_fc_use_keysize", NULL, acl_parse_int, acl_match_int },
|
||||||
|
@ -3012,6 +3107,7 @@ static struct acl_kw_list acl_kws = {{ },{
|
||||||
* not enabled.
|
* not enabled.
|
||||||
*/
|
*/
|
||||||
static struct bind_kw_list bind_kws = { "SSL", { }, {
|
static struct bind_kw_list bind_kws = { "SSL", { }, {
|
||||||
|
{ "alpn", bind_parse_alpn, 1 }, /* set ALPN supported protocols */
|
||||||
{ "ca-file", bind_parse_ca_file, 1 }, /* set CAfile to process verify on client cert */
|
{ "ca-file", bind_parse_ca_file, 1 }, /* set CAfile to process verify on client cert */
|
||||||
{ "ca-ignore-err", bind_parse_ignore_err, 1 }, /* set error IDs to ignore on verify depth > 0 */
|
{ "ca-ignore-err", bind_parse_ignore_err, 1 }, /* set error IDs to ignore on verify depth > 0 */
|
||||||
{ "ciphers", bind_parse_ciphers, 1 }, /* set SSL cipher suite */
|
{ "ciphers", bind_parse_ciphers, 1 }, /* set SSL cipher suite */
|
||||||
|
|
Loading…
Reference in New Issue