diff --git a/InvestmentManager/InvestmentManager/InvestmentManager.csproj b/InvestmentManager/InvestmentManager/InvestmentManager.csproj index 65d9c3c..19d9958 100644 --- a/InvestmentManager/InvestmentManager/InvestmentManager.csproj +++ b/InvestmentManager/InvestmentManager/InvestmentManager.csproj @@ -12,6 +12,7 @@ + diff --git a/InvestmentManager/InvestmentManager/Program.cs b/InvestmentManager/InvestmentManager/Program.cs index 477c408..a07add4 100644 --- a/InvestmentManager/InvestmentManager/Program.cs +++ b/InvestmentManager/InvestmentManager/Program.cs @@ -1,7 +1,9 @@ +using AspNetCoreRateLimit; using HealthChecks.UI.Client; using InvestmentManager.Core; using InvestmentManager.DataAccess.EF; using InvestmentManager.HealthChecks; +using InvestmentManager.RateLimit; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Http; @@ -126,6 +128,12 @@ options.AddHealthCheckEndpoint(" HC UI endpoint", "https://localhost:51500/healthui"); }).AddInMemoryStorage(); +// Add and configure services required for AspNetCoreRateLimit +RateLimit.ConfigureServices(builder.Services, builder.Configuration); +// This is required to set the default value for AspNetCoreRateLimit.IProcessingStrategy +builder.Services.AddSingleton(); + + var app = builder.Build(); // Configure the HTTP request pipeline. @@ -144,6 +152,7 @@ app.UseCors(); app.UseAuthentication(); app.UseAuthorization(); +app.UseIpRateLimiting(); app.UseEndpoints(endpoints => { diff --git a/InvestmentManager/InvestmentManager/RateLimit/RateLimit.cs b/InvestmentManager/InvestmentManager/RateLimit/RateLimit.cs new file mode 100644 index 0000000..04ae75c --- /dev/null +++ b/InvestmentManager/InvestmentManager/RateLimit/RateLimit.cs @@ -0,0 +1,30 @@ +using AspNetCoreRateLimit; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace InvestmentManager.RateLimit +{ + public static class RateLimit + { + public static void ConfigureServices(IServiceCollection services, IConfiguration configuration) + { + // needed to load configuration from appsettings.json + services.AddOptions(); + // needed to store rate limit counters and ip rules + services.AddMemoryCache(); + //load general configuration from appsettings.json + services.Configure(configuration.GetSection("IpRateLimiting")); + //load ip rules from appsettings.json + services.Configure(configuration.GetSection("IpRateLimitPolicies")); + // inject counter and rules stores + services.AddSingleton(); + services.AddSingleton(); + // Add framework services. + services.AddMvc(); + services.AddSingleton(); + // configuration (resolvers, counter key builders) + services.AddSingleton(); + } + } +} diff --git a/InvestmentManager/InvestmentManager/appsettings.json b/InvestmentManager/InvestmentManager/appsettings.json index c7069f1..bb435cd 100644 --- a/InvestmentManager/InvestmentManager/appsettings.json +++ b/InvestmentManager/InvestmentManager/appsettings.json @@ -32,5 +32,22 @@ ], "EvaluationTimeOnSeconds": 10, "MinimumSecondsBetweenFailureNotifications": 60 + }, + "IpRateLimiting": { + "EnableEndpointRateLimiting": true, + "StackBlockedRequests": true, + "RealIpHeader": "X-Real-IP", + "ClientIdHeader": "X-ClientId", + "HttpStatusCode": 429, + "IpWhitelist": [ "23" ], + "EndpointWhitelist": [ "get:/api/license", "*:/api/status" ], + "ClientWhitelist": [ "test4", "test3" ], + "GeneralRules": [ + { + "Endpoint": "*:/health/ready", + "Period": "10s", + "Limit": 1 + } + ] } }