From 403edff4b81e6e7dacc0a71e293101898fb292f4 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Thu, 6 Sep 2012 11:58:37 +0200 Subject: [PATCH] MEDIUM: config: implement maxsslconn in the global section SSL connections take a huge amount of memory, and unfortunately openssl does not check malloc() returns and easily segfaults when too many connections are used. The only solution against this is to provide a global maxsslconn setting to reject SSL connections above the limit in order to avoid reaching unsafe limits. --- doc/configuration.txt | 10 ++++++++++ include/types/global.h | 3 +++ src/cfgparse.c | 16 ++++++++++++++++ src/haproxy.c | 3 +++ src/ssl_sock.c | 12 +++++++++--- 5 files changed, 41 insertions(+), 3 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 00a5238e11..e11b140f35 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -452,6 +452,7 @@ The following keywords are supported in the "global" section : - maxconn - maxconnrate - maxpipes + - maxsslconn - noepoll - nokqueue - nopoll @@ -672,6 +673,15 @@ maxpipes The splice code dynamically allocates and releases pipes, and can fall back to standard copy, so setting this value too low may only impact performance. +maxsslconn + Sets the maximum per-process number of concurrent SSL connections to + . By default there is no SSL-specific limit, which means that the + global maxconn setting will apply to all connections. Setting this limit + avoids having openssl use too much memory and crash when malloc returns NULL + (since it unfortunately does not reliably check for such conditions). Note + that the limit applies both to incoming and outgoing connections, so one + connection which is deciphered then ciphered accounts for 2 SSL connections. + noepoll Disables the use of the "epoll" event polling system on Linux. It is equivalent to the command-line argument "-de". The next polling system diff --git a/include/types/global.h b/include/types/global.h index bd8a06e81e..5775e27dcf 100644 --- a/include/types/global.h +++ b/include/types/global.h @@ -67,6 +67,9 @@ struct global { int gid; int nbproc; int maxconn, hardmaxconn; +#ifdef USE_OPENSSL + int maxsslconn; +#endif struct freq_ctr conn_per_sec; int cps_lim, cps_max; int maxpipes; /* max # of pipes */ diff --git a/src/cfgparse.c b/src/cfgparse.c index a15050355c..925013a35e 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -720,6 +720,22 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm) err_code |= ERR_ALERT; } #endif /* SYSTEM_MAXCONN */ + } + else if (!strcmp(args[0], "maxsslconn")) { +#ifdef USE_OPENSSL + if (*(args[1]) == 0) { + Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + global.maxsslconn = atol(args[1]); +#else + if (*(args[1]) == 0) { + Alert("parsing [%s:%d] : '%s' is not implemented.\n", file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } +#endif } else if (!strcmp(args[0], "maxconnrate")) { if (global.cps_lim != 0) { diff --git a/src/haproxy.c b/src/haproxy.c index 7439a4c98c..def0f3fb9b 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -129,6 +129,9 @@ struct global global = { .sslcachesize = 20000, #endif }, +#if defined (USE_OPENSSL) && defined(DEFAULT_MAXSSLCONN) + .maxsslconn = DEFAULT_MAXSSLCONN, +#endif /* others NULL OK */ }; diff --git a/src/ssl_sock.c b/src/ssl_sock.c index cfe788d273..69c409962f 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -44,6 +44,7 @@ #include +static int sslconns = 0; void ssl_sock_infocbk(const SSL *ssl, int where, int ret) { @@ -69,10 +70,12 @@ static int ssl_sock_init(struct connection *conn) if (conn->data_ctx) return 0; + if (global.maxsslconn && sslconns >= global.maxsslconn) + return -1; + /* If it is in client mode initiate SSL session in connect state otherwise accept state */ if (target_srv(&conn->target)) { - /* Alloc a new SSL session ctx */ conn->data_ctx = SSL_new(target_srv(&conn->target)->ssl_ctx.ctx); if (!conn->data_ctx) @@ -87,10 +90,11 @@ static int ssl_sock_init(struct connection *conn) /* leave init state and start handshake */ conn->flags |= CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN; + + sslconns++; return 0; } else if (target_client(&conn->target)) { - /* Alloc a new SSL session ctx */ conn->data_ctx = SSL_new(target_client(&conn->target)->ssl_ctx.ctx); if (!conn->data_ctx) @@ -106,6 +110,8 @@ static int ssl_sock_init(struct connection *conn) /* leave init state and start handshake */ conn->flags |= CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN; + + sslconns++; return 0; } /* don't know how to handle such a target */ @@ -339,8 +345,8 @@ static void ssl_sock_close(struct connection *conn) { if (conn->data_ctx) { SSL_free(conn->data_ctx); conn->data_ctx = NULL; + sslconns--; } - } /* This function tries to perform a clean shutdown on an SSL connection, and in -- 2.47.3