Added query throttling for network ranges

This commit is contained in:
Markus Makela
2014-11-03 11:36:13 +02:00
parent b7f9819407
commit e275b5e533

View File

@ -22,20 +22,23 @@
* *
* A filter that acts as a firewall, denying queries that do not meet a set requirements. * A filter that acts as a firewall, denying queries that do not meet a set requirements.
* *
* This filter uses "rules" to define the blcking parameters. To configure rules into the configuration file, * This filter uses "rules" to define the blcking parameters. Write the rules to a separate file and
* give each rule a unique name and assing the rule contents by passing a string enclosed in quotes. * set the path to the file:
*
* rules=<path to file>
*
* *
* For example, to define a rule denying users from accessing the column 'salary' between 15:00 and 17:00, the following is needed in the configuration file: * For example, to define a rule denying users from accessing the column 'salary' between 15:00 and 17:00, the following is needed in the configuration file:
* *
* rule1="rule block_salary deny columns salary at_times 15:00:00-17:00:00" * rule block_salary deny columns salary at_times 15:00:00-17:00:00
* *
* To apply this rule to users John, connecting from any address, and Jane, connecting from the address 192.168.0.1, use the following: * To apply this rule to users John, connecting from any address, and Jane, connecting from the address 192.168.0.1, use the following:
* *
* rule2="users John@% Jane@192.168.0.1 match any rules block_salary" * users John@% Jane@192.168.0.1 match any rules block_salary
* *
* Rule syntax TODO: query type restrictions, update the documentation * Rule syntax TODO: query type restrictions, update the documentation
* *
* rule NAME deny|allow [wildcard | columns VALUE ... | regex REGEX] [at_times VALUE...] * rule NAME deny|allow [wildcard | columns VALUE ... | regex REGEX | limit_queries COUNT TIMEPERIOD HOLDOFF] [at_times VALUE...]
*/ */
#include <my_config.h> #include <my_config.h>
#include <stdint.h> #include <stdint.h>
@ -108,6 +111,7 @@ typedef enum{
typedef enum { typedef enum {
RT_UNDEFINED, RT_UNDEFINED,
RT_COLUMN, RT_COLUMN,
RT_THROTTLE,
RT_PERMISSION, RT_PERMISSION,
RT_WILDCARD, RT_WILDCARD,
RT_REGEX RT_REGEX
@ -127,6 +131,16 @@ typedef struct timerange_t{
struct tm end; struct tm end;
}TIMERANGE; }TIMERANGE;
typedef struct queryspeed_t{
time_t first_query;
time_t triggered;
double period;
double cooldown;
int count;
int limit;
}QUERYSPEED;
/** /**
* A structure used to identify individual rules and to store their contents * A structure used to identify individual rules and to store their contents
* *
@ -411,6 +425,11 @@ int get_octet(char* str)
} }
/**
* Parses a string that contains an IP address and converts the last octet to '%'
* @param str String to parse
* @return Pointer to modified string
*/
char* next_ip_class(char* str) char* next_ip_class(char* str)
{ {
assert(str != NULL); assert(str != NULL);
@ -738,17 +757,17 @@ void link_rules(char* rule, FW_INSTANCE* instance)
USER* user; USER* user;
RULELIST *tl = NULL,*tail = NULL; RULELIST *tl = NULL,*tail = NULL;
if((user = hashtable_fetch(instance->htable,userptr)) == NULL){ if((user = (USER*)hashtable_fetch(instance->htable,userptr)) == NULL){
/**New user*/ /**New user*/
user = calloc(1,sizeof(USER)); user = (USER*)calloc(1,sizeof(USER));
if(user == NULL){ if(user == NULL){
return; return;
} }
} }
user->name = strdup(userptr); user->name = (char*)strdup(userptr);
tl = rlistdup(rulelist); tl = (RULELIST*)rlistdup(rulelist);
tail = tl; tail = tl;
while(tail && tail->next){ while(tail && tail->next){
tail = tail->next; tail = tail->next;
@ -862,12 +881,15 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
else if(strcmp(tok,"regex") == 0) else if(strcmp(tok,"regex") == 0)
{ {
bool escaped = false; bool escaped = false;
regex_t *re;
char* start = tok, *str;
tok += 6; tok += 6;
while(isspace(*tok) || *tok == '\'' || *tok == '"'){ while(isspace(*tok) || *tok == '\'' || *tok == '"'){
tok++; tok++;
} }
char* start = tok, *str;
while(true){ while(true){
if((*tok == '\'' || *tok == '"') && !escaped){ if((*tok == '\'' || *tok == '"') && !escaped){
@ -878,10 +900,15 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
} }
str = calloc(((tok - start) + 1),sizeof(char)); str = calloc(((tok - start) + 1),sizeof(char));
re = (regex_t*)malloc(sizeof(regex_t));
if(re == NULL || str == NULL){
skygw_log_write_flush(LOGFILE_ERROR, "Fatal Error: malloc returned NULL.");
return;
}
memcpy(str, start, (tok-start)); memcpy(str, start, (tok-start));
regex_t *re = malloc(sizeof(regex_t));
if(regcomp(re, str,REG_NOSUB)){ if(regcomp(re, str,REG_NOSUB)){
skygw_log_write(LOGFILE_ERROR, "fwfilter: Invalid regular expression '%s'.", str); skygw_log_write(LOGFILE_ERROR, "fwfilter: Invalid regular expression '%s'.", str);
@ -893,6 +920,21 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
free(str); free(str);
} }
else if(strcmp(tok,"limit_queries") == 0)
{
QUERYSPEED* qs = (QUERYSPEED*)calloc(1,sizeof(QUERYSPEED));
tok = strtok(NULL," ");
qs->limit = atoi(tok);
tok = strtok(NULL," ");
qs->period = atof(tok);
tok = strtok(NULL," ");
qs->cooldown = atof(tok);
ruledef->type = RT_THROTTLE;
ruledef->data = (void*)qs;
}
tok = strtok(NULL," ,"); tok = strtok(NULL," ,");
} }
@ -916,7 +958,7 @@ static FILTER *
createInstance(char **options, FILTER_PARAMETER **params) createInstance(char **options, FILTER_PARAMETER **params)
{ {
FW_INSTANCE *my_instance; FW_INSTANCE *my_instance;
int i,paramc; int i;
HASHTABLE* ht; HASHTABLE* ht;
STRLINK *ptr,*tmp; STRLINK *ptr,*tmp;
char *filename = NULL, *nl; char *filename = NULL, *nl;
@ -1190,6 +1232,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
RULELIST *rulelist = NULL; RULELIST *rulelist = NULL;
USER* user = NULL; USER* user = NULL;
STRLINK* strln = NULL; STRLINK* strln = NULL;
QUERYSPEED* queryspeed;
int qlen; int qlen;
ipaddr = strdup(dcb->remote); ipaddr = strdup(dcb->remote);
@ -1332,6 +1375,45 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
} }
break; break;
case RT_THROTTLE:
queryspeed = (QUERYSPEED*)rulelist->rule->data;
if(queryspeed->count > queryspeed->limit)
{
queryspeed->triggered = time_now;
queryspeed->count = 0;
accept = false;
skygw_log_write(LOGFILE_TRACE,
"fwfilter: rule '%s': query limit triggered (%d queries in %f seconds), denying queries from user %s for %f seconds.",
rulelist->rule->name,
queryspeed->limit,
queryspeed->period,
uname_addr,
queryspeed->cooldown);
}
else if(difftime(time_now,queryspeed->triggered) < queryspeed->cooldown)
{
double blocked_for = queryspeed->cooldown - difftime(time_now,queryspeed->triggered);
sprintf(emsg,"Queries denied for %f seconds",uname_addr,blocked_for);
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': user %s denied for %f seconds",rulelist->rule->name,uname_addr,blocked_for);
msg = strdup(emsg);
accept = false;
}
else if(difftime(time_now,queryspeed->first_query) < queryspeed->period)
{
queryspeed->count++;
}
else
{
queryspeed->first_query = time_now;
}
break;
default: default:
break; break;
@ -1411,6 +1493,49 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
} }
break; break;
case RT_THROTTLE:
queryspeed = (QUERYSPEED*)rulelist->rule->data;
if(queryspeed->count > queryspeed->limit)
{
skygw_log_write(LOGFILE_TRACE,
"fwfilter: rule '%s': query limit triggered (%d queries during the last %f seconds), denying queries from user %s for %f seconds.",
rulelist->rule->name,
queryspeed->count,
queryspeed->period,
uname_addr,
queryspeed->cooldown);
queryspeed->triggered = time_now;
queryspeed->count = 0;
accept = false;
}
else if(difftime(queryspeed->triggered,time_now) < queryspeed->cooldown)
{
double blocked_for = queryspeed->cooldown - difftime(queryspeed->triggered,time_now);
sprintf(emsg,"Access denied for user %s for %f seconds.",uname_addr,blocked_for);
msg = strdup(emsg);
skygw_log_write(LOGFILE_TRACE,
"fwfilter: rule '%s': user %s blocked for %f seconds.",
rulelist->rule->name,
uname_addr,
blocked_for);
accept = false;
}
else if(difftime(time_now,queryspeed->first_query) < queryspeed->period)
{
queryspeed->count++;
}
else
{
queryspeed->first_query = time_now;
}
break;
default:
break;
} }