add a security module (#628) (#629)

* Add security mechanisms, IP whitelists, blacklists, and extended security interfaces.

* Optimized configuration name

* Fix IP Security Bug

* Repair Security Protection Module Bug

* Add security module unit test

* Optimize log prompts
This commit is contained in:
阿凌
2018-09-26 01:29:38 +08:00
committed by Tom Pallister
parent aa14b2f877
commit 4a8f4c2e03
18 changed files with 526 additions and 10 deletions

View File

@ -38,7 +38,7 @@ namespace Ocelot.Configuration.Builder
private List<AddHeader> _addHeadersToDownstream;
private List<AddHeader> _addHeadersToUpstream;
private bool _dangerousAcceptAnyServerCertificateValidator;
private SecurityOptions _securityOptions;
public DownstreamReRouteBuilder()
{
_downstreamAddresses = new List<DownstreamHostAndPort>();
@ -227,6 +227,12 @@ namespace Ocelot.Configuration.Builder
return this;
}
public DownstreamReRouteBuilder WithSecurityOptions(SecurityOptions securityOptions)
{
_securityOptions = securityOptions;
return this;
}
public DownstreamReRoute Build()
{
return new DownstreamReRoute(
@ -258,7 +264,8 @@ namespace Ocelot.Configuration.Builder
_delegatingHandlers,
_addHeadersToDownstream,
_addHeadersToUpstream,
_dangerousAcceptAnyServerCertificateValidator);
_dangerousAcceptAnyServerCertificateValidator,
_securityOptions);
}
}
}

View File

@ -0,0 +1,12 @@
using Ocelot.Configuration.File;
using System;
using System.Collections.Generic;
using System.Text;
namespace Ocelot.Configuration.Creator
{
public interface ISecurityOptionsCreator
{
SecurityOptions Create(FileSecurityOptions securityOptions);
}
}

View File

@ -21,6 +21,7 @@ namespace Ocelot.Configuration.Creator
private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator;
private readonly IDownstreamAddressesCreator _downstreamAddressesCreator;
private readonly IReRouteKeyCreator _reRouteKeyCreator;
private readonly ISecurityOptionsCreator _securityOptionsCreator;
public ReRoutesCreator(
IClaimsToThingCreator claimsToThingCreator,
@ -35,7 +36,8 @@ namespace Ocelot.Configuration.Creator
IHeaderFindAndReplaceCreator headerFAndRCreator,
IDownstreamAddressesCreator downstreamAddressesCreator,
ILoadBalancerOptionsCreator loadBalancerOptionsCreator,
IReRouteKeyCreator reRouteKeyCreator
IReRouteKeyCreator reRouteKeyCreator,
ISecurityOptionsCreator securityOptionsCreator
)
{
_reRouteKeyCreator = reRouteKeyCreator;
@ -52,6 +54,7 @@ namespace Ocelot.Configuration.Creator
_fileReRouteOptionsCreator = fileReRouteOptionsCreator;
_httpHandlerOptionsCreator = httpHandlerOptionsCreator;
_loadBalancerOptionsCreator = loadBalancerOptionsCreator;
_securityOptionsCreator = securityOptionsCreator;
}
public List<ReRoute> Create(FileConfiguration fileConfiguration)
@ -97,6 +100,8 @@ namespace Ocelot.Configuration.Creator
var lbOptions = _loadBalancerOptionsCreator.Create(fileReRoute.LoadBalancerOptions);
var securityOptions = _securityOptionsCreator.Create(fileReRoute.SecurityOptions);
var reRoute = new DownstreamReRouteBuilder()
.WithKey(fileReRoute.Key)
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
@ -128,6 +133,7 @@ namespace Ocelot.Configuration.Creator
.WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream)
.WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream)
.WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator)
.WithSecurityOptions(securityOptions)
.Build();
return reRoute;

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
using Ocelot.Configuration.File;
namespace Ocelot.Configuration.Creator
{
public class SecurityOptionsCreator : ISecurityOptionsCreator
{
public SecurityOptions Create(FileSecurityOptions securityOptions)
{
return new SecurityOptions(securityOptions.IPAllowedList, securityOptions.IPBlockedList);
}
}
}

