(feat) user rate-limiting support for all SOGo requests

pull/203/head
Ludovic Marcotte 2016-03-18 11:03:45 -04:00
parent bb04ce39d6
commit 9d6ab2df33
6 changed files with 176 additions and 0 deletions

View File

@ -715,6 +715,21 @@ Default value is `0`, or disabled.
|Number of seconds, default to `300` (or 5 minutes). Note that
_SOGoCacheCleanupInterval_ must be set to a value equal or higher than
_SOGoFailedLoginBlockInterval_.
|S |SOGoMaximumRequestCount
|Parameter used to control the number of requests a user can send to the SOGo
server in _SOGoMaximumRequestInterval_ seconds or more. If conditions are met
or exceeded, the user will not be able to perform requests on the SOGo server
for _SOGoRequestBlockInterval_ seconds and will receive 429 HTTP responses for
any requests being made. Default value is 0, or disabled
|S |SOGoMaximumRequestInterval
|Number of seconds, defaults to `30`.
|S |SOGoRequestBlockInterval
|Number of seconds, defaults to 300 (or 5 minutes). Note that _SOGoCacheCleanupInterval_
must be set to a value equal or higher than _SOGoRequestBlockInterval_.
|=======================================================================
Authentication using LDAP

View File

@ -476,6 +476,8 @@ static BOOL debugLeaks;
static BOOL debugOn = NO;
WOResponse *resp;
NSDate *startDate;
NSString *path;
NSTimeInterval timeDelta;
if (debugRequests)
@ -499,6 +501,62 @@ static BOOL debugLeaks;
}
#endif
// We check for rate-limiting settings - ignore anything actually
// sent to /SOGo/ (so unauthenticated requests).
path = [_request requestHandlerPath];
if ([path length])
{
NSDictionary *requestCount;
NSString *username;
NSRange r;
r = [path rangeOfString: @"/"];
username = [path substringWithRange: NSMakeRange(0, r.location)];
requestCount = [cache requestCountForLogin: username];
if (requestCount)
{
SOGoSystemDefaults *sd;
unsigned int current_time, start_time, delta, block_time, request_count;
sd = [SOGoSystemDefaults sharedSystemDefaults];
current_time = [[NSCalendarDate date] timeIntervalSince1970];
start_time = [[requestCount objectForKey: @"InitialDate"] unsignedIntValue];
delta = current_time - start_time;
block_time = [sd requestBlockInterval];
request_count = [[requestCount objectForKey: @"RequestCount"] intValue];
if ( request_count >= [sd maximumRequestCount] &&
delta < [sd maximumRequestInterval] &&
delta <= block_time )
{
resp = [WOResponse responseWithRequest: _request];
[resp setStatus: 429];
return resp;
}
if (delta > block_time)
{
[cache setRequestCount: 1
forLogin: username
interval: current_time];
}
else
[cache setRequestCount: (request_count+1)
forLogin: username
interval: start_time];
}
else
{
[cache setRequestCount: 1
forLogin: username
interval: 0];
}
}
resp = [super dispatchRequest: _request];
[cache killCache];

View File

@ -141,6 +141,15 @@
forPath: (NSString *) thePath;
- (NSMutableDictionary *) aclsForPath: (NSString *) thePath;
//
// SOGo rate-limiting
//
- (void) setRequestCount: (int) theCount
forLogin: (NSString *) theLogin
interval: (unsigned int) theInterval;
- (NSDictionary *) requestCountForLogin: (NSString *) theLogin;
@end
#endif /* SOGOCACHE_H */

View File

@ -727,5 +727,58 @@ static memcached_st *handle = NULL;
}
//
// SOGo request count for rate-limiting
//
- (void) setRequestCount: (int) theCount
forLogin: (NSString *) theLogin
interval: (unsigned int) theInterval
{
NSMutableDictionary *d;
NSNumber *count;
if (theCount)
{
count = [NSNumber numberWithInt: theCount];
d = [NSMutableDictionary dictionaryWithDictionary: [self requestCountForLogin: theLogin]];
if (![d objectForKey: @"InitialDate"] || theInterval == 0)
[d setObject: [NSNumber numberWithUnsignedInt: [[NSCalendarDate date] timeIntervalSince1970]] forKey: @"InitialDate"];
else
[d setObject: [NSNumber numberWithUnsignedInt: theInterval] forKey: @"InitialDate"];
[d setObject: count forKey: @"RequestCount"];
[self _cacheValues: [d jsonRepresentation]
ofType: @"requestcount"
forKey: theLogin];
}
else
{
[self removeValueForKey: [NSString stringWithFormat: @"%@+failedlogins", theLogin]];
}
}
//
// Returns a dictionary with two keys/values
//
// RequestCount ->
// InitialDate ->
//
- (NSDictionary *) requestCountForLogin: (NSString *) theLogin
{
NSDictionary *d;
NSString *s;
s = [self _valuesOfType: @"requestcount" forKey: theLogin];
d = nil;
if (s)
{
d = [s objectFromJSONString];
}
return d;
}
@end

View File

@ -97,6 +97,11 @@
- (int) maximumSubmissionInterval;
- (int) messageSubmissionBlockInterval;
- (int) maximumRequestCount;
- (int) maximumRequestInterval;
- (int) requestBlockInterval;
- (int) maximumPingInterval;
- (int) maximumSyncInterval;
- (int) internalSyncInterval;

View File

@ -598,6 +598,42 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
return v;
}
//
// SOGo rate-limiting
//
- (int) maximumRequestCount
{
return [self integerForKey: @"SOGoMaximumRequestCount"];
}
- (int) maximumRequestInterval
{
int v;
v = [self integerForKey: @"SOGoMaximumRequestInterval"];
if (!v)
v = 30;
return v;
}
- (int) requestBlockInterval
{
int v;
v = [self integerForKey: @"SOGoRequestBlockInterval"];
if (!v)
v = 300;
return v;
}
//
// SOGo EAS settings
//
- (int) maximumPingInterval
{
int v;