Skip to content

Commit 43daaa0

Browse files
authored
Merge pull request #3 from crowdsecurity/settings-validation
- add redis and memcached connection checks - lint pass
2 parents e04ea28 + 35b6e9f commit 43daaa0

15 files changed

+389
-222
lines changed

crowdsec.php

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,35 @@
44
Plugin URI: https://www.crowdsec.net/
55
Description: Safer Together. Protect your WordPress application with CrowdSec.
66
Tags: security, firewall, malware scanner, two factor authentication, captcha, waf, web app firewall, mfa, 2fa
7-
Version 0.1.0
7+
Version 0.2.0
88
Author: CrowdSec
99
Author URI: https://www.crowdsec.net/
1010
Github: https://github.com/crowdsecurity/cs-wordpress-blocker
1111
License: MIT
1212
Requires PHP: 7.2
13-
Stable tag: 0.1.0
13+
Stable tag: 0.2.0
1414
Text Domain: crowdsec-wp
1515
*/
1616

17-
1817
session_start();
19-
require_once __DIR__ . '/vendor/autoload.php';
18+
require_once __DIR__.'/vendor/autoload.php';
19+
20+
define('CROWDSEC_PLUGIN_PATH', __DIR__);
21+
define('CROWDSEC_PLUGIN_URL', plugin_dir_url(__FILE__));
2022

2123
class WordpressCrowdSecBouncerException extends \RuntimeException
2224
{
2325
}
2426

25-
require_once __DIR__ . '/inc/constants.php';
26-
require_once __DIR__ . '/inc/scheduling.php';
27-
require_once __DIR__ . '/inc/plugin-setup.php';
27+
require_once __DIR__.'/inc/constants.php';
28+
require_once __DIR__.'/inc/check-config.php';
29+
require_once __DIR__.'/inc/scheduling.php';
30+
require_once __DIR__.'/inc/plugin-setup.php';
2831
register_activation_hook(__FILE__, 'activate_crowdsec_plugin');
2932
register_deactivation_hook(__FILE__, 'deactivate_crowdsec_plugin');
30-
require_once __DIR__ . '/inc/bouncer-instance.php';
31-
require_once __DIR__ . '/inc/admin/init.php';
32-
require_once __DIR__ . '/inc/bounce-current-ip.php';
33+
require_once __DIR__.'/inc/bouncer-instance.php';
34+
require_once __DIR__.'/inc/admin/init.php';
35+
require_once __DIR__.'/inc/bounce-current-ip.php';
3336

3437
// Apply bouncing
35-
add_action('plugins_loaded', "safelyBounceCurrentIp");
38+
add_action('plugins_loaded', 'safelyBounceCurrentIp');

inc/admin/advanced-settings.php

Lines changed: 90 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?php
22

3+
use CrowdSecBouncer\BouncerException;
34
use CrowdSecBouncer\Constants;
45

56
function adminAdvancedSettings()
@@ -20,24 +21,26 @@ function adminAdvancedSettings()
2021
unscheduleBlocklistRefresh();
2122
}, '
2223
<p>With the stream mode, every decision is retrieved in an asynchronous way. 3 advantages: <br>&nbsp;1) Inivisible latency when loading pages<br>&nbsp;2) The IP verifications works even if your CrowdSec is not reachable.<br>&nbsp;3) The API can never be overloaded by the WordPress traffic</p>
23-
<p>Note: This method has one limit: for maximum 60 seconds, all the new decisions may not be taken into account.</p>' .
24-
(get_option("crowdsec_stream_mode") ?
24+
<p>Note: This method has one limit: for maximum 60 seconds, all the new decisions may not be taken into account.</p>'.
25+
(get_option('crowdsec_stream_mode') ?
2526
'<p><input style="margin-right:10px" type="button" value="Refresh the cache now" class="button button-secondary button-small" onclick="document.getElementById(\'crowdsec_ation_refresh_cache\').submit();"></p>' :
2627
'<p><input style="margin-right:10px" type="button" disabled="disabled" value="Refresh the cache now" class="button button-secondary button-small"></p>'));
2728

