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:
Willy Tarreau 2013-04-02 02:30:41 +02:00
parent a4312fa28e
commit ab861d3856
3 changed files with 120 additions and 2 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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 */