Skip to content

Commit 5dbbbef

Browse files
Fabman08FabrizioMancinraman-mRaynaldM
authored
#1400 Manage multiple patterns for allowed/blocked IPs via Security Options config section (#1399)
* Add IpAddressRange package and manage multiple pattern in order to allow or block ip addresses * Update SecurityOptions.cs * Update FileSecurityOptions.cs * Fix Issues * Update routing.rst Fix typos and mistakes in the Security Options paragraph * Update FileSecurityOptions.cs Add developer's XML docs with description from #1400 * Update configuration.rst * Update docs with License Reference * SecurityOptions init moved to SecurityOptionsCreator * Update unit test * SecurityOptionsCreator: File-scoped namespace declaration * Fix SA1312: Variable 'xxx' should begin with lower-case letter * Remove using alias and make logical reference to the package * Fix SA1609: Property documentation should have value * FileSecurityOptions: File-scoped namespace declaration * Fix SA1135: Using directive for namespace 'Ocelot.Responses' should be qualified. Sort usings. Convert to file-scoped namespace. * Fix test code style * Refactor SecurityOptions * FileSecurityOptions: Add constructors * Refactor SecurityOptions: Add constructors * Using constructors for SecurityOptions creation, not initialization * Fix unit test after latest infrastructure updates * Convert to block scoped namespace * Update IPAddressRange to v.6.0.0 * Update src/Ocelot/Configuration/Creator/SecurityOptionsCreator.cs Co-authored-by: Raynald Messié <[email protected]> * Revert "Update src/Ocelot/Configuration/Creator/SecurityOptionsCreator.cs" This reverts commit a77a30e. * Update comment con IPAddressRange reference * Use Select<IPAddress, string> instead of AsEnumerable * Remove and Sort Usings --------- Co-authored-by: Fabrizio Mancin <[email protected]> Co-authored-by: Raman Maksimchuk <[email protected]> Co-authored-by: Raman Maksimchuk <[email protected]> Co-authored-by: Raynald Messié <[email protected]>
1 parent cc0b9b8 commit 5dbbbef

File tree

7 files changed

+444
-11
lines changed

7 files changed

+444
-11
lines changed

docs/features/configuration.rst

+6-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,12 @@ Here is an example Route configuration, You don't need to set all of these thing
6767
"UseTracing": true,
6868
"MaxConnectionsPerServer": 100
6969
},
70-
"DangerousAcceptAnyServerCertificateValidator": false
70+
"DangerousAcceptAnyServerCertificateValidator": false,
71+
"SecurityOptions": {
72+
"IPAllowedList": [],
73+
"IPBlockedList": [],
74+
"ExcludeAllowedFromBlocked": false
75+
}
7176
}
7277
7378
More information on how to use these options is below.

docs/features/routing.rst

+48
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,51 @@ Ocelot will also allow you to put query string parameters in the UpstreamPathTem
237237
238238
In this example Ocelot will only match requests that have a matching url path and the query string starts with unitId=something. You can have other queries after this
239239
but you must start with the matching parameter. Also Ocelot will swap the {unitId} parameter from the query string and use it in the downstream request path.
240+
241+
Security Options
242+
^^^^^^^^^^^^^^^^
243+
244+
Ocelot allows you to manage multiple patterns for allowed/blocked IPs using the `IPAddressRange <https://github.com/jsakamoto/ipaddressrange>`_ package with `MPL-2.0 License <https://github.com/jsakamoto/ipaddressrange/blob/master/LICENSE>`_.
245+
246+
This feature is designed to allow greater IP management in order to include or exclude a wide IP range via CIDR notation or IP range.
247+
The current patterns managed are the following:
248+
249+
* Single IP: :code:`192.168.1.1`
250+
* IP Range: :code:`192.168.1.1-192.168.1.250`
251+
* IP Short Range: :code:`192.168.1.1-250`
252+
* IP Range with subnet: :code:`192.168.1.0/255.255.255.0`
253+
* CIDR: :code:`192.168.1.0/24`
254+
* CIDR for IPv6: :code:`fe80::/10`
255+
* The allowed/blocked lists are evaluated during configuration loading
256+
* The *ExcludeAllowedFromBlocked* property is intended to provide the ability to specify a wide range of blocked IP addresses and allow a subrange of IP addresses.
257+
Default value: :code:`false`
258+
* The absence of a property in **SecurityOptions** is allowed, it takes the default value.
259+
260+
.. code-block:: json
261+
262+
{
263+
"Routes": [
264+
{
265+
"DownstreamPathTemplate": "/api/service/{Id}",
266+
"UpstreamPathTemplate": "/api/internal-service/{Id}/full",
267+
"UpstreamHttpMethod": [
268+
"Get"
269+
],
270+
"DownstreamScheme": "http",
271+
"DownstreamHostAndPorts": [
272+
{
273+
"Host": "localhost",
274+
"Port": 50110
275+
}
276+
],
277+
"SecurityOptions": {
278+
"IPBlockedList": [ "192.168.0.0/23" ],
279+
"IPAllowedList": ["192.168.0.15", "192.168.1.15"],
280+
"ExcludeAllowedFromBlocked": true
281+
},
282+
},
283+
],
284+
"GlobalConfiguration": { }
285+
}
286+
287+
This feature was requested in the `issue 1400 <https://github.com/ThreeMammals/Ocelot/issues/1400>`_.
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,39 @@
1-
using Ocelot.Configuration.File;
1+
using NetTools; // <PackageReference Include="IPAddressRange" Version="6.0.0" />
2+
using Ocelot.Configuration.File;
23

