Skip to content

Commit 9336726

Browse files
committed
add cdn ip range feature
1 parent c574fb9 commit 9336726

File tree

3 files changed

+91
-5
lines changed

3 files changed

+91
-5
lines changed

inc/admin/advanced-settings.php

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,11 @@ function adminAdvancedSettings()
5656

5757
// Field "crowdsec_redis_dsn"
5858
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.
6159
return $input;
6260
}, '<p>Fill in this field only if you have chosen the Redis cache.<br>Example of DSN: redis://localhost:6379.', 'redis://...', '');
6361

6462
// Field "crowdsec_memcached_dsn"
6563
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.
6864
return $input;
6965
}, '<p>Fill in this field only if you have chosen the Memcached cache.<br>Example of DSN: memcached://localhost:11211.', 'memcached://...', '');
7066

@@ -174,7 +170,8 @@ function adminAdvancedSettings()
174170
foreach (Constants::ORDERED_REMEDIATIONS as $remediation) {
175171
$choice[$remediation] = $remediation;
176172
}
177-
addFieldSelect('crowdsec_fallback_remediation', 'Fallback to', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_remediations', function ($input) {
173+
addFieldSelect('crowdsec_fallback_remediation', 'Fallback to', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings',
174+
'crowdsec_admin_advanced_remediations', function ($input) {
178175
if (!in_array($input, Constants::ORDERED_REMEDIATIONS)) {
179176
$input = CROWDSEC_BOUNCING_LEVEL_DISABLED;
180177
add_settings_error('Fallback to', 'crowdsec_error', 'Fallback to: Incorrect Fallback selected.');
@@ -183,6 +180,64 @@ function adminAdvancedSettings()
183180
return $input;
184181
}, '<p>Which remediation to apply when CrowdSec advises unhandled remediation.</p>', $choice);
185182

183+
function cidrToLongBounds(int $longIp, int $factor): array
184+
{
185+
$range = [];
186+
$range[0] = (($longIp) & ((-1 << (32 - $factor))));
187+
$range[1] = ((($range[0])) + pow(2, (32 - $factor)) - 1);
188+
189+
return $range;
190+
}
191+
192+
function convertInlineIpRangesToLongArray(string $inlineIpRanges): array
193+
{
194+
$longIpBoundsList = [];
195+
$stringRangeArray = explode(',', $inlineIpRanges);
196+
foreach ($stringRangeArray as $stringRange) {
197+
$stringRange = trim($stringRange);
198+
if (false !== strpos($stringRange, '/')) {
199+
$stringRange = explode('/', $stringRange);
200+
$longIp = ip2long($stringRange[0]);
201+
$factor = (int) $stringRange[1];
202+
if (false === $longIp) {
203+
throw new WordpressCrowdSecBouncerException('Invalid IP List format.');
204+
}
205+
if (0 === (int) $factor) {
206+
throw new WordpressCrowdSecBouncerException('Invalid IP List format.');
207+
}
208+
$longBounds = cidrToLongBounds($longIp, $factor);
209+
$longIpBoundsList = array_merge($longIpBoundsList, [$longBounds]);
210+
} else {
211+
$long = ip2long($stringRange);
212+
if (false === $long) {
213+
throw new WordpressCrowdSecBouncerException('Invalid IP List format.');
214+
}
215+
$longIpBoundsList = array_merge($longIpBoundsList, [[$long, $long]]);
216+
}
217+
}
218+
return $longIpBoundsList;
219+
}
220+
221+
// Field "crowdsec_trust_ip_forward"
222+
addFieldString('crowdsec_trust_ip_forward_list', 'Trust these CDN IPs<br>(or Load Balancer, HTTP Proxy)', 'crowdsec_plugin_advanced_settings',
223+
'crowdsec_advanced_settings', 'crowdsec_admin_advanced_remediations', function ($input) {
224+
try {
225+
$longList = convertInlineIpRangesToLongArray($input);
226+
update_option('crowdsec_trust_ip_forward_array', $longList);
227+
AdminNotice::displaySuccess('Ips with XFF to trust successfully saved.');
228+
} catch (WordpressCrowdSecBouncerException $e) {
229+
update_option('crowdsec_trust_ip_forward_array', []);
230+
add_settings_error('Trust these CDN IPs', 'crowdsec_error', 'Trust these CDN IPs: Invalid IP List format.');
231+
232+
return '';
233+
}
234+
235+
return $input;
236+
}, '<p>The <em><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For" '.
237+
'target="_blank">X-forwarded-For</a></em> HTTP Header will be trust only when the client IP is in this list.'.
238+
'<br><strong>Comma (,)</strong> separated ips or ips ranges. Example: 1.2.3.4/24, 2.3.4.5, 3.4.5.6/27',
239+
'fill the IPs or IPs ranges here...', '');
240+
186241
// Field "crowdsec_hide_mentions"
187242
addFieldCheckbox('crowdsec_hide_mentions', 'Hide CrowdSec mentions', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_remediations', function () {
188243
// Stream mode just activated.

inc/bounce-current-ip.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,35 @@
55

66
function bounceCurrentIp()
77
{
8+
function shouldTrustXforwardedFor(string $ip): bool
9+
{
10+
$longIp = ip2long($ip);
11+
foreach (get_option('crowdsec_trust_ip_forward_array') as $longBounds) {
12+
if ($longIp >= $longBounds[0] && $longIp <= $longBounds[1]) {
13+
return true;
14+
}
15+
}
16+
17+
return false;
18+
}
19+
820
$ip = $_SERVER['REMOTE_ADDR'];
921

22+
// X-Forwarded-For override
23+
if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) {
24+
$ipList = array_map('trim', array_values(array_filter(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']))));
25+
$forwardedIp = end($ipList);
26+
if (shouldTrustXforwardedFor($ip)) {
27+
$ip = $forwardedIp;
28+
} else {
29+
getCrowdSecLoggerInstance()->warning('', [
30+
'type' => 'WP_NON_AUTHORIZED_X_FORWARDED_FOR_USAGE',
31+
'original_ip' => $ip,
32+
'x_forwarded_for_ip' => $forwardedIp,
33+
]);
34+
}
35+
}
36+
1037
function displayCaptchaWall()
1138
{
1239
header('HTTP/1.0 401 Unauthorized');

inc/plugin-setup.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ function activate_crowdsec_plugin()
2929
update_option('crowdsec_fallback_remediation', Constants::REMEDIATION_CAPTCHA);
3030

3131
update_option('crowdsec_hide_mentions', false);
32+
update_option('crowdsec_trust_ip_forward', '');
33+
update_option('crowdsec_trust_ip_forward_array', []);
3234
}
3335

3436
/**
@@ -68,4 +70,6 @@ function deactivate_crowdsec_plugin()
6870
delete_option('crowdsec_fallback_remediation');
6971

7072
delete_option('crowdsec_hide_mentions');
73+
delete_option('crowdsec_trust_ip_forward');
74+
delete_option('crowdsec_trust_ip_forward_array');
7175
}

0 commit comments

Comments
 (0)