2829
// Field "crowdsec_stream_mode_refresh_frequency"
2930
addFieldString('crowdsec_stream_mode_refresh_frequency', 'Resync decisions each<br>(stream mode only)', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_stream_mode', function ($input) {
30-
$input = (int)$input;
31+
$input = (int) $input;
3132
if ($input < 60) {
3233
$input = 60;
33-
add_settings_error("Resync decisions each", "crowdsec_error", 'The "Resync decisions each" value should be more than 60sec (WP_CRON_LOCK_TIMEOUT). We just reset the frequency to 60 seconds.');
34+
add_settings_error('Resync decisions each', 'crowdsec_error', 'The "Resync decisions each" value should be more than 60sec (WP_CRON_LOCK_TIMEOUT). We just reset the frequency to 60 seconds.');
35+
3436
return $input;
3537
}
3638

3739
// Update wp-cron schedule.
38-
if ((bool)get_option("crowdsec_stream_mode")) {
40+
if ((bool) get_option('crowdsec_stream_mode')) {
3941
scheduleBlocklistRefresh();
4042
}
43+
4144
return $input;
4245
}, ' seconds. <p>Our advice is 60 seconds (according to WP_CRON_LOCK_TIMEOUT).</p>', '...', 'width: 115px;', 'number');
4346

@@ -46,67 +49,115 @@ function adminAdvancedSettings()
4649
********************/
4750

4851
add_settings_section('crowdsec_admin_advanced_cache', 'Caching configuration <input style="margin-left: 7px;margin-top: -3px;" type="button" value="Clear now" class="button button-secondary button-small" onclick="if (confirm(\'Are you sure you want to completely clear the cache?\')) document.getElementById(\'crowdsec_ation_clear_cache\').submit();">', function () {
49-
?>
52+
?>
5053
<p>Polish the decisions cache settings by selecting the best technology or the cache durations best suited to your use.</p>
5154
<?php
5255
}, 'crowdsec_advanced_settings');
5356

