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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).


## [0.24.0] - 2022-07-08

### Added
- Add a `configs` attribute to Bouncer class

## [0.23.0] - 2022-07-07

### Added
Expand Down
46 changes: 20 additions & 26 deletions src/AbstractBounce.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,44 +117,37 @@ protected function initLoggerHelper(string $logDirectoryPath, string $loggerName
/**
* Handle X-Forwarded-For HTTP header to retrieve the IP to bounce
*
* @param $ip
* @return false|mixed
* @param string $ip
* @param array $configs
* @return string
*/
protected function handleForwardedFor($ip)
protected function handleForwardedFor(string $ip, array $configs): string
{
if (empty($this->settings['forced_test_forwarded_ip'])) {
$forwardedIp = null;
if (empty($configs['forced_test_forwarded_ip'])) {
$XForwardedForHeader = $this->getHttpRequestHeader('X-Forwarded-For');
if (null !== $XForwardedForHeader) {
$ipList = array_map('trim', array_values(array_filter(explode(',', $XForwardedForHeader))));
$forwardedIp = end($ipList);
if ($this->shouldTrustXforwardedFor($ip)) {
$ip = $forwardedIp;
} else {
$this->logger->warning('', [
'type' => 'NON_AUTHORIZED_X_FORWARDED_FOR_USAGE',
'original_ip' => $ip,
'x_forwarded_for_ip' => $forwardedIp,
]);
}
}
} else if ($this->settings['forced_test_forwarded_ip'] === Constants::X_FORWARDED_DISABLED) {
} else if ($configs['forced_test_forwarded_ip'] === Constants::X_FORWARDED_DISABLED) {
$this->logger->debug('', [
'type' => 'DISABLED_X_FORWARDED_FOR_USAGE',
'original_ip' => $ip,
]);
} else {
$forwardedIp = $this->settings['forced_test_forwarded_ip'];
if ($this->shouldTrustXforwardedFor($ip)) {
$ip = $forwardedIp;
} else {
$this->logger->warning('', [
'type' => 'NON_AUTHORIZED_TEST_X_FORWARDED_FOR_USAGE',
'original_ip' => $ip,
'x_forwarded_for_ip_for_test' => $forwardedIp,
]);
}
$forwardedIp = (string) $configs['forced_test_forwarded_ip'];
}

if (is_string($forwardedIp) && $this->shouldTrustXforwardedFor($ip)) {
$ip = $forwardedIp;
} else {
$this->logger->warning('', [
'type' => 'NON_AUTHORIZED_X_FORWARDED_FOR_USAGE',
'original_ip' => $ip,
'x_forwarded_for_ip' => is_string($forwardedIp) ? $forwardedIp : 'type not as expected',
]);
}
return $ip;
}

Expand All @@ -171,9 +164,10 @@ protected function bounceCurrentIp(): void
if (!$this->bouncer) {
throw new BouncerException('Bouncer must be instantiated to bounce an IP.');
}
$configs = $this->bouncer->getConfigs();
// Retrieve the current IP (even if it is a proxy IP) or a testing IP
$ip = !empty($this->settings['forced_test_ip']) ? $this->settings['forced_test_ip'] : $this->getRemoteIp();
$ip = $this->handleForwardedFor($ip);
$ip = !empty($configs['forced_test_ip']) ? $configs['forced_test_ip'] : $this->getRemoteIp();
$ip = $this->handleForwardedFor($ip, $configs);
$remediation = $this->bouncer->getRemediationForIp($ip);
$this->handleRemediation($remediation, $ip);
} catch (Exception $e) {
Expand Down
61 changes: 38 additions & 23 deletions src/Bouncer.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,15 @@ class Bouncer
/** @var int */
private $maxRemediationLevelIndex = 0;

/** @var array */
private $configs = [];

public function __construct(
TagAwareAdapterInterface $cacheAdapter = null,
LoggerInterface $logger = null,
ApiCache $apiCache = null
) {
LoggerInterface $logger = null,
ApiCache $apiCache = null
)
{
if (!$logger) {
$logger = new Logger('null');
$logger->pushHandler(new NullHandler());
Expand All @@ -50,6 +54,16 @@ public function __construct(
$this->apiCache = $apiCache ?: new ApiCache($logger, new ApiClient($logger), $cacheAdapter);
}

/**
* Retrieve Bouncer configurations
*
* @return array
*/
public function getConfigs(): array
{
return $this->configs;
}

/**
* Configure this instance.
*
Expand All @@ -59,33 +73,33 @@ public function __construct(
*/
public function configure(array $config): void
{
// Process input configuration.
// Process and validate input configuration.
$configuration = new Configuration();
$processor = new Processor();
$finalConfig = $processor->processConfiguration($configuration, [$config]);
$this->configs = $processor->processConfiguration($configuration, [$config]);
/** @var int */
$index = array_search(
$finalConfig['max_remediation_level'],
$this->configs['max_remediation_level'],
Constants::ORDERED_REMEDIATIONS
);
$this->maxRemediationLevelIndex = $index;
$cacheDurations = [
'clean_ip_cache_duration' => $finalConfig['clean_ip_cache_duration'],
'bad_ip_cache_duration' => $finalConfig['bad_ip_cache_duration'],
'captcha_cache_duration' => $finalConfig['captcha_cache_duration'],
'geolocation_cache_duration' => $finalConfig['geolocation_cache_duration'],
'clean_ip_cache_duration' => $this->configs['clean_ip_cache_duration'],
'bad_ip_cache_duration' => $this->configs['bad_ip_cache_duration'],
'captcha_cache_duration' => $this->configs['captcha_cache_duration'],
'geolocation_cache_duration' => $this->configs['geolocation_cache_duration'],
];

// Configure Api Cache.
$this->apiCache->configure(
$finalConfig['stream_mode'],
$finalConfig['api_url'],
$finalConfig['api_timeout'],
$finalConfig['api_user_agent'],
$finalConfig['api_key'],
$this->configs['stream_mode'],
$this->configs['api_url'],
$this->configs['api_timeout'],
$this->configs['api_user_agent'],
$this->configs['api_key'],
$cacheDurations,
$finalConfig['fallback_remediation'],
$finalConfig['geolocation']
$this->configs['fallback_remediation'],
$this->configs['geolocation']
);
}

Expand Down Expand Up @@ -115,7 +129,7 @@ private function capRemediationLevel(string $remediation): string
*
* @return string the remediation to apply (ex: 'ban', 'captcha', 'bypass')
*
* @throws InvalidArgumentException
* @throws InvalidArgumentException|\Psr\Cache\CacheException
*/
public function getRemediationForIp(string $ip): string
{
Expand Down Expand Up @@ -154,11 +168,12 @@ public static function getAccessForbiddenHtmlTemplate(array $config): string
* The input $config should match the TemplateConfiguration input format.
*/
public static function getCaptchaHtmlTemplate(
bool $error,
bool $error,
string $captchaImageSrc,
string $captchaResolutionFormUrl,
array $config
): string {
array $config
): string
{
// Process template configuration.
$configuration = new TemplateConfiguration();
$processor = new Processor();
Expand All @@ -176,7 +191,7 @@ public static function getCaptchaHtmlTemplate(
*
* @return array "count": number of decisions added, "errors": decisions not added
*
* @throws InvalidArgumentException
* @throws InvalidArgumentException|\Psr\Cache\CacheException
*/
public function warmBlocklistCacheUp(): array
{
Expand All @@ -189,7 +204,7 @@ public function warmBlocklistCacheUp(): array
*
* @return array Number of deleted and new decisions, and errors when processing decisions
*
* @throws InvalidArgumentException
* @throws InvalidArgumentException|\Psr\Cache\CacheException
*/
public function refreshBlocklistCache(): array
{
Expand Down
2 changes: 1 addition & 1 deletion src/Constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Constants
public const DEFAULT_LAPI_URL = 'http://localhost:8080';

/** @var string The last version of this library */
public const VERSION = 'v0.23.0';
public const VERSION = 'v0.24.0';

/** @var string The user agent used to send request to LAPI */
public const BASE_USER_AGENT = 'PHP CrowdSec Bouncer/' . self::VERSION;
Expand Down
1 change: 1 addition & 0 deletions src/StandaloneBounce.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ public function getBouncerInstance(array $settings, bool $forceReload = false):
if ($this->bouncer && !$forceReload) {
return $this->bouncer;
}
$this->settings = array_merge($this->settings, $settings);
$bouncingLevel = $this->getStringSettings('bouncing_level');
$apiUserAgent = 'Standalone CrowdSec PHP Bouncer/' . Constants::VERSION;
$apiTimeout = $this->getIntegerSettings('api_timeout');
Expand Down