Skip to content
Snippets Groups Projects
Commit 32f56e64 authored by Matt Gaskey's avatar Matt Gaskey Committed by Vojislav Jovanovic
Browse files

Issue #3409793 by mgaskey, alexgreyhead, vaish: Allow administrators to...

Issue #3409793 by mgaskey, alexgreyhead, vaish: Allow administrators to specify IP addresses and ranges which should never be rate limited
parent 19f7a07b
Branches
Tags
1 merge request!5add IP address allowlist capabilities for rate limiting
......@@ -150,7 +150,9 @@ Install Crawler Rate Limit module
```
2. Configure Crawler Rate Limit. Add the following snippet to your
`settings.php` file.
`settings.php` file. Make sure to adjust all the values to match your
website's traffic. Feel free to omit/delete all optional sections that you
don't intend to use.
```php
// Below configuration uses a redis backend and will limit each
......@@ -193,7 +195,8 @@ Install Crawler Rate Limit module
// visitor (identified by combination of IP address + User-Agent string).
'requests' => 300,
];
// Autonomous system-level (ASN) regular traffic rate limit.
// Autonomous system-level (ASN) regular traffic rate limit. Optional.
// Requires geoip2/geoip2 and associated ASN Database.
// See https://github.com/maxmind/GeoIP2-php
// See https://dev.maxmind.com/geoip/docs/databases/asn#binary-database
......@@ -209,6 +212,22 @@ Install Crawler Rate Limit module
// See https://dev.maxmind.com/geoip/updating-databases
'database' => '/var/www/example.com/private/geoip2/GeoLite2-ASN.mmdb',
];
// Allow specified IP addresses to bypass rate limiting. Optional. Useful
// if your website is maintained by a number of users all accessing the site
// from the same location, using the same browsers, and at the same time.
// Allowlist can contain:
// - IPv4 addresses or subnets in CIDR notation
// - IPv6 addresses or subnets in CIDR notation
// Default value: empty array.
//
// For example:
// $settings['crawler_rate_limit.settings']['ip_address_allowlist'] = [
// '127.0.0.1',
// '10.0.0.0/8',
// '192.168.1.0/24',
// ];
$settings['crawler_rate_limit.settings']['ip_address_allowlist'] = [];
```
- Make sure to adjust the value for `backend` to match the one you decided
......
......@@ -8,6 +8,7 @@ use GeoIp2\Database\Reader;
use Jaybizzle\CrawlerDetect\CrawlerDetect;
use RateLimit\Exception\LimitExceeded;
use RateLimit\Rate;
use Symfony\Component\HttpFoundation\IpUtils;
use Symfony\Component\HttpFoundation\Request;
/**
......@@ -120,6 +121,20 @@ class RateLimitManager implements RateLimitManagerInterface {
*/
private CrawlerDetect $crawlerDetect;
/**
* IP addresses or subnets which are allowed to bypass rate limiting.
*
* @var array
*/
protected array $ipAddressAllowlist;
/**
* IP address of the client.
*
* @var string|null
*/
protected ?string $clientIp;
/**
* Class constructor.
*
......@@ -144,6 +159,9 @@ class RateLimitManager implements RateLimitManagerInterface {
$this->requestsBots = $settings['bot_traffic']['requests'];
$this->limitRegular = $settings['limit_regular'];
$this->limitRegularAsn = $settings['limit_regular_asn'];
$this->ipAddressAllowlist = $settings['ip_address_allowlist'];
// Set retryAfter to higher interval value until we figure out which type of
// request we are handling in limit() method.
$this
......@@ -193,6 +211,13 @@ class RateLimitManager implements RateLimitManagerInterface {
return FALSE;
}
$this->clientIp = $request->getClientIp();
// Check if the request is from an IP address which is on the allowlist.
if (IpUtils::checkIp($this->clientIp, $this->ipAddressAllowlist)) {
return FALSE;
}
// If crawler, enforce crawler limit.
if ($this->crawlerDetect->isCrawler()) {
$this->retryAfter = $this->intervalBots;
......@@ -245,7 +270,7 @@ class RateLimitManager implements RateLimitManagerInterface {
// As identifier we use combination of IP address and User Agent string.
// @todo Change hashing algorithm to "xxh3" once the support for PHP 7.4
// is removed.
$identifier = hash('crc32c', $request->getClientIp() . $request->headers->get('user-agent'));
$identifier = hash('crc32c', $this->clientIp . $request->headers->get('user-agent'));
if ($this->limitReached($identifier, $this->requestsRegular, $this->intervalRegular)) {
return TRUE;
}
......@@ -258,7 +283,7 @@ class RateLimitManager implements RateLimitManagerInterface {
$this->retryAfter = $this->intervalRegularAsn;
try {
$reader = new Reader($this->asnDbPath);
$record = $reader->asn($request->getClientIp());
$record = $reader->asn($this->clientIp);
$identifier = 'asn-' . $record->autonomousSystemNumber;
return $this
->limitReached($identifier, $this->requestsRegularAsn, $this->intervalRegularAsn);
......@@ -354,6 +379,7 @@ class RateLimitManager implements RateLimitManagerInterface {
'limit_regular' => FALSE,
'limit_regular_asn' => FALSE,
'deprecated' => FALSE,
'ip_address_allowlist' => [],
];
// If user-provided values for interval and requests can't be converted to
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment