Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.turnserver
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,8 @@ Options with values:
Defaults is 1000.
--401-window=<seconds> Set the time window duration in seconds for rate limiting 401 Unauthorized responses.
Defaults is 120.
--401-allowlist=<filename> Set the path of the allow-list, one IP per line allowed to bypass the 401 rate-limit settings.
Default is none.


==================================
Expand Down
17 changes: 17 additions & 0 deletions examples/etc/turnserver.conf
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,23 @@
#no-tlsv1_1
#no-tlsv1_2


# Set the maximum number of 401 Unauthorized responses allowed per rate-limiting
# window. When set to 0 disalbes 401 rate-limiting.
#
#401-req-limit=1000

# Set the time window duration in seconds for rate limiting 401 Unauthorized
# responses.
#
#401-window=120

# Set the path of the allow-list, one IP per line allowed to bypass the 401
# rate-limit settings. By default this is disabled. Uncomment to enable use
# of the 401 rate-limit allow list:
#
#401-allowlist=/etc/coturn/allowlist.txt

# Disable RFC5780 (NAT behavior discovery).
#
# Originally, if there are more than one listener address from the same
Expand Down
30 changes: 28 additions & 2 deletions src/apps/relay/mainrelay.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,8 @@ turn_params_t turn_params = {

///////// Ratelimt /////////
RATELIMIT_DEFAULT_MAX_REQUESTS_PER_WINDOW, /* 401-req-limit */
RATELIMIT_DEFAULT_WINDOW_SECS /* 401-window */
RATELIMIT_DEFAULT_WINDOW_SECS, /* 401-window */
NULL /* 401-allowlist */
};

//////////////// OpenSSL Init //////////////////////
Expand Down Expand Up @@ -284,6 +285,7 @@ static void reload_ssl_certs(evutil_socket_t sock, short events, void *args);

static void shutdown_handler(evutil_socket_t sock, short events, void *args);
static void drain_handler(evutil_socket_t sock, short events, void *args);
static void ratelimit_update_allowlist_handler(evutil_socket_t sock, short events, void *args);

//////////////////////////////////////////////////

Expand Down Expand Up @@ -1311,6 +1313,8 @@ static char Usage[] =
" per rate-limiting window. If set to 0 disables rate limiting. Default is 1000.\n"
" --401-window=<seconds>\t\t\t\tSet the time window duration in seconds for rate limiting 401 Unauthorized responses.\n"
" Default is 120.\n"
" --401-allowlist=<filename>\t\t\tSet the path of the allow-list, one IP per line allowed to bypass the 401\n"
" rate-limit settings. Default is none.\n"
" --version Print version (and exit).\n"
" -h Help\n"
"\n";
Expand Down Expand Up @@ -1477,7 +1481,8 @@ enum EXTRA_OPTS {
FEDERATION_PKEY_PWD_OPT,
FEDERATION_REMOTE_WHITELIST_OPT,
RATELIMIT_REQUESTS_OPT,
RATELIMIT_WINDOW_OPT
RATELIMIT_WINDOW_OPT,
RATELIMIT_ALLOWLIST_OPT
};

