Skip to content

Commit 6e470a1

Browse files
Altahrimbackportbot[bot]
authored andcommitted
feat(ip): add configurable IPv6 subnet for BFP and throttling
Signed-off-by: Benjamin Gaussorgues <[email protected]> [skip ci]
1 parent 5e8b942 commit 6e470a1

File tree

3 files changed

+23
-8
lines changed

3 files changed

+23
-8
lines changed

config/config.sample.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,16 @@
420420
*/
421421
'ratelimit.protection.enabled' => true,
422422

423+
/**
424+
* Size of subnet used to normalize IPv6
425+
*
426+
* For Brute Force Protection and Rate Limiting, IPv6 are truncated using subnet size.
427+
* It defaults to /56 but you can set it between /32 and /64
428+
*
429+
* Defaults to ``56``
430+
*/
431+
'security.ipv6_normalized_subnet_size' => 56,
432+
423433
/**
424434
* By default, WebAuthn is available, but it can be explicitly disabled by admins
425435
*/

lib/private/Security/Normalizer/IpAddress.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
*/
99
namespace OC\Security\Normalizer;
1010

11+
use OCP\IConfig;
12+
1113
/**
1214
* Class IpAddress is used for normalizing IPv4 and IPv6 addresses in security
1315
* relevant contexts in Nextcloud.
@@ -24,7 +26,8 @@ public function __construct(
2426
}
2527

2628
/**
27-
* Return the given subnet for an IPv6 address (48 first bits)
29+
* Return the given subnet for an IPv6 address
30+
* Rely on security.ipv6_normalized_subnet_size, defaults to 56
2831
*/
2932
private function getIPv6Subnet(string $ip): string {
3033
if ($ip[0] === '[' && $ip[-1] === ']') { // If IP is with brackets, for example [::1]
@@ -35,8 +38,10 @@ private function getIPv6Subnet(string $ip): string {
3538
$ip = substr($ip, 0, $pos - 1);
3639
}
3740

38-
$binary = \inet_pton($ip);
39-
$mask = inet_pton('FFFF:FFFF:FFFF::');
41+
$config = \OCP\Server::get(IConfig::class);
42+
$maskSize = min(64, $config->getSystemValueInt('security.ipv6_normalized_subnet_size', 56));
43+
$maskSize = max(32, $maskSize);
44+
$mask = pack('VVP', (1 << 32) - 1, (1 << $maskSize - 32) - 1, 0);
4045

4146
return inet_ntop($binary & $mask).'/48';
4247
}
@@ -63,7 +68,7 @@ private function getEmbeddedIpv4(string $ipv6): ?string {
6368

6469

6570
/**
66-
* Gets either the /32 (IPv4) or the /48 (IPv6) subnet of an IP address
71+
* Gets either the /32 (IPv4) or the /56 (default for IPv6) subnet of an IP address
6772
*/
6873
public function getSubnet(): string {
6974
if (filter_var($this->ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {

tests/lib/Security/Normalizer/IpAddressTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,19 @@ public function subnetDataProvider() {
3737
],
3838
[
3939
'2001:0db8:0000:0000:0000:8a2e:0370:7334',
40-
'2001:db8::/48',
40+
'2001:db8::/56',
4141
],
4242
[
4343
'2001:db8:3333:4444:5555:6666:7777:8888',
44-
'2001:db8:3333::/48',
44+
'2001:db8:3333:4400::/56',
4545
],
4646
[
4747
'::1234:5678',
48-
'::/48',
48+
'::/56',
4949
],
5050
[
5151
'[::1]',
52-
'::/48',
52+
'::/56',
5353
],
5454
];
5555
}

0 commit comments

Comments
 (0)