Skip to content

Commit

Permalink
#1400 Manage multiple patterns for allowed/blocked IPs via Security O…
Browse files Browse the repository at this point in the history
…ptions 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é <redbird_project@yahoo.fr>

* 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 <fabrizio.mancin@esprinet.com>
Co-authored-by: Raman Maksimchuk <10501504+raman-m@users.noreply.github.com>
Co-authored-by: Raman Maksimchuk <dotnet044@gmail.com>
Co-authored-by: Raynald Messié <redbird_project@yahoo.fr>
  • Loading branch information
5 people committed Sep 30, 2023
1 parent cc0b9b8 commit 5dbbbef
Show file tree
Hide file tree
Showing 7 changed files with 444 additions and 11 deletions.
7 changes: 6 additions & 1 deletion docs/features/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,12 @@ Here is an example Route configuration, You don't need to set all of these thing
"UseTracing": true,
"MaxConnectionsPerServer": 100
},
"DangerousAcceptAnyServerCertificateValidator": false
"DangerousAcceptAnyServerCertificateValidator": false,
"SecurityOptions": {
"IPAllowedList": [],
"IPBlockedList": [],
"ExcludeAllowedFromBlocked": false
}
}
More information on how to use these options is below.
Expand Down
48 changes: 48 additions & 0 deletions docs/features/routing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,51 @@ Ocelot will also allow you to put query string parameters in the UpstreamPathTem
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
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.

Security Options
^^^^^^^^^^^^^^^^

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>`_.

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.
The current patterns managed are the following:

* Single IP: :code:`192.168.1.1`
* IP Range: :code:`192.168.1.1-192.168.1.250`
* IP Short Range: :code:`192.168.1.1-250`
* IP Range with subnet: :code:`192.168.1.0/255.255.255.0`
* CIDR: :code:`192.168.1.0/24`
* CIDR for IPv6: :code:`fe80::/10`
* The allowed/blocked lists are evaluated during configuration loading
* 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.
Default value: :code:`false`
* The absence of a property in **SecurityOptions** is allowed, it takes the default value.

.. code-block:: json
{
"Routes": [
{
"DownstreamPathTemplate": "/api/service/{Id}",
"UpstreamPathTemplate": "/api/internal-service/{Id}/full",
"UpstreamHttpMethod": [
"Get"
],
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 50110
}
],
"SecurityOptions": {
"IPBlockedList": [ "192.168.0.0/23" ],
"IPAllowedList": ["192.168.0.15", "192.168.1.15"],
"ExcludeAllowedFromBlocked": true
},
},
],
"GlobalConfiguration": { }
}
This feature was requested in the `issue 1400 <https://github.com/ThreeMammals/Ocelot/issues/1400>`_.
31 changes: 29 additions & 2 deletions src/Ocelot/Configuration/Creator/SecurityOptionsCreator.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,39 @@
using Ocelot.Configuration.File;
using NetTools; // <PackageReference Include="IPAddressRange" Version="6.0.0" />
using Ocelot.Configuration.File;

namespace Ocelot.Configuration.Creator
{
public class SecurityOptionsCreator : ISecurityOptionsCreator
{
public SecurityOptions Create(FileSecurityOptions securityOptions)
{
return new SecurityOptions(securityOptions.IPAllowedList, securityOptions.IPBlockedList);
var ipAllowedList = new List<string>();
var ipBlockedList = new List<string>();

foreach (var allowed in securityOptions.IPAllowedList)
{
if (IPAddressRange.TryParse(allowed, out var allowedIpAddressRange))
{
var allowedIps = allowedIpAddressRange.Select<IPAddress, string>(x => x.ToString());
ipAllowedList.AddRange(allowedIps);
}
}

foreach (var blocked in securityOptions.IPBlockedList)
{
if (IPAddressRange.TryParse(blocked, out var blockedIpAddressRange))
{
var blockedIps = blockedIpAddressRange.Select<IPAddress, string>(x => x.ToString());
ipBlockedList.AddRange(blockedIps);
}
}

if (securityOptions.ExcludeAllowedFromBlocked)
{
ipBlockedList = ipBlockedList.Except(ipAllowedList).ToList();
}

return new SecurityOptions(ipAllowedList, ipBlockedList);
}
}
}
36 changes: 34 additions & 2 deletions src/Ocelot/Configuration/File/FileSecurityOptions.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,47 @@
namespace Ocelot.Configuration.File
namespace Ocelot.Configuration.File
{
public class FileSecurityOptions
{
public FileSecurityOptions()
{
IPAllowedList = new List<string>();
IPBlockedList = new List<string>();
ExcludeAllowedFromBlocked = false;
}

public List<string> IPAllowedList { get; set; }
public FileSecurityOptions(string allowedIPs = null, string blockedIPs = null, bool? excludeAllowedFromBlocked = null)
: this()
{
if (!string.IsNullOrEmpty(allowedIPs))
{
IPAllowedList.Add(allowedIPs);
}

if (!string.IsNullOrEmpty(blockedIPs))
{
IPBlockedList.Add(blockedIPs);
}

ExcludeAllowedFromBlocked = excludeAllowedFromBlocked ?? false;
}

public FileSecurityOptions(IEnumerable<string> allowedIPs = null, IEnumerable<string> blockedIPs = null, bool? excludeAllowedFromBlocked = null)
: this()
{
IPAllowedList.AddRange(allowedIPs ?? Enumerable.Empty<string>());
IPBlockedList.AddRange(blockedIPs ?? Enumerable.Empty<string>());
ExcludeAllowedFromBlocked = excludeAllowedFromBlocked ?? false;
}

public List<string> IPAllowedList { get; set; }
public List<string> IPBlockedList { get; set; }

/// <summary>
/// Provides the ability to specify a wide range of blocked IP addresses and allow a subrange of IP addresses.
/// </summary>
/// <value>
/// Default value: false.
/// </value>
public bool ExcludeAllowedFromBlocked { get; set; }
}
}
27 changes: 23 additions & 4 deletions src/Ocelot/Configuration/SecurityOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,33 @@
{
public class SecurityOptions
{
public SecurityOptions(List<string> allowedList, List<string> blockedList)
public SecurityOptions()
{
IPAllowedList = allowedList;
IPBlockedList = blockedList;
IPAllowedList = new();
IPBlockedList = new();
}

public List<string> IPAllowedList { get; }
public SecurityOptions(string allowed = null, string blocked = null)
: this()
{
if (!string.IsNullOrEmpty(allowed))
{
IPAllowedList.Add(allowed);
}

if (!string.IsNullOrEmpty(blocked))
{
IPBlockedList.Add(blocked);
}
}

public SecurityOptions(List<string> allowedList = null, List<string> blockedList = null)
{
IPAllowedList = allowedList ?? new();
IPBlockedList = blockedList ?? new();
}

public List<string> IPAllowedList { get; }
public List<string> IPBlockedList { get; }
}
}
1 change: 1 addition & 0 deletions src/Ocelot/Ocelot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

<ItemGroup>
<PackageReference Include="FluentValidation" Version="11.5.2" />
<PackageReference Include="IPAddressRange" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="7.0.5" />
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="3.1.32">
<NoWarn>NU1701</NoWarn>
Expand Down

0 comments on commit 5dbbbef

Please sign in to comment.