MEDIUM: ssl: add sample fetches for is_ssl, ssl_has_sni, ssl_sni_*

This allows SNI presence and value to be checked on incoming SSL connections.
It is usable both for ACLs and stick tables.
This commit is contained in:
Willy Tarreau 2012-09-10 08:20:03 +02:00
parent 1ee0e302a1
commit 7875d0967f
2 changed files with 151 additions and 4 deletions

View File

@ -7901,11 +7901,18 @@ during analysis. This requires that some data has been buffered, for instance
through TCP request content inspection. Please see the "tcp-request content" through TCP request content inspection. Please see the "tcp-request content"
keyword for more detailed information on the subject. keyword for more detailed information on the subject.
is_ssl
Returns true when the incoming connection was made via an SSL/TLS data layer
and is locally deciphered. This means it has matched a socket declared with
a "bind" line having the "ssl" option.
rep_ssl_hello_type <integer> rep_ssl_hello_type <integer>
Returns true when data in the response buffer looks like a complete SSL (v3 Returns true when data in the response buffer looks like a complete SSL (v3
or superior) hello message and handshake type is equal to <integer>. or superior) hello message and handshake type is equal to <integer>.
This test was designed to be used with TCP response content inspection: a This test was designed to be used with TCP response content inspection: a
SSL session ID may be fetched. SSL session ID may be fetched. Note that this only applies to raw contents
found in the request buffer and not to contents deciphered via an SSL data
layer, so this will not work with "bind" lines having the "ssl" option.
req_len <integer> req_len <integer>
Returns true when the length of the data in the request buffer matches the Returns true when the length of the data in the request buffer matches the
@ -7946,7 +7953,9 @@ req_ssl_hello_type <integer>
Returns true when data in the request buffer looks like a complete SSL (v3 Returns true when data in the request buffer looks like a complete SSL (v3
or superior) hello message and handshake type is equal to <integer>. or superior) hello message and handshake type is equal to <integer>.
This test was designed to be used with TCP request content inspection: an This test was designed to be used with TCP request content inspection: an
SSL session ID may be fetched. SSL session ID may be fetched. Note that this only applies to raw contents
found in the request buffer and not to contents deciphered via an SSL data
layer, so this will not work with "bind" lines having the "ssl" option.
req_ssl_sni <string> req_ssl_sni <string>
Returns true when data in the request buffer looks like a complete SSL (v3 Returns true when data in the request buffer looks like a complete SSL (v3
@ -7956,7 +7965,10 @@ req_ssl_sni <string>
or denying access to certain hosts when SSL/TLS is used by the client. This or denying access to certain hosts when SSL/TLS is used by the client. This
test was designed to be used with TCP request content inspection. If content test was designed to be used with TCP request content inspection. If content
switching is needed, it is recommended to first wait for a complete client switching is needed, it is recommended to first wait for a complete client
hello (type 1), like in the example below. hello (type 1), like in the example below. Note that this only applies to raw
contents found in the request buffer and not to contents deciphered via an
SSL data layer, so this will not work with "bind" lines having the "ssl"
option. See also "ssl_sni" below.
Examples : Examples :
# Wait for a client hello for at most 5 seconds # Wait for a client hello for at most 5 seconds
@ -7972,7 +7984,53 @@ req_ssl_ver <decimal>
easily fooled. In particular, it waits for as many bytes as announced in the easily fooled. In particular, it waits for as many bytes as announced in the
message header if this header looks valid (bound to the buffer size). Note message header if this header looks valid (bound to the buffer size). Note
that TLSv1 is announced as SSL version 3.1. This test was designed to be used that TLSv1 is announced as SSL version 3.1. This test was designed to be used
with TCP request content inspection. with TCP request content inspection. Note that this only applies to raw
contents found in the request buffer and not to contents deciphered via an
SSL data layer, so this will not work with "bind" lines having the "ssl"
option.
ssl_sni <string>
Returns true when the incoming connection was made over an SSL/TLS data layer
which deciphered it and found a Server Name Indication TLS extension sent by
the client, matching the specified string. In HTTPS, the SNI field (when
present) is equal to the requested host name. This match is different from
req_ssl_sni above in that it applies to the connection being deciphered by
haproxy and not to SSL contents being blindly forwarded. This requires that
the SSL library is build with support for TLS extensions (check haproxy -vv).
ssl_has_sni
This is used to check for presence of a Server Name Indication TLS extension
in an incoming connection was made over an SSL/TLS data layer. Returns true
when the incoming connection presents a TLS SNI field. This requires that
the SSL library is build with support for TLS extensions (check haproxy -vv).
ssl_sni <string>
Returns true when the incoming connection was made over an SSL/TLS data layer
which deciphered it and found a Server Name Indication TLS extension sent by
the client, matching the specified string. In HTTPS, the SNI field (when
present) is equal to the requested host name. This match is different from
req_ssl_sni above in that it applies to the connection being deciphered by
haproxy and not to SSL contents being blindly forwarded. See also ssl_sni_end
and ssl_sni_req below. This requires that the SSL library is build with
support for TLS extensions (check haproxy -vv).
ssl_sni_end <string>
Returns true when the incoming connection was made over an SSL/TLS data layer
which deciphered it and found a Server Name Indication TLS extension sent by
the client, ending like the specified string. In HTTPS, the SNI field (when
present) is equal to the requested host name. This match is different from
req_ssl_sni above in that it applies to the connection being deciphered by
haproxy and not to SSL contents being blindly forwarded. This requires that
the SSL library is build with support for TLS extensions (check haproxy -vv).
ssl_sni_req <regex>
Returns true when the incoming connection was made over an SSL/TLS data layer
which deciphered it and found a Server Name Indication TLS extension sent by
the client, matching the specified regex. In HTTPS, the SNI field (when
present) is equal to the requested host name. This match is different from
req_ssl_sni above in that it applies to the connection being deciphered by
haproxy and not to SSL contents being blindly forwarded. This requires that
the SSL library is build with support for TLS extensions (check haproxy -vv).
wait_end wait_end
Waits for the end of the analysis period to return true. This may be used in Waits for the end of the analysis period to return true. This may be used in
@ -8542,6 +8600,10 @@ The list of currently supported pattern fetch functions is the following :
last one. A typical use is with the X-Forwarded-For header once last one. A typical use is with the X-Forwarded-For header once
converted to IP, associated with an IP stick-table. converted to IP, associated with an IP stick-table.
is_ssl This checks the data layer used by incoming connection, and
returns 1 if the connection was made via an SSL/TLS data layer,
otherwise zero.
path This extracts the request's URL path (without the host part). A path This extracts the request's URL path (without the host part). A
typical use is with prefetch-capable caches, and with portals typical use is with prefetch-capable caches, and with portals
which need to aggregate multiple information from databases and which need to aggregate multiple information from databases and
@ -8569,6 +8631,18 @@ The list of currently supported pattern fetch functions is the following :
that this function will be useful but it's available at no cost. that this function will be useful but it's available at no cost.
It is of type integer and only works with such tables. It is of type integer and only works with such tables.
ssl_has_sni This checks the data layer used by incoming connection, and
returns 1 if the connection was made via an SSL/TLS data layer
and the client sent a Server Name Indication TLS extension,
otherwise zero. This requires that the SSL library is build with
support for TLS extensions (check haproxy -vv).
ssl_sni This extracts the Server Name Indication field from an incoming
connection made via an SSL/TLS data layer and locally deciphered
by haproxy. The result typically is a string matching the HTTPS
host name (253 chars or less). The SSL library must have been
built with support for TLS extensions (check haproxy -vv).
url This extracts the request's URL as presented in the request. A url This extracts the request's URL as presented in the request. A
typical use is with prefetch-capable caches, and with portals typical use is with prefetch-capable caches, and with portals
which need to aggregate multiple information from databases and which need to aggregate multiple information from databases and

View File

@ -45,6 +45,8 @@
#include <types/global.h> #include <types/global.h>
#include <types/ssl_sock.h> #include <types/ssl_sock.h>
#include <proto/acl.h>
#include <proto/arg.h>
#include <proto/connection.h> #include <proto/connection.h>
#include <proto/fd.h> #include <proto/fd.h>
#include <proto/freq_ctr.h> #include <proto/freq_ctr.h>
@ -748,6 +750,75 @@ static void ssl_sock_shutw(struct connection *conn, int clean)
SSL_set_shutdown(conn->data_ctx, SSL_SENT_SHUTDOWN); SSL_set_shutdown(conn->data_ctx, SSL_SENT_SHUTDOWN);
} }
/***** Below are some sample fetching functions for ACL/patterns *****/
/* boolean, returns true if data layer is SSL */
static int
smp_fetch_is_ssl(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
smp->type = SMP_T_BOOL;
smp->data.uint = (l4->si[0].conn.data == &ssl_sock);
return 1;
}
/* boolean, returns true if data layer is SSL */
static int
smp_fetch_has_sni(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
smp->type = SMP_T_BOOL;
smp->data.uint = (l4->si[0].conn.data == &ssl_sock) &&
SSL_get_servername(l4->si[0].conn.data_ctx, TLSEXT_NAMETYPE_host_name) != NULL;
return 1;
#else
return 0;
#endif
}
static int
smp_fetch_ssl_sni(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
smp->flags = 0;
smp->type = SMP_T_CSTR;
if (!l4 || l4->si[0].conn.data != &ssl_sock)
return 0;
/* data points to cookie value */
smp->data.str.str = (char *)SSL_get_servername(l4->si[0].conn.data_ctx, TLSEXT_NAMETYPE_host_name);
smp->data.str.len = strlen(smp->data.str.str);
return 1;
#else
return 0;
#endif
}
/* Note: must not be declared <const> as its list will be overwritten.
* Please take care of keeping this list alphabetically sorted.
*/
static struct sample_fetch_kw_list sample_fetch_keywords = {{ },{
{ "is_ssl", smp_fetch_is_ssl, 0, NULL, SMP_T_BOOL, SMP_CAP_REQ|SMP_CAP_RES },
{ "ssl_has_sni", smp_fetch_has_sni, 0, NULL, SMP_T_BOOL, SMP_CAP_REQ|SMP_CAP_RES },
{ "ssl_sni", smp_fetch_ssl_sni, 0, NULL, SMP_T_CSTR, SMP_CAP_REQ|SMP_CAP_RES },
{ NULL, NULL, 0, 0, 0 },
}};
/* Note: must not be declared <const> as its list will be overwritten.
* Please take care of keeping this list alphabetically sorted.
*/
static struct acl_kw_list acl_kws = {{ },{
{ "is_ssl", acl_parse_int, smp_fetch_is_ssl, acl_match_nothing, ACL_USE_L6REQ_PERMANENT, 0 },
{ "ssl_has_sni", acl_parse_int, smp_fetch_has_sni, acl_match_nothing, ACL_USE_L6REQ_PERMANENT, 0 },
{ "ssl_sni", acl_parse_str, smp_fetch_ssl_sni, acl_match_str, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 },
{ "ssl_sni_end", acl_parse_str, smp_fetch_ssl_sni, acl_match_end, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 },
{ "ssl_sni_reg", acl_parse_str, smp_fetch_ssl_sni, acl_match_reg, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 },
{ NULL, NULL, NULL, NULL },
}};
/* data-layer operations for SSL sockets */ /* data-layer operations for SSL sockets */
struct data_ops ssl_sock = { struct data_ops ssl_sock = {
@ -768,6 +839,8 @@ static void __ssl_sock_init(void) {
SSL_library_init(); SSL_library_init();
cm = SSL_COMP_get_compression_methods(); cm = SSL_COMP_get_compression_methods();
sk_SSL_COMP_zero(cm); sk_SSL_COMP_zero(cm);
sample_register_fetches(&sample_fetch_keywords);
acl_register_keywords(&acl_kws);
} }
/* /*