Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/Ocelot/Configuration/Creator/ISecurityOptionsCreator.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using Ocelot.Configuration.File;

namespace Ocelot.Configuration.Creator
namespace Ocelot.Configuration.Creator;

public interface ISecurityOptionsCreator
{
public interface ISecurityOptionsCreator
{
SecurityOptions Create(FileSecurityOptions securityOptions);
}
SecurityOptions Create(FileSecurityOptions securityOptions, FileGlobalConfiguration global);
}
2 changes: 1 addition & 1 deletion src/Ocelot/Configuration/Creator/RoutesCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ private DownstreamRoute SetUpDownstreamRoute(FileRoute fileRoute, FileGlobalConf

var lbOptions = _loadBalancerOptionsCreator.Create(fileRoute.LoadBalancerOptions);

var securityOptions = _securityOptionsCreator.Create(fileRoute.SecurityOptions);
var securityOptions = _securityOptionsCreator.Create(fileRoute.SecurityOptions, globalConfiguration);

var downstreamHttpVersion = _versionCreator.Create(fileRoute.DownstreamHttpVersion);

Expand Down
49 changes: 19 additions & 30 deletions src/Ocelot/Configuration/Creator/SecurityOptionsCreator.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,28 @@
using NetTools; // <PackageReference Include="IPAddressRange" Version="6.0.0" />
using Ocelot.Configuration.File;

namespace Ocelot.Configuration.Creator
namespace Ocelot.Configuration.Creator;

public class SecurityOptionsCreator : ISecurityOptionsCreator
{
public class SecurityOptionsCreator : ISecurityOptionsCreator
public SecurityOptions Create(FileSecurityOptions securityOptions, FileGlobalConfiguration global)
{
public SecurityOptions Create(FileSecurityOptions securityOptions)
{
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();
}
var options = securityOptions.IsEmpty() ? global.SecurityOptions : securityOptions;
var allowedIPs = options.IPAllowedList.SelectMany(Parse)
.ToArray();
var blockedIPs = options.IPBlockedList.SelectMany(Parse)
.Except(options.ExcludeAllowedFromBlocked ? allowedIPs : Enumerable.Empty<string>())
.ToArray();
return new(allowedIPs, blockedIPs);
}

return new SecurityOptions(ipAllowedList, ipBlockedList);
private static string[] Parse(string ipValue)
{
if (IPAddressRange.TryParse(ipValue, out var range))
{
return range.Select<IPAddress, string>(ip => ip.ToString()).ToArray();
}

return Array.Empty<string>();
}
}
3 changes: 3 additions & 0 deletions src/Ocelot/Configuration/File/FileGlobalConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public FileGlobalConfiguration()
HttpHandlerOptions = new FileHttpHandlerOptions();
CacheOptions = new FileCacheOptions();
MetadataOptions = new FileMetadataOptions();
SecurityOptions = new FileSecurityOptions();
}

public string RequestIdKey { get; set; }
Expand Down Expand Up @@ -48,5 +49,7 @@ public FileGlobalConfiguration()
public FileCacheOptions CacheOptions { get; set; }

public FileMetadataOptions MetadataOptions { get; set; }

public FileSecurityOptions SecurityOptions { get; set; }
}
}
79 changes: 38 additions & 41 deletions src/Ocelot/Configuration/File/FileSecurityOptions.cs
Original file line number Diff line number Diff line change
@@ -1,54 +1,51 @@
namespace Ocelot.Configuration.File
namespace Ocelot.Configuration.File;

public class FileSecurityOptions
{
public class FileSecurityOptions
public FileSecurityOptions()
{
public FileSecurityOptions()
{
IPAllowedList = new List<string>();
IPBlockedList = new List<string>();
ExcludeAllowedFromBlocked = false;
}
IPAllowedList = new();
IPBlockedList = new();
ExcludeAllowedFromBlocked = false;
}

public FileSecurityOptions(FileSecurityOptions from)
{
IPAllowedList = new(from.IPAllowedList);
IPBlockedList = new(from.IPBlockedList);
ExcludeAllowedFromBlocked = from.ExcludeAllowedFromBlocked;
}
public FileSecurityOptions(FileSecurityOptions from)
{
IPAllowedList = new(from.IPAllowedList);
IPBlockedList = new(from.IPBlockedList);
ExcludeAllowedFromBlocked = from.ExcludeAllowedFromBlocked;
}

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

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

ExcludeAllowedFromBlocked = excludeAllowedFromBlocked ?? false;
IPAllowedList.Add(allowedIPs);
}

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