57+
// Field "crowdsec_redis_dsn"
58+
addFieldString('crowdsec_redis_dsn', 'Redis DSN<br>(if applicable)', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_cache', function ($input) {
59+
// TODO P1 block remove if cache set to redis
60+
// TODO P1 Display an error in all sections (settings and advanced) when a cache config can not work.
61+
return $input;
62+
}, '<p>Fill in this field only if you have chosen the Redis cache.<br>Example of DSN: redis://localhost:6379.', 'redis://...', '');
63+
64+
// Field "crowdsec_memcached_dsn"
65+
addFieldString('crowdsec_memcached_dsn', 'Memcached DSN<br>(if applicable)', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_cache', function ($input) {
66+
// TODO P1 block remove if cache set to memcached
67+
// TODO P1 Display an error in all sections (settings and advanced) when a cache config can not work.
68+
return $input;
69+
}, '<p>Fill in this field only if you have chosen the Memcached cache.<br>Example of DSN: memcached://localhost:11211.', 'memcached://...', '');
70+
5471
// Field "crowdsec_cache_system"
5572
addFieldSelect('crowdsec_cache_system', 'Technology', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_cache', function ($input) {
5673
if (!in_array($input, [CROWDSEC_CACHE_SYSTEM_PHPFS, CROWDSEC_CACHE_SYSTEM_REDIS, CROWDSEC_CACHE_SYSTEM_MEMCACHED])) {
5774
$input = CROWDSEC_CACHE_SYSTEM_PHPFS;
58-
add_settings_error("Technology", "crowdsec_error", "Technology: Incorrect cache technology selected.");
75+
add_settings_error('Technology', 'crowdsec_error', 'Technology: Incorrect cache technology selected.');
5976
}
6077

61-
$bouncer = getBouncerInstance();
62-
$bouncer->clearCache();
63-
$message = __('Cache system changed. Previous cache data has been cleared.');
64-
65-
// Update wp-cron schedule if stream mode is enabled
66-
if ((bool)get_option("crowdsec_stream_mode")) {
67-
$bouncer = getBouncerInstance($input); // Reload bouncer instance with the new cache system
68-
$result = $bouncer->warmBlocklistCacheUp();
69-
$message .= __(' As the stream mode is enabled, the cache has just been warmed up, ' . ($result > 0 ? 'there are now ' . $result . ' decisions' : 'there is now ' . $result . ' decision') . ' in cache.');
70-
scheduleBlocklistRefresh();
78+
try {
79+
$bouncer = getBouncerInstance();
80+
try {
81+
$bouncer->clearCache();
82+
} catch (BouncerException $e) {
83+
$cacheSystem = esc_attr(get_option('crowdsec_cache_system'));
84+
switch ($cacheSystem) {
85+
case CROWDSEC_CACHE_SYSTEM_MEMCACHED:
86+
throw new WordpressCrowdSecBouncerException('Unable to connect Memcached.'.
87+
' Please fix the Memcached DSN or select another cache technology.');
88+
break;
89+
90+
case CROWDSEC_CACHE_SYSTEM_REDIS:
91+
throw new WordpressCrowdSecBouncerException('Unable to connect Redis.'.
92+
' Please fix the Redis DSN or select another cache technology.');
93+
default:
94+
throw new WordpressCrowdSecBouncerException('Unable to connect the cache system: '.$e->getMessage());
95+
}
96+
}
97+
98+
$message = __('Cache system changed. Previous cache data has been cleared.');
99+
} catch (WordpressCrowdSecBouncerException $e) {
71100
}
72101

73-
AdminNotice::displaySuccess($message);
102+
try {
103+
// Reload bouncer instance with the new cache system and so test if dsn is correct.
104+
getCacheAdapterInstance($input);
105+
try {
106+
// Try the adapter connection (Redis or Memcached will crash if the connection is incorrect)
107+
$bouncer = getBouncerInstance($input);
108+
$bouncer->testConnection();
109+
} catch (BouncerException $e) {
110+
throw new WordpressCrowdSecBouncerException($e->getMessage());
111+
}
112+
} catch (WordpressCrowdSecBouncerException $e) {
113+
AdminNotice::displayError($e->getMessage());
114+
}
74115

116+
try {
117+
try {
118+
//Update wp-cron schedule if stream mode is enabled
119+
if ((bool) get_option('crowdsec_stream_mode')) {
120+
$bouncer = getBouncerInstance($input); // Reload bouncer instance with the new cache system
121+
$result = $bouncer->warmBlocklistCacheUp();
122+
$message = __('As the stream mode is enabled, the cache has just been warmed up, '.($result > 0 ? 'there are now '.$result.' decisions' : 'there is now '.$result.' decision').' in cache.');
123+
AdminNotice::displaySuccess($message);
124+
scheduleBlocklistRefresh();
125+
}
126+
} catch (WordpressCrowdSecBouncerException $e) {
127+
AdminNotice::displayError($e->getMessage());
128+
}
129+
} catch (BouncerException $e) {
130+
AdminNotice::displayError($e->getMessage());
131+
}
75132

76133
return $input;
77-
}, ((get_option("crowdsec_cache_system") === CROWDSEC_CACHE_SYSTEM_PHPFS) ?
78-
'<input style="margin-right:10px" type="button" value="Prune now" class="button button-secondary" onclick="document.getElementById(\'crowdsec_ation_prune_cache\').submit();">' : '') .
134+
}, ((CROWDSEC_CACHE_SYSTEM_PHPFS === get_option('crowdsec_cache_system')) ?
135+
'<input style="margin-right:10px" type="button" value="Prune now" class="button button-secondary" onclick="document.getElementById(\'crowdsec_ation_prune_cache\').submit();">' : '').
79136
'<p>The File system cache is faster than calling LAPI. Redis or Memcached is faster than the File System cache.</p>', [
80137
CROWDSEC_CACHE_SYSTEM_PHPFS => 'File system',
81138
CROWDSEC_CACHE_SYSTEM_REDIS => 'Redis',
82139
CROWDSEC_CACHE_SYSTEM_MEMCACHED => 'Memcached',
83140
]);
84141