View File

@ -35,7 +35,8 @@ namespace Ocelot.Configuration
List<string> delegatingHandlers,
List<AddHeader> addHeadersToDownstream,
List<AddHeader> addHeadersToUpstream,
bool dangerousAcceptAnyServerCertificateValidator)
bool dangerousAcceptAnyServerCertificateValidator,
SecurityOptions securityOptions)
{
DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator;
AddHeadersToDownstream = addHeadersToDownstream;
@ -66,6 +67,7 @@ namespace Ocelot.Configuration
DownstreamPathTemplate = downstreamPathTemplate;
LoadBalancerKey = loadBalancerKey;
AddHeadersToUpstream = addHeadersToUpstream;
SecurityOptions = securityOptions;
}
public string Key { get; }
@ -97,5 +99,6 @@ namespace Ocelot.Configuration
public List<AddHeader> AddHeadersToDownstream { get; }
public List<AddHeader> AddHeadersToUpstream { get; }
public bool DangerousAcceptAnyServerCertificateValidator { get; }
public SecurityOptions SecurityOptions { get; }
}
}

View File

@ -21,6 +21,7 @@ namespace Ocelot.Configuration.File
DownstreamHostAndPorts = new List<FileHostAndPort>();
DelegatingHandlers = new List<string>();
LoadBalancerOptions = new FileLoadBalancerOptions();
SecurityOptions = new FileSecurityOptions();
Priority = 1;
}
@ -50,5 +51,6 @@ namespace Ocelot.Configuration.File
public int Priority { get;set; }
public int Timeout { get; set; }
public bool DangerousAcceptAnyServerCertificateValidator { get; set; }
public FileSecurityOptions SecurityOptions { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Ocelot.Configuration.File
{
public class FileSecurityOptions
{
public FileSecurityOptions()
{
IPAllowedList = new List<string>();
IPBlockedList = new List<string>();
}
public List<string> IPAllowedList { get; set; }
public List<string> IPBlockedList { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Ocelot.Configuration
{
public class SecurityOptions
{
public SecurityOptions(List<string> allowedList, List<string> blockedList)
{
this.IPAllowedList = allowedList;
this.IPBlockedList = blockedList;
}
public List<string> IPAllowedList { get; private set; }
public List<string> IPBlockedList { get; private set; }
}
}

View File

@ -35,6 +35,8 @@ namespace Ocelot.DependencyInjection
using Ocelot.Infrastructure;
using Ocelot.Middleware.Multiplexer;
using Ocelot.Request.Creator;
using Ocelot.Security.IPSecurity;
using Ocelot.Security;
public class OcelotBuilder : IOcelotBuilder
{
@ -125,6 +127,9 @@ namespace Ocelot.DependencyInjection
Services.TryAddSingleton<IQoSFactory, QoSFactory>();
Services.TryAddSingleton<IExceptionToErrorMapper, HttpExeptionToErrorMapper>();
//add security
this.AddSecurity();
//add asp.net services..
var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;
@ -139,22 +144,28 @@ namespace Ocelot.DependencyInjection
Services.AddWebEncoders();
}
public IOcelotBuilder AddSingletonDefinedAggregator<T>()
public IOcelotBuilder AddSingletonDefinedAggregator<T>()
where T : class, IDefinedAggregator
{
Services.AddSingleton<IDefinedAggregator, T>();
return this;
}
public IOcelotBuilder AddTransientDefinedAggregator<T>()
public IOcelotBuilder AddTransientDefinedAggregator<T>()
where T : class, IDefinedAggregator
{
Services.AddTransient<IDefinedAggregator, T>();
return this;
}
public IOcelotBuilder AddDelegatingHandler<THandler>(bool global = false)
where THandler : DelegatingHandler
private void AddSecurity()
{
Services.TryAddSingleton<ISecurityOptionsCreator, SecurityOptionsCreator>();
Services.TryAddSingleton<ISecurityPolicy, IPSecurityPolicy>();
}
public IOcelotBuilder AddDelegatingHandler<THandler>(bool global = false)
where THandler : DelegatingHandler
{
if(global)
{

View File

@ -15,6 +15,7 @@ using Ocelot.Request.Middleware;
using Ocelot.Requester.Middleware;
using Ocelot.RequestId.Middleware;
using Ocelot.Responder.Middleware;
using Ocelot.Security.Middleware;
using Ocelot.WebSockets.Middleware;
namespace Ocelot.Middleware.Pipeline
@ -48,6 +49,9 @@ namespace Ocelot.Middleware.Pipeline
// Then we get the downstream route information
builder.UseDownstreamRouteFinderMiddleware();
// This security module, IP whitelist blacklist, extended security mechanism
builder.UseSecurityMiddleware();
//Expand other branch pipes
if (pipelineConfiguration.MapWhenOcelotPipeline != null)
{

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Middleware;
using Ocelot.Responses;
namespace Ocelot.Security.IPSecurity
{
public class IPSecurityPolicy : ISecurityPolicy
{
public async Task<Response> Security(DownstreamContext context)
{
IPAddress clientIp = context.HttpContext.Connection.RemoteIpAddress;
SecurityOptions securityOptions = context.DownstreamReRoute.SecurityOptions;
if (securityOptions == null)
{
return new OkResponse();
}
if (securityOptions.IPBlockedList != null)
{
if (securityOptions.IPBlockedList.Exists(f => f == clientIp.ToString()))
{
var error = new UnauthenticatedError($" This request rejects access to {clientIp.ToString()} IP");
return new ErrorResponse(error);
}
}
if (securityOptions.IPAllowedList != null && securityOptions.IPAllowedList.Count > 0)
{
if (!securityOptions.IPAllowedList.Exists(f => f == clientIp.ToString()))
{
var error = new UnauthenticatedError($"{clientIp.ToString()} does not allow access, the request is invalid");
return new ErrorResponse(error);
}
}
return await Task.FromResult(new OkResponse());
}
}
}

View File

@ -0,0 +1,14 @@
using Ocelot.Middleware;
using Ocelot.Responses;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Ocelot.Security
{
public interface ISecurityPolicy
{
Task<Response> Security(DownstreamContext context);
}
}

View File

@ -0,0 +1,43 @@
using Ocelot.Logging;
using Ocelot.Middleware;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ocelot.Security.Middleware
{
public class SecurityMiddleware : OcelotMiddleware
{
private readonly OcelotRequestDelegate _next;
private readonly IOcelotLogger _logger;
private readonly IEnumerable<ISecurityPolicy> _securityPolicies;
public SecurityMiddleware(IOcelotLoggerFactory loggerFactory,
IEnumerable<ISecurityPolicy> securityPolicies,
OcelotRequestDelegate next)
: base(loggerFactory.CreateLogger<SecurityMiddleware>())
{
_logger = loggerFactory.CreateLogger<SecurityMiddleware>();
_securityPolicies = securityPolicies;
_next = next;
}
public async Task Invoke(DownstreamContext context)
{
if (_securityPolicies != null)
{
foreach (var policie in _securityPolicies)
{
var result = await policie.Security(context);
if (!result.IsError)
{
continue;
}
this.SetPipelineError(context, result.Errors);
return;
}
}
await _next.Invoke(context);
}
}
}

View File

@ -0,0 +1,15 @@
using Ocelot.Middleware.Pipeline;
using System;
using System.Collections.Generic;
using System.Text;
namespace Ocelot.Security.Middleware
{
public static class SecurityMiddlewareExtensions
{
public static IOcelotPipelineBuilder UseSecurityMiddleware(this IOcelotPipelineBuilder builder)
{
return builder.UseMiddleware<SecurityMiddleware>();
}
}
}