34
namespace Ocelot.Configuration.Creator
45
{
56
public class SecurityOptionsCreator : ISecurityOptionsCreator
67
{
78
public SecurityOptions Create(FileSecurityOptions securityOptions)
89
{
9-
return new SecurityOptions(securityOptions.IPAllowedList, securityOptions.IPBlockedList);
10+
var ipAllowedList = new List<string>();
11+
var ipBlockedList = new List<string>();
12+
13+
foreach (var allowed in securityOptions.IPAllowedList)
14+
{
15+
if (IPAddressRange.TryParse(allowed, out var allowedIpAddressRange))
16+
{
17+
var allowedIps = allowedIpAddressRange.Select<IPAddress, string>(x => x.ToString());
18+
ipAllowedList.AddRange(allowedIps);
19+
}
20+
}
21+
22+
foreach (var blocked in securityOptions.IPBlockedList)
23+
{
24+
if (IPAddressRange.TryParse(blocked, out var blockedIpAddressRange))
25+
{
26+
var blockedIps = blockedIpAddressRange.Select<IPAddress, string>(x => x.ToString());
27+
ipBlockedList.AddRange(blockedIps);
28+
}
29+
}
30+
31+
if (securityOptions.ExcludeAllowedFromBlocked)
32+
{
33+
ipBlockedList = ipBlockedList.Except(ipAllowedList).ToList();
34+
}
35+
36+
return new SecurityOptions(ipAllowedList, ipBlockedList);
1037
}
1138
}
1239
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,47 @@
1-
namespace Ocelot.Configuration.File
1+
namespace Ocelot.Configuration.File
22
{
33
public class FileSecurityOptions
44
{
55
public FileSecurityOptions()
66
{
77
IPAllowedList = new List<string>();
88
IPBlockedList = new List<string>();
9+
ExcludeAllowedFromBlocked = false;
910
}
1011

11-
public List<string> IPAllowedList { get; set; }
12+
public FileSecurityOptions(string allowedIPs = null, string blockedIPs = null, bool? excludeAllowedFromBlocked = null)
13+
: this()
14+
{
15+
if (!string.IsNullOrEmpty(allowedIPs))
16+
{
17+
IPAllowedList.Add(allowedIPs);
18+
}
19+
20+
if (!string.IsNullOrEmpty(blockedIPs))
21+
{
22+
IPBlockedList.Add(blockedIPs);
23+
}
24+
25+
ExcludeAllowedFromBlocked = excludeAllowedFromBlocked ?? false;
26+
}
1227

28+
public FileSecurityOptions(IEnumerable<string> allowedIPs = null, IEnumerable<string> blockedIPs = null, bool? excludeAllowedFromBlocked = null)
29+
: this()
30+
{
31+
IPAllowedList.AddRange(allowedIPs ?? Enumerable.Empty<string>());
32+
IPBlockedList.AddRange(blockedIPs ?? Enumerable.Empty<string>());
33+
ExcludeAllowedFromBlocked = excludeAllowedFromBlocked ?? false;
34+
}
35+
36+
public List<string> IPAllowedList { get; set; }
1337
public List<string> IPBlockedList { get; set; }
38+
39+
/// <summary>
40+
/// Provides the ability to specify a wide range of blocked IP addresses and allow a subrange of IP addresses.
41+
/// </summary>
42+
/// <value>
43+
/// Default value: false.
44+
/// </value>
45+
public bool ExcludeAllowedFromBlocked { get; set; }
1446
}
1547
}

src/Ocelot/Configuration/SecurityOptions.cs

+23-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,33 @@
22
{
33
public class SecurityOptions
44
{
5-
public SecurityOptions(List<string> allowedList, List<string> blockedList)
5+
public SecurityOptions()
66
{
7-
IPAllowedList = allowedList;
8-
IPBlockedList = blockedList;
7+
IPAllowedList = new();
8+
IPBlockedList = new();
99
}
1010

11-
public List<string> IPAllowedList { get; }
11+
public SecurityOptions(string allowed = null, string blocked = null)
12+
: this()
13+
{
14+
if (!string.IsNullOrEmpty(allowed))
15+
{
16+
IPAllowedList.Add(allowed);
17+
}
18+
19+
if (!string.IsNullOrEmpty(blocked))
20+
{
21+
IPBlockedList.Add(blocked);
22+
}
23+
}
1224

25+
public SecurityOptions(List<string> allowedList = null, List<string> blockedList = null)
26+
{
27+
IPAllowedList = allowedList ?? new();
28+
IPBlockedList = blockedList ?? new();
29+
}
30+
31+
public List<string> IPAllowedList { get; }
1332
public List<string> IPBlockedList { get; }
1433
}
1534
}

src/Ocelot/Ocelot.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
<ItemGroup>
3131
<PackageReference Include="FluentValidation" Version="11.5.2" />
32+
<PackageReference Include="IPAddressRange" Version="6.0.0" />
3233
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="7.0.5" />
3334
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="3.1.32">
3435
<NoWarn>NU1701</NoWarn>

0 commit comments

Comments
 (0)