struct myoption {
Expand Down Expand Up @@ -1632,6 +1637,7 @@ static const struct myoption long_options[] = {
{"syslog-facility", required_argument, NULL, SYSLOG_FACILITY_OPT},
{"401-req-limit", optional_argument, NULL, RATELIMIT_REQUESTS_OPT},
{"401-window", optional_argument, NULL, RATELIMIT_WINDOW_OPT},
{"401-allowlist", optional_argument, NULL, RATELIMIT_ALLOWLIST_OPT},
{NULL, no_argument, NULL, 0}};

static const struct myoption admin_long_options[] = {
Expand Down Expand Up @@ -2396,6 +2402,10 @@ static void set_option(int c, char *value) {
turn_params.ratelimit_401_window_seconds = get_int_value(value, RATELIMIT_DEFAULT_WINDOW_SECS);
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Setting 401 ratelimit window to: %i seconds\n", turn_params.ratelimit_401_window_seconds);
break;
case RATELIMIT_ALLOWLIST_OPT:
STRCPY(turn_params.ratelimit_401_allowlist, value);
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Setting 401 ratelimit allow list to: %s\n", turn_params.ratelimit_401_allowlist);
break;
/* these options have been already taken care of before: */
case 'l':
case NO_STDOUT_LOG_OPT:
Expand Down Expand Up @@ -3344,6 +3354,12 @@ int main(int argc, char **argv) {

setup_server();

/* Init allow list if configured */
if (turn_params.ratelimit_401_allowlist != NULL) {
ratelimit_init_allowlist_map();
ratelimit_update_allowlist(turn_params.ratelimit_401_allowlist);
}

#if defined(WINDOWS)
// TODO: implement it!!! add windows server
#else
Expand All @@ -3356,6 +3372,8 @@ int main(int argc, char **argv) {
event_add(ev, NULL);
ev = evsignal_new(turn_params.listener.event_base, SIGUSR1, drain_handler, NULL);
event_add(ev, NULL);
ev = evsignal_new(turn_params.listener.event_base, SIGRTMIN+3, ratelimit_update_allowlist_handler, NULL);
event_add(ev, NULL);
#endif

drop_privileges();
Expand Down Expand Up @@ -3995,4 +4013,12 @@ static void drain_handler(evutil_socket_t sock, short events, void *args) {
UNUSED_ARG(events);
UNUSED_ARG(args);
}

static void ratelimit_update_allowlist_handler(evutil_socket_t sock, short events, void *args) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Reloading 401 ratelimit allowlist signal %d\n", sock);
ratelimit_update_allowlist(turn_params.ratelimit_401_allowlist);

UNUSED_ARG(events);
UNUSED_ARG(args);
}
///////////////////////////////
1 change: 1 addition & 0 deletions src/apps/relay/mainrelay.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ typedef struct _turn_params_ {

vint ratelimit_401_requests_per_window;
vint ratelimit_401_window_seconds;
char ratelimit_401_allowlist[1025];
} turn_params_t;

extern turn_params_t turn_params;
Expand Down
3 changes: 2 additions & 1 deletion src/apps/relay/netengine.c
Original file line number Diff line number Diff line change
Expand Up @@ -1674,7 +1674,8 @@ static void setup_relay_server(struct relay_server *rs, ioa_engine_handle e, int
turn_params.oauth_server_name, turn_params.acme_redirect,
turn_params.allocation_default_address_family, &turn_params.log_binding,
&turn_params.no_stun_backward_compatibility, &turn_params.response_origin_only_with_rfc5780,
&turn_params.ratelimit_401_requests_per_window, &turn_params.ratelimit_401_window_seconds);
&turn_params.ratelimit_401_requests_per_window, &turn_params.ratelimit_401_window_seconds,
&turn_params.ratelimit_401_allowlist);


// Intentionally performed outside init_turn_server to help avoid future merge conflicts
Expand Down
2 changes: 1 addition & 1 deletion src/ns_turn_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
#ifndef __IOADEFS__
#define __IOADEFS__

#define TURN_SERVER_VERSION "wireapp/4.6.2h"
#define TURN_SERVER_VERSION "wireapp/4.6.2j"
#define TURN_SERVER_VERSION_NAME "Gorst"
#ifndef TURN_SERVER_BUILD_INFO
#define TURN_SERVER_BUILD_INFO ""
Expand Down
111 changes: 103 additions & 8 deletions src/server/ns_turn_ratelimit.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,99 @@
* SUCH DAMAGE.
*/

#include <sys/stat.h>

#include "ns_turn_maps.h"
#include "ns_turn_ioalib.h"
#include "ns_turn_ratelimit.h"

/////////////////// rate limit //////////////////////////

ur_addr_map *rate_limit_map = NULL;
ur_addr_map *rate_limit_allowlist_map = NULL;

int ratelimit_window_secs = RATELIMIT_DEFAULT_WINDOW_SECS;

TURN_MUTEX_DECLARE(rate_limit_main_mutex);
TURN_MUTEX_DECLARE(rate_limit_allowlist_mutex);

void ratelimit_remove_newlines(char *str) {
char *src = str;
char *dst = str;

while (*src != '\0') {
if (*src != '\r' && *src != '\n') {
*dst++ = *src;
}
src++;
}
*dst = '\0';
}

void ratelimit_init_allowlist_map() {
TURN_MUTEX_INIT(&rate_limit_allowlist_mutex);
TURN_MUTEX_LOCK(&rate_limit_allowlist_mutex);

rate_limit_allowlist_map = (ur_addr_map*)malloc(sizeof(ur_addr_map));
ur_addr_map_init(rate_limit_allowlist_map);

TURN_MUTEX_UNLOCK(&rate_limit_allowlist_mutex);
}

void ratelimit_update_allowlist(const char *allowlist) {

FILE *file = NULL;
file = fopen(allowlist, "r");
if (file != NULL) {
char line[1024];

/* Rebuild map */
TURN_MUTEX_LOCK(&rate_limit_allowlist_mutex);
ur_addr_map_clean(rate_limit_allowlist_map);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably want to hold the lock for the entire read process, otherwise you run the risk of rate limiting an allowed endpoint between the list being cleared and the entries being added back.

ur_addr_map_init(rate_limit_allowlist_map);
TURN_MUTEX_UNLOCK(&rate_limit_allowlist_mutex);
/* loop over file and add entries */
while (fgets(line, sizeof(line) - 1, file) != NULL) {
if (!line) {
break;
}

ioa_addr new_address;
ratelimit_remove_newlines(line);
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Added address to 401 ratelimit allow list from file %s: %s\n", allowlist, line);
if(make_ioa_addr_from_full_string((const uint8_t *)line, 0, &new_address) != 0) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Malformed address in 401 ratelimit allow list file %s: %s\n", allowlist, line);
} else {
TURN_MUTEX_LOCK(&rate_limit_allowlist_mutex);
ur_addr_map_put(rate_limit_allowlist_map, &new_address, (ur_addr_map_value_type)1);
TURN_MUTEX_UNLOCK(&rate_limit_allowlist_mutex);
}
}
if (file) {
fclose(file);
}
} else {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Could not open 401 ratelimit allow list file: %s\n", allowlist);
}
}

int ratelimit_is_on_allowlist(const char *allowlist, ioa_addr *addr) {
/* If no allowlist provided, return early */
if (!allowlist) {
return 0;
}

ur_addr_map_value_type ratelimit_ptr = 0;

TURN_MUTEX_LOCK(&rate_limit_allowlist_mutex);
if (ur_addr_map_get(rate_limit_allowlist_map, addr, &ratelimit_ptr)) {
TURN_MUTEX_UNLOCK(&rate_limit_allowlist_mutex);
return 1;
} else {
TURN_MUTEX_UNLOCK(&rate_limit_allowlist_mutex);
return 0;
}
Comment thread
e-lisa marked this conversation as resolved.
}

void ratelimit_add_node(ioa_addr *address) {
// copy address
Expand All @@ -63,7 +147,7 @@ void ratelimit_init_map() {
TURN_MUTEX_UNLOCK(&rate_limit_main_mutex);
}

int ratelimit_is_address_limited(ioa_addr *address, int max_requests, int window_seconds) {
int ratelimit_is_address_limited(ioa_addr *address, int max_requests, int window_seconds, const char *allowlist) {
/* Housekeeping, prune the map when ADDR_MAP_SIZE is hit and delete expired items */
turn_time_t current_time = turn_time();

Expand All @@ -85,39 +169,50 @@ int ratelimit_is_address_limited(ioa_addr *address, int max_requests, int window
ioa_addr *address_new = (ioa_addr *)malloc(sizeof(ioa_addr));
*address_new = *address;
addr_set_port(address_new, 0);
int retval = 0;

if (ur_addr_map_get(rate_limit_map, address_new, &ratelimit_ptr)) {
Comment thread
e-lisa marked this conversation as resolved.
Comment thread
e-lisa marked this conversation as resolved.
free(address_new);
ratelimit_entry *rateLimitEntry = (ratelimit_entry *)(void *)(ur_map_value_type)ratelimit_ptr;
TURN_MUTEX_LOCK(&(rateLimitEntry->mutex));

if (turn_time_before(current_time, rateLimitEntry->last_request_time)) {
/* Check if request is inside the ratelimit window; reset the count and request time */
rateLimitEntry->request_count = 1;
rateLimitEntry->last_request_time = current_time;
TURN_MUTEX_UNLOCK(&(rateLimitEntry->mutex));
return 0;
retval = 0;
goto end_ratelimit_entry;
} else if (rateLimitEntry->request_count < max_requests) {
/* Check if request count is below requests per window; increment the count */
if (rateLimitEntry->request_count < UINT32_MAX)
rateLimitEntry->request_count++;
rateLimitEntry->last_request_time = current_time;
TURN_MUTEX_UNLOCK(&(rateLimitEntry->mutex));
return 0;
retval = 0;
goto end_ratelimit_entry;
} else {
/* Before ratelimit, check allow list */
if(ratelimit_is_on_allowlist(allowlist, address_new)) {
Comment thread
sgodin marked this conversation as resolved.
retval = 0;
goto end_ratelimit_entry;
}

/* Request is outside of defined window and count, request is ratelimited */
if (rateLimitEntry->request_count < UINT32_MAX)
rateLimitEntry->request_count++;
rateLimitEntry->last_request_time = current_time;
retval = 1;

end_ratelimit_entry:
TURN_MUTEX_UNLOCK(&(rateLimitEntry->mutex));
return 1;
free(address_new);
return retval;
}
} else {
// New entry, allow response
TURN_MUTEX_LOCK(&rate_limit_main_mutex);
ratelimit_add_node(address_new);
free(address_new);
TURN_MUTEX_UNLOCK(&rate_limit_main_mutex);
return 0;
retval = 0;
}
return retval;
}
4 changes: 3 additions & 1 deletion src/server/ns_turn_ratelimit.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@
extern "C" {
#endif

int ratelimit_is_address_limited(ioa_addr *address, int max_requests, int window_seconds);
int ratelimit_is_address_limited(ioa_addr *address, int max_requests,
int window_seconds, const char *allowlist);
void ratelimit_add_node(ioa_addr *address);
int ratelimit_delete_expired(ur_map_value_type value);
void ratelimit_init_map(void);
void ratelimit_update_allowlist();
////// Rate limit for 401 Unauthorized //////

#define RATELIMIT_DEFAULT_MAX_REQUESTS_PER_WINDOW 1000
Expand Down
7 changes: 5 additions & 2 deletions src/server/ns_turn_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -3922,7 +3922,8 @@ static int handle_turn_command(turn_turnserver *server, ts_ur_super_session *ss,
}
if(err_code == 401 && *server->ratelimit_401_requests_per_window > 0) {
ioa_addr *rate_limit_address = get_remote_addr_from_ioa_socket(ss->client_socket);
if (ratelimit_is_address_limited(rate_limit_address, *server->ratelimit_401_requests_per_window, *server->ratelimit_401_window_seconds)) {
if (ratelimit_is_address_limited(rate_limit_address, *server->ratelimit_401_requests_per_window,
*server->ratelimit_401_window_seconds, server->ratelimit_401_allowlist)) {
no_response = 1;
char raddr[129];
addr_to_string_no_port(rate_limit_address, (unsigned char *)raddr);
Expand Down Expand Up @@ -4930,7 +4931,8 @@ void init_turn_server(turn_turnserver *server, turnserver_id id, int verbose, io
const char *acme_redirect, ALLOCATION_DEFAULT_ADDRESS_FAMILY allocation_default_address_family,
vintp log_binding, vintp no_stun_backward_compatibility,
vintp response_origin_only_with_rfc5780,
vintp ratelimit_401_requests_per_window, vintp ratelimit_401_window_seconds) {
vintp ratelimit_401_requests_per_window, vintp ratelimit_401_window_seconds,
const char *ratelimit_401_allowlist) {

if (!server)
return;
Expand Down Expand Up @@ -5013,6 +5015,7 @@ void init_turn_server(turn_turnserver *server, turnserver_id id, int verbose, io

server->ratelimit_401_requests_per_window = ratelimit_401_requests_per_window;
server->ratelimit_401_window_seconds = ratelimit_401_window_seconds;
server->ratelimit_401_allowlist = ratelimit_401_allowlist;
}

ioa_engine_handle turn_server_get_engine(turn_turnserver *s) {
Expand Down
4 changes: 3 additions & 1 deletion src/server/ns_turn_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ struct _turn_turnserver {

vintp ratelimit_401_requests_per_window;
vintp ratelimit_401_window_seconds;
const char *ratelimit_401_allowlist;
};

const char *get_version(turn_turnserver *server);
Expand All @@ -235,7 +236,8 @@ void init_turn_server(turn_turnserver *server, turnserver_id id, int verbose, io
allocate_bps_cb allocate_bps_func, int oauth, const char *oauth_server_name,
const char *acme_redirect, ALLOCATION_DEFAULT_ADDRESS_FAMILY allocation_default_address_family,
vintp log_binding, vintp no_stun_backward_compatibility, vintp response_origin_only_with_rfc5780,
vintp ratelimit_401_requests_per_window, vintp ratelimit_401_window_seconds);
vintp ratelimit_401_requests_per_window, vintp ratelimit_401_window_seconds,
const char *ratelimit_401_allowlist);

ioa_engine_handle turn_server_get_engine(turn_turnserver *s);

Expand Down