public List<string> IPAllowedList { get; set; }
public List<string> IPBlockedList { get; set; }
ExcludeAllowedFromBlocked = excludeAllowedFromBlocked ?? false;
}

/// <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; }
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>A <see cref="bool"/> value, defaults to <see langword="false"/>.</value>
public bool ExcludeAllowedFromBlocked { get; set; }

public bool IsEmpty() => IPAllowedList.Count == 0 && IPBlockedList.Count == 0;
}
14 changes: 7 additions & 7 deletions src/Ocelot/Configuration/SecurityOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ public class SecurityOptions
{
public SecurityOptions()
{
IPAllowedList = new();
IPBlockedList = new();
IPAllowedList = new List<string>();
IPBlockedList = new List<string>();
}

public SecurityOptions(string allowed = null, string blocked = null)
Expand All @@ -22,13 +22,13 @@ public SecurityOptions(string allowed = null, string blocked = null)
}
}

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

public List<string> IPAllowedList { get; }
public List<string> IPBlockedList { get; }
public IList<string> IPAllowedList { get; }
public IList<string> IPBlockedList { get; }
}
}
48 changes: 25 additions & 23 deletions src/Ocelot/Security/IPSecurity/IPSecurityPolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,40 @@
using Ocelot.Middleware;
using Ocelot.Responses;

namespace Ocelot.Security.IPSecurity
namespace Ocelot.Security.IPSecurity;

public class IPSecurityPolicy : ISecurityPolicy
{
public class IPSecurityPolicy : ISecurityPolicy
public Response Security(DownstreamRoute downstreamRoute, HttpContext context)
{
public async Task<Response> Security(DownstreamRoute downstreamRoute, HttpContext httpContext)
var clientIp = context.Connection.RemoteIpAddress;
var options = downstreamRoute.SecurityOptions;
if (options == null || clientIp == null)
{
var clientIp = httpContext.Connection.RemoteIpAddress;
var securityOptions = downstreamRoute.SecurityOptions;
if (securityOptions == null)
{
return new OkResponse();
}
return new OkResponse();
}

if (securityOptions.IPBlockedList != null)
if (options.IPBlockedList?.Count > 0)
{
if (options.IPBlockedList.Contains(clientIp.ToString()))
{
if (securityOptions.IPBlockedList.Exists(f => f == clientIp.ToString()))
{
var error = new UnauthenticatedError($" This request rejects access to {clientIp} IP");
return new ErrorResponse(error);
}
var error = new UnauthenticatedError($"This request rejects access to {clientIp} IP");
return new ErrorResponse(error);
}
}

if (securityOptions.IPAllowedList?.Count > 0)
if (options.IPAllowedList?.Count > 0)
{
if (!options.IPAllowedList.Contains(clientIp.ToString()))
{
if (!securityOptions.IPAllowedList.Exists(f => f == clientIp.ToString()))
{
var error = new UnauthenticatedError($"{clientIp} does not allow access, the request is invalid");
return new ErrorResponse(error);
}
var error = new UnauthenticatedError($"{clientIp} does not allow access, the request is invalid");
return new ErrorResponse(error);
}

return await Task.FromResult(new OkResponse());
}

return new OkResponse();
}

public Task<Response> SecurityAsync(DownstreamRoute downstreamRoute, HttpContext context)
=> Task.Run(() => Security(downstreamRoute, context));
}
10 changes: 5 additions & 5 deletions src/Ocelot/Security/ISecurityPolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
using Ocelot.Configuration;
using Ocelot.Responses;

namespace Ocelot.Security
namespace Ocelot.Security;

public interface ISecurityPolicy
{
public interface ISecurityPolicy
{
Task<Response> Security(DownstreamRoute downstreamRoute, HttpContext httpContext);
}
Response Security(DownstreamRoute downstreamRoute, HttpContext context);
Task<Response> SecurityAsync(DownstreamRoute downstreamRoute, HttpContext context);
}
5 changes: 2 additions & 3 deletions src/Ocelot/Security/Middleware/SecurityMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ public class SecurityMiddleware : OcelotMiddleware

public SecurityMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IEnumerable<ISecurityPolicy> securityPolicies
)
IEnumerable<ISecurityPolicy> securityPolicies)
: base(loggerFactory.CreateLogger<SecurityMiddleware>())
{
_securityPolicies = securityPolicies;
Expand All @@ -27,7 +26,7 @@ public async Task Invoke(HttpContext httpContext)
{
foreach (var policy in _securityPolicies)
{
var result = await policy.Security(downstreamRoute, httpContext);
var result = policy.Security(downstreamRoute, httpContext);
if (!result.IsError)
{
continue;
Expand Down
Loading