85-
// Field "crowdsec_redis_dsn"
86-
addFieldString('crowdsec_redis_dsn', 'Redis DSN<br>(if applicable)', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_cache', function ($input) {
87-
return $input;
88-
}, '<p>Fill in this field only if you have chosen the Redis cache.<br>Example of DSN: redis://localhost:6379.', 'redis://...', '');
89-
90-
// Field "crowdsec_memcached_dsn"
91-
addFieldString('crowdsec_memcached_dsn', 'Memcached DSN<br>(if applicable)', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_cache', function ($input) {
92-
return $input;
93-
}, '<p>Fill in this field only if you have chosen the Memcached cache.<br>Example of DSN: memcached://localhost:11211.', 'memcached://...', '');
94-
95142
// Field "crowdsec_clean_ip_cache_duration"
96143
addFieldString('crowdsec_clean_ip_cache_duration', 'Recheck clean IPs each', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_cache', function ($input) {
97-
if ((int)$input <= 0) {
98-
add_settings_error("Recheck clean IPs each", "crowdsec_error", "Recheck clean IPs each: Minimum is 1 second.");
99-
return "1";
144+
if ((int) $input <= 0) {
145+
add_settings_error('Recheck clean IPs each', 'crowdsec_error', 'Recheck clean IPs each: Minimum is 1 second.');
146+
147+
return '1';
100148
}
149+
101150
return $input;
102151
}, ' seconds. <p>The duration (in seconds) between re-asking LAPI about an already checked clean IP.<br>Minimum 1 second.', '...', 'width: 115px;', 'number');
103152

104153
// Field "crowdsec_bad_ip_cache_duration"
105154
addFieldString('crowdsec_bad_ip_cache_duration', 'Recheck bad IPs each', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_cache', function ($input) {
106-
if ((int)$input <= 0) {
107-
add_settings_error("Recheck bad IPs each", "crowdsec_error", "Recheck bad IPs each: Minimum is 1 second.");
108-
return "1";
155+
if ((int) $input <= 0) {
156+
add_settings_error('Recheck bad IPs each', 'crowdsec_error', 'Recheck bad IPs each: Minimum is 1 second.');
157+
158+
return '1';
109159
}
160+
110161
return $input;
111162
}, ' seconds. <p>The duration (in seconds) between re-asking LAPI about an already checked bad IP.<br>Minimum 1 second.', '...', 'width: 115px;', 'number');
112163

@@ -115,7 +166,7 @@ function adminAdvancedSettings()
115166
**************************/
116167

117168
add_settings_section('crowdsec_admin_advanced_remediations', 'Remediations', function () {
118-
echo "Configuration some details about remediations.";
169+
echo 'Configuration some details about remediations.';
119170
}, 'crowdsec_advanced_settings');
120171

121172
// Field "crowdsec_fallback_remediation"
@@ -126,8 +177,9 @@ function adminAdvancedSettings()
126177
addFieldSelect('crowdsec_fallback_remediation', 'Fallback to', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_remediations', function ($input) {
127178
if (!in_array($input, Constants::ORDERED_REMEDIATIONS)) {
128179
$input = CROWDSEC_BOUNCING_LEVEL_DISABLED;
129-
add_settings_error("Fallback to", "crowdsec_error", "Fallback to: Incorrect Fallback selected.");
180+
add_settings_error('Fallback to', 'crowdsec_error', 'Fallback to: Incorrect Fallback selected.');
130181
}
182+
131183
return $input;
132184
}, '<p>Which remediation to apply when CrowdSec advises unhandled remediation.</p>', $choice);
133185

0 commit comments

Comments
 (0)