mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-22 22:31:28 +02:00
MEDIUM: listener: apply a limit on the session rate submitted to SSL
Just like the previous commit, we sometimes want to limit the rate of incoming SSL connections. While it can be done for a frontend, it was not possible for a whole process, which makes sense when multiple processes are running on a system to server multiple customers. The new global "maxsslrate" setting is usable to fix a limit on the session rate going to the SSL frontends. The limits applies before the SSL handshake and not after, so that it saves the SSL stack from expensive key computations that would finally be aborted before being accounted for. The same setting may be changed at run time on the CLI using "set rate-limit ssl-session global".
This commit is contained in:
parent
93e7c006c1
commit
e43d5323c6
@ -467,6 +467,7 @@ The following keywords are supported in the "global" section :
|
|||||||
- maxpipes
|
- maxpipes
|
||||||
- maxsessrate
|
- maxsessrate
|
||||||
- maxsslconn
|
- maxsslconn
|
||||||
|
- maxsslrate
|
||||||
- noepoll
|
- noepoll
|
||||||
- nokqueue
|
- nokqueue
|
||||||
- nopoll
|
- nopoll
|
||||||
@ -753,6 +754,18 @@ maxsslconn <number>
|
|||||||
that the limit applies both to incoming and outgoing connections, so one
|
that the limit applies both to incoming and outgoing connections, so one
|
||||||
connection which is deciphered then ciphered accounts for 2 SSL connections.
|
connection which is deciphered then ciphered accounts for 2 SSL connections.
|
||||||
|
|
||||||
|
maxsslrate <number>
|
||||||
|
Sets the maximum per-process number of SSL sessions per second to <number>.
|
||||||
|
SSL listeners will stop accepting connections when this limit is reached. It
|
||||||
|
can be used to limit the global SSL CPU usage regardless of each frontend
|
||||||
|
capacity. It is important to note that this can only be used as a service
|
||||||
|
protection measure, as there will not necessarily be a fair share between
|
||||||
|
frontends when the limit is reached, so it's a good idea to also limit each
|
||||||
|
frontend to some value close to its expected share. It is also important to
|
||||||
|
note that the sessions are accounted before they enter the SSL stack and not
|
||||||
|
after, which also protects the stack against bad handshakes. Also, lowering
|
||||||
|
tune.maxaccept can improve fairness.
|
||||||
|
|
||||||
maxzlibmem <number>
|
maxzlibmem <number>
|
||||||
Sets the maximum amount of RAM in megabytes per process usable by the zlib.
|
Sets the maximum amount of RAM in megabytes per process usable by the zlib.
|
||||||
When the maximum amount is reached, future sessions will not compress as long
|
When the maximum amount is reached, future sessions will not compress as long
|
||||||
@ -12615,6 +12628,13 @@ set rate-limit sessions global <value>
|
|||||||
applies to all frontends and the change has an immediate effect. The value
|
applies to all frontends and the change has an immediate effect. The value
|
||||||
is passed in number of sessions per second.
|
is passed in number of sessions per second.
|
||||||
|
|
||||||
|
set rate-limit ssl-sessions global <value>
|
||||||
|
Change the process-wide SSL session rate limit, which is set by the global
|
||||||
|
'maxsslrate' setting. A value of zero disables the limitation. This limit
|
||||||
|
applies to all frontends and the change has an immediate effect. The value
|
||||||
|
is passed in number of sessions per second sent to the SSL stack. It applies
|
||||||
|
before the handshake in order to protect the stack against handshake abuses.
|
||||||
|
|
||||||
set table <table> key <key> [data.<data_type> <value>]*
|
set table <table> key <key> [data.<data_type> <value>]*
|
||||||
Create or update a stick-table entry in the table. If the key is not present,
|
Create or update a stick-table entry in the table. If the key is not present,
|
||||||
an entry is inserted. See stick-table in section 4.2 to find all possible
|
an entry is inserted. See stick-table in section 4.2 to find all possible
|
||||||
|
@ -81,10 +81,12 @@ struct global {
|
|||||||
#endif
|
#endif
|
||||||
struct freq_ctr conn_per_sec;
|
struct freq_ctr conn_per_sec;
|
||||||
struct freq_ctr sess_per_sec;
|
struct freq_ctr sess_per_sec;
|
||||||
|
struct freq_ctr ssl_per_sec;
|
||||||
struct freq_ctr comp_bps_in; /* bytes per second, before http compression */
|
struct freq_ctr comp_bps_in; /* bytes per second, before http compression */
|
||||||
struct freq_ctr comp_bps_out; /* bytes per second, after http compression */
|
struct freq_ctr comp_bps_out; /* bytes per second, after http compression */
|
||||||
int cps_lim, cps_max;
|
int cps_lim, cps_max;
|
||||||
int sps_lim, sps_max;
|
int sps_lim, sps_max;
|
||||||
|
int ssl_lim, ssl_max;
|
||||||
int comp_rate_lim; /* HTTP compression rate limit */
|
int comp_rate_lim; /* HTTP compression rate limit */
|
||||||
int maxpipes; /* max # of pipes */
|
int maxpipes; /* max # of pipes */
|
||||||
int maxsock; /* max # of sockets */
|
int maxsock; /* max # of sockets */
|
||||||
|
@ -888,6 +888,19 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
|
|||||||
}
|
}
|
||||||
global.sps_lim = atol(args[1]);
|
global.sps_lim = atol(args[1]);
|
||||||
}
|
}
|
||||||
|
else if (!strcmp(args[0], "maxsslrate")) {
|
||||||
|
if (global.ssl_lim != 0) {
|
||||||
|
Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
|
||||||
|
err_code |= ERR_ALERT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
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.ssl_lim = atol(args[1]);
|
||||||
|
}
|
||||||
else if (!strcmp(args[0], "maxcomprate")) {
|
else if (!strcmp(args[0], "maxcomprate")) {
|
||||||
if (*(args[1]) == 0) {
|
if (*(args[1]) == 0) {
|
||||||
Alert("parsing [%s:%d] : '%s' expects an integer argument in kb/s.\n", file, linenum, args[0]);
|
Alert("parsing [%s:%d] : '%s' expects an integer argument in kb/s.\n", file, linenum, args[0]);
|
||||||
|
@ -1462,6 +1462,45 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef USE_OPENSSL
|
||||||
|
else if (strcmp(args[2], "ssl-sessions") == 0) {
|
||||||
|
if (strcmp(args[3], "global") == 0) {
|
||||||
|
int v;
|
||||||
|
|
||||||
|
if (s->listener->bind_conf->level < ACCESS_LVL_ADMIN) {
|
||||||
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
||||||
|
appctx->st0 = STAT_CLI_PRINT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*args[4]) {
|
||||||
|
appctx->ctx.cli.msg = "Expects an integer value.\n";
|
||||||
|
appctx->st0 = STAT_CLI_PRINT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = atoi(args[4]);
|
||||||
|
if (v < 0) {
|
||||||
|
appctx->ctx.cli.msg = "Value out of range.\n";
|
||||||
|
appctx->st0 = STAT_CLI_PRINT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
global.ssl_lim = v;
|
||||||
|
|
||||||
|
/* Dequeues all of the listeners waiting for a resource */
|
||||||
|
if (!LIST_ISEMPTY(&global_listener_queue))
|
||||||
|
dequeue_all_listeners(&global_listener_queue);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appctx->ctx.cli.msg = "'set rate-limit ssl-sessions' only supports 'global'.\n";
|
||||||
|
appctx->st0 = STAT_CLI_PRINT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
else if (strcmp(args[2], "http-compression") == 0) {
|
else if (strcmp(args[2], "http-compression") == 0) {
|
||||||
if (strcmp(args[3], "global") == 0) {
|
if (strcmp(args[3], "global") == 0) {
|
||||||
int v;
|
int v;
|
||||||
@ -1482,7 +1521,7 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
appctx->ctx.cli.msg = "'set rate-limit' supports 'connections', 'sessions', and 'http-compression'.\n";
|
appctx->ctx.cli.msg = "'set rate-limit' supports 'connections', 'sessions', 'ssl-sessions', and 'http-compression'.\n";
|
||||||
appctx->st0 = STAT_CLI_PRINT;
|
appctx->st0 = STAT_CLI_PRINT;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -2223,6 +2262,11 @@ static int stats_dump_info_to_buffer(struct stream_interface *si)
|
|||||||
"SessRate: %d\n"
|
"SessRate: %d\n"
|
||||||
"SessRateLimit: %d\n"
|
"SessRateLimit: %d\n"
|
||||||
"MaxSessRate: %d\n"
|
"MaxSessRate: %d\n"
|
||||||
|
#ifdef USE_OPENSSL
|
||||||
|
"SslRate: %d\n"
|
||||||
|
"SslRateLimit: %d\n"
|
||||||
|
"MaxSslRate: %d\n"
|
||||||
|
#endif
|
||||||
"CompressBpsIn: %u\n"
|
"CompressBpsIn: %u\n"
|
||||||
"CompressBpsOut: %u\n"
|
"CompressBpsOut: %u\n"
|
||||||
"CompressBpsRateLim: %u\n"
|
"CompressBpsRateLim: %u\n"
|
||||||
@ -2251,6 +2295,9 @@ static int stats_dump_info_to_buffer(struct stream_interface *si)
|
|||||||
global.maxpipes, pipes_used, pipes_free,
|
global.maxpipes, pipes_used, pipes_free,
|
||||||
read_freq_ctr(&global.conn_per_sec), global.cps_lim, global.cps_max,
|
read_freq_ctr(&global.conn_per_sec), global.cps_lim, global.cps_max,
|
||||||
read_freq_ctr(&global.sess_per_sec), global.sps_lim, global.sps_max,
|
read_freq_ctr(&global.sess_per_sec), global.sps_lim, global.sps_max,
|
||||||
|
#ifdef USE_OPENSSL
|
||||||
|
read_freq_ctr(&global.ssl_per_sec), global.ssl_lim, global.ssl_max,
|
||||||
|
#endif
|
||||||
read_freq_ctr(&global.comp_bps_in), read_freq_ctr(&global.comp_bps_out),
|
read_freq_ctr(&global.comp_bps_in), read_freq_ctr(&global.comp_bps_out),
|
||||||
global.comp_rate_lim,
|
global.comp_rate_lim,
|
||||||
#ifdef USE_ZLIB
|
#ifdef USE_ZLIB
|
||||||
|
@ -294,7 +294,23 @@ void listener_accept(int fd)
|
|||||||
if (max_accept > max)
|
if (max_accept > max)
|
||||||
max_accept = max;
|
max_accept = max;
|
||||||
}
|
}
|
||||||
|
#ifdef USE_OPENSSL
|
||||||
|
if (!(l->options & LI_O_UNLIMITED) && global.ssl_lim && l->bind_conf && l->bind_conf->is_ssl) {
|
||||||
|
int max = freq_ctr_remain(&global.ssl_per_sec, global.ssl_lim, 0);
|
||||||
|
int expire;
|
||||||
|
|
||||||
|
if (unlikely(!max)) {
|
||||||
|
/* frontend accept rate limit was reached */
|
||||||
|
limit_listener(l, &global_listener_queue);
|
||||||
|
expire = tick_add(now_ms, next_event_delay(&global.ssl_per_sec, global.ssl_lim, 0));
|
||||||
|
task_schedule(global_listener_queue_task, tick_first(expire, global_listener_queue_task->expire));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_accept > max)
|
||||||
|
max_accept = max;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (p && p->fe_sps_lim) {
|
if (p && p->fe_sps_lim) {
|
||||||
int max = freq_ctr_remain(&p->fe_sess_per_sec, p->fe_sps_lim, 0);
|
int max = freq_ctr_remain(&p->fe_sess_per_sec, p->fe_sps_lim, 0);
|
||||||
|
|
||||||
@ -435,6 +451,14 @@ void listener_accept(int fd)
|
|||||||
if (global.sess_per_sec.curr_ctr > global.sps_max)
|
if (global.sess_per_sec.curr_ctr > global.sps_max)
|
||||||
global.sps_max = global.sess_per_sec.curr_ctr;
|
global.sps_max = global.sess_per_sec.curr_ctr;
|
||||||
}
|
}
|
||||||
|
#ifdef USE_OPENSSL
|
||||||
|
if (!(l->options & LI_O_UNLIMITED) && l->bind_conf && l->bind_conf->is_ssl) {
|
||||||
|
|
||||||
|
update_freq_ctr(&global.ssl_per_sec, 1);
|
||||||
|
if (global.ssl_per_sec.curr_ctr > global.ssl_max)
|
||||||
|
global.ssl_max = global.ssl_per_sec.curr_ctr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} /* end of while (max_accept--) */
|
} /* end of while (max_accept--) */
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user