mirror of
https://github.com/bitwarden/server.git
synced 2025-07-01 16:12:49 -05:00
[PS-93] Distributed Ip rate limiting (#2060)
* Upgrade AspNetCoreRateLimiter and enable redis distributed cache for rate limiting. - Upgrades AspNetCoreRateLimiter to 4.0.2, which required updating NewtonSoft.Json to 13.0.1. - Replaces Microsoft.Extensions.Caching.Redis with Microsoft.Extensions.Caching.StackExchangeRedis as the original was deprecated and conflicted with the latest AspNetCoreRateLimiter - Adds startup task to Program.cs for Api/Identity projects to support AspNetCoreRateLimiters breaking changes for seeding its stores. - Adds a Redis connection string option to GlobalSettings Signed-off-by: Shane Melton <smelton@bitwarden.com> * Cleanup Redis distributed cache registration - Add new AddDistributedCache service collection extension to add either a Memory or Redis distributed cache. - Remove distributed cache registration from Identity service collection extension. - Add IpRateLimitSeedStartupService.cs to run at application startup to seed the Ip rate limiting policies. Signed-off-by: Shane Melton <smelton@bitwarden.com> * Add caching configuration to SSO Startup.cs Signed-off-by: Shane Melton <smelton@bitwarden.com> * Add ProjectName as an instance name for Redis options Signed-off-by: Shane Melton <smelton@bitwarden.com> * Use distributed cache in CustomIpRateLimitMiddleware.cs Signed-off-by: Shane Melton <smelton@bitwarden.com> * Undo changes to Program.cs and launchSettings.json * Move new service collection extensions to SharedWeb * Upgrade Caching.StackExchangeRedis package to v6 * Cleanup and fix leftover merge conflicts * Remove use of Newtonsoft.Json in distributed cache extensions * Cleanup more formatting * Fix formatting * Fix startup issue caused by merge and fix integration test Signed-off-by: Shane Melton <smelton@bitwarden.com> * Linting fix Signed-off-by: Shane Melton <smelton@bitwarden.com>
This commit is contained in:
@ -1,8 +1,11 @@
|
||||
using System.Reflection;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using AspNetCoreRateLimit;
|
||||
using AspNetCoreRateLimit.Redis;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.HostedServices;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Models.Business.Tokenables;
|
||||
@ -27,13 +30,17 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc.Localization;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Caching.StackExchangeRedis;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Serilog.Context;
|
||||
using StackExchange.Redis;
|
||||
using NoopRepos = Bit.Core.Repositories.Noop;
|
||||
using Role = Bit.Core.Entities.Role;
|
||||
using TableStorageRepos = Bit.Core.Repositories.TableStorage;
|
||||
|
||||
namespace Bit.SharedWeb.Utilities
|
||||
@ -566,16 +573,6 @@ namespace Bit.SharedWeb.Utilities
|
||||
|
||||
public static IServiceCollection AddDistributedIdentityServices(this IServiceCollection services, GlobalSettings globalSettings)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(globalSettings.IdentityServer?.RedisConnectionString))
|
||||
{
|
||||
services.AddDistributedMemoryCache();
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddDistributedRedisCache(options =>
|
||||
options.Configuration = globalSettings.IdentityServer.RedisConnectionString);
|
||||
}
|
||||
|
||||
services.AddOidcStateDataFormatterCache();
|
||||
services.AddSession();
|
||||
services.ConfigureApplicationCookie(configure => configure.CookieManager = new DistributedCacheCookieManager());
|
||||
@ -600,5 +597,59 @@ namespace Bit.SharedWeb.Utilities
|
||||
options.TimestampDriftTolerance = 300000;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds either an in-memory or distributed IP rate limiter depending if a Redis connection string is available.
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="globalSettings"></param>
|
||||
public static void AddIpRateLimiting(this IServiceCollection services,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
services.AddHostedService<IpRateLimitSeedStartupService>();
|
||||
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
|
||||
|
||||
if (string.IsNullOrEmpty(globalSettings.Redis.ConnectionString))
|
||||
{
|
||||
services.AddInMemoryRateLimiting();
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddRedisRateLimiting(); // Requires a registered IConnectionMultiplexer
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an implementation of <see cref="IDistributedCache"/> to the service collection. Uses a memory
|
||||
/// cache if self hosted or no Redis connection string is available in GlobalSettings.
|
||||
/// </summary>
|
||||
public static void AddDistributedCache(
|
||||
this IServiceCollection services,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
if (globalSettings.SelfHosted || string.IsNullOrEmpty(globalSettings.Redis.ConnectionString))
|
||||
{
|
||||
services.AddDistributedMemoryCache();
|
||||
return;
|
||||
}
|
||||
|
||||
// Register the IConnectionMultiplexer explicitly so it can be accessed via DI
|
||||
// (e.g. for the IP rate limiting store)
|
||||
services.AddSingleton<IConnectionMultiplexer>(
|
||||
_ => ConnectionMultiplexer.Connect(globalSettings.Redis.ConnectionString));
|
||||
|
||||
// Explicitly register IDistributedCache to re-use existing IConnectionMultiplexer
|
||||
// to reduce the number of redundant connections to the Redis instance
|
||||
services.AddSingleton<IDistributedCache>(s =>
|
||||
{
|
||||
return new RedisCache(new RedisCacheOptions
|
||||
{
|
||||
// Use "ProjectName:" as an instance name to namespace keys and avoid conflicts between projects
|
||||
InstanceName = $"{globalSettings.ProjectName}:",
|
||||
ConnectionMultiplexerFactory = () =>
|
||||
Task.FromResult(s.GetRequiredService<IConnectionMultiplexer>())
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,15 @@
|
||||
"Newtonsoft.Json": "13.0.1"
|
||||
}
|
||||
},
|
||||
"AspNetCoreRateLimit.Redis": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "CsSGy/7SXt6iBOKg0xCvsRjb/ZHshbtr2Of1MHc912L2sLnZqadUrTboyXZC+ZlgEBeJ14GyjPTu8ZyfEhGUnw==",
|
||||
"dependencies": {
|
||||
"AspNetCoreRateLimit": "4.0.2",
|
||||
"StackExchange.Redis": "2.5.43"
|
||||
}
|
||||
},
|
||||
"AutoMapper": {
|
||||
"type": "Transitive",
|
||||
"resolved": "11.0.0",
|
||||
@ -483,14 +492,14 @@
|
||||
"Microsoft.Extensions.Primitives": "6.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Redis": {
|
||||
"Microsoft.Extensions.Caching.StackExchangeRedis": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "cb21miiGDVjlNl8TRBKIi7OEFdlKuV8d4ZoYqFOhKdZhzo7Sv+b8Puy3NLW3y/g+UDclt7FTh+Za7ykurtaVMQ==",
|
||||
"resolved": "6.0.6",
|
||||
"contentHash": "bdVQpYm1hcHf0pyAypMjtDw3HjWQJ89UzloyyF1OBs56QlgA1naM498tP2Vjlho5vVRALMGPYzdRKCen8koubw==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Caching.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Options": "2.2.0",
|
||||
"StackExchange.Redis.StrongName": "1.2.6"
|
||||
"Microsoft.Extensions.Caching.Abstractions": "6.0.0",
|
||||
"Microsoft.Extensions.Options": "6.0.0",
|
||||
"StackExchange.Redis": "2.2.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration": {
|
||||
@ -736,8 +745,8 @@
|
||||
},
|
||||
"Microsoft.NETCore.Platforms": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.1.1",
|
||||
"contentHash": "TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ=="
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ=="
|
||||
},
|
||||
"Microsoft.NETCore.Targets": {
|
||||
"type": "Transitive",
|
||||
@ -775,11 +784,11 @@
|
||||
},
|
||||
"Microsoft.Win32.Registry": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.7.0",
|
||||
"contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
|
||||
"dependencies": {
|
||||
"System.Security.AccessControl": "4.7.0",
|
||||
"System.Security.Principal.Windows": "4.7.0"
|
||||
"System.Security.AccessControl": "5.0.0",
|
||||
"System.Security.Principal.Windows": "5.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Win32.SystemEvents": {
|
||||
@ -890,6 +899,14 @@
|
||||
"resolved": "1.2.2",
|
||||
"contentHash": "2hrZfkbzeWJ3tNXXt/1beg4IY+nS4F3gIfh4NVFvW0f6Pj51hGpiJ4prBz7Dmrr4ZYrA96rTERVGieZ4xYm7jA=="
|
||||
},
|
||||
"Pipelines.Sockets.Unofficial": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.2",
|
||||
"contentHash": "Bhk0FWxH1paI+18zr1g5cTL+ebeuDcBCR+rRFO+fKEhretgjs7MF2Mc1P64FGLecWp4zKCUOPzngBNrqVyY7Zg==",
|
||||
"dependencies": {
|
||||
"System.IO.Pipelines": "5.0.1"
|
||||
}
|
||||
},
|
||||
"Pomelo.EntityFrameworkCore.MySql": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.1",
|
||||
@ -1227,34 +1244,13 @@
|
||||
"Serilog.Sinks.PeriodicBatching": "2.3.0"
|
||||
}
|
||||
},
|
||||
"StackExchange.Redis.StrongName": {
|
||||
"StackExchange.Redis": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.2.6",
|
||||
"contentHash": "UFmT1/JYu1PLiRwkyvEPVHk/tVTJa8Ka2rb9yzidzDoQARvhBVRpaWUeaP81373v54jupDBvAoGHGl0EY/HphQ==",
|
||||
"resolved": "2.5.43",
|
||||
"contentHash": "YQ38jVbX1b5mBi6lizESou+NpV6QZpeo6ofRR6qeuqJ8ePOmhcwhje3nDTNIGEkfPSK0sLuF6pR5rtFyq2F46g==",
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "1.6.1",
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Collections.Concurrent": "4.3.0",
|
||||
"System.Collections.NonGeneric": "4.3.0",
|
||||
"System.Diagnostics.Tools": "4.3.0",
|
||||
"System.IO.Compression": "4.3.0",
|
||||
"System.IO.FileSystem": "4.3.0",
|
||||
"System.Linq": "4.3.0",
|
||||
"System.Net.NameResolution": "4.3.0",
|
||||
"System.Net.Security": "4.3.0",
|
||||
"System.Net.Sockets": "4.3.0",
|
||||
"System.Reflection.Emit": "4.3.0",
|
||||
"System.Reflection.Emit.Lightweight": "4.3.0",
|
||||
"System.Reflection.TypeExtensions": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Runtime.InteropServices.RuntimeInformation": "4.3.0",
|
||||
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||
"System.Security.Cryptography.X509Certificates": "4.3.0",
|
||||
"System.Text.RegularExpressions": "4.3.0",
|
||||
"System.Threading": "4.3.0",
|
||||
"System.Threading.Thread": "4.3.0",
|
||||
"System.Threading.ThreadPool": "4.3.0",
|
||||
"System.Threading.Timer": "4.3.0"
|
||||
"Pipelines.Sockets.Unofficial": "2.2.2",
|
||||
"System.Diagnostics.PerformanceCounter": "5.0.0"
|
||||
}
|
||||
},
|
||||
"starkbank-ecdsa": {
|
||||
@ -1321,15 +1317,15 @@
|
||||
},
|
||||
"System.Collections.NonGeneric": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==",
|
||||
"resolved": "4.0.1",
|
||||
"contentHash": "hMxFT2RhhlffyCdKLDXjx8WEC5JfCvNozAZxCablAuFRH74SCV4AgzE8yJCh/73bFnEoZgJ9MJmkjQ0dJmnKqA==",
|
||||
"dependencies": {
|
||||
"System.Diagnostics.Debug": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Threading": "4.3.0"
|
||||
"System.Diagnostics.Debug": "4.0.11",
|
||||
"System.Globalization": "4.0.11",
|
||||
"System.Resources.ResourceManager": "4.0.1",
|
||||
"System.Runtime": "4.1.0",
|
||||
"System.Runtime.Extensions": "4.1.0",
|
||||
"System.Threading": "4.0.11"
|
||||
}
|
||||
},
|
||||
"System.Collections.Specialized": {
|
||||
@ -1400,6 +1396,17 @@
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Diagnostics.PerformanceCounter": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "kcQWWtGVC3MWMNXdMDWfrmIlFZZ2OdoeT6pSNVRtk9+Sa7jwdPiMlNwb0ZQcS7NRlT92pCfmjRtkSWUW3RAKwg==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "5.0.0",
|
||||
"Microsoft.Win32.Registry": "5.0.0",
|
||||
"System.Configuration.ConfigurationManager": "5.0.0",
|
||||
"System.Security.Principal.Windows": "5.0.0"
|
||||
}
|
||||
},
|
||||
"System.Diagnostics.Process": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1625,6 +1632,11 @@
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "Rfm2jYCaUeGysFEZjDe7j1R4x6Z6BzumS/vUT5a1AA/AWJuGX71PoGB0RmpyX3VmrGqVnAwtfMn39OHR8Y/5+g=="
|
||||
},
|
||||
"System.IO.Pipelines": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.1",
|
||||
"contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg=="
|
||||
},
|
||||
"System.Linq": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1725,23 +1737,23 @@
|
||||
},
|
||||
"System.Net.NameResolution": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "AFYl08R7MrsrEjqpQWTZWBadqXyTzNDaWpMqyxhb0d6sGhV6xMDKueuBXlLL30gz+DIRY6MpdgnHWlCh5wmq9w==",
|
||||
"resolved": "4.0.0",
|
||||
"contentHash": "JdqRdM1Qym3YehqdKIi5LHrpypP4JMfxKQSNCJ2z4WawkG0il+N3XfNeJOxll2XrTnG7WgYYPoeiu/KOwg0DQw==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Diagnostics.Tracing": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.Net.Primitives": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Runtime.Handles": "4.3.0",
|
||||
"System.Runtime.InteropServices": "4.3.0",
|
||||
"System.Security.Principal.Windows": "4.3.0",
|
||||
"System.Threading": "4.3.0",
|
||||
"System.Threading.Tasks": "4.3.0",
|
||||
"runtime.native.System": "4.3.0"
|
||||
"Microsoft.NETCore.Platforms": "1.0.1",
|
||||
"System.Collections": "4.0.11",
|
||||
"System.Diagnostics.Tracing": "4.1.0",
|
||||
"System.Globalization": "4.0.11",
|
||||
"System.Net.Primitives": "4.0.11",
|
||||
"System.Resources.ResourceManager": "4.0.1",
|
||||
"System.Runtime": "4.1.0",
|
||||
"System.Runtime.Extensions": "4.1.0",
|
||||
"System.Runtime.Handles": "4.0.1",
|
||||
"System.Runtime.InteropServices": "4.1.0",
|
||||
"System.Security.Principal.Windows": "4.0.0",
|
||||
"System.Threading": "4.0.11",
|
||||
"System.Threading.Tasks": "4.0.11",
|
||||
"runtime.native.System": "4.0.0"
|
||||
}
|
||||
},
|
||||
"System.Net.NetworkInformation": {
|
||||
@ -2317,8 +2329,8 @@
|
||||
},
|
||||
"System.Security.Principal.Windows": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.7.0",
|
||||
"contentHash": "ojD0PX0XhneCsUbAZVKdb7h/70vyYMDYs85lwEI+LngEONe/17A0cFaRFqZU+sOEidcVswYWikYOQ9PPfjlbtQ=="
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
|
||||
},
|
||||
"System.Security.SecureString": {
|
||||
"type": "Transitive",
|
||||
@ -2585,6 +2597,7 @@
|
||||
"AWSSDK.SQS": "3.7.2.47",
|
||||
"AWSSDK.SimpleEmail": "3.7.0.150",
|
||||
"AspNetCoreRateLimit": "4.0.2",
|
||||
"AspNetCoreRateLimit.Redis": "1.0.1",
|
||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "1.2.1",
|
||||
"Azure.Storage.Blobs": "12.11.0",
|
||||
"Azure.Storage.Queues": "12.9.0",
|
||||
@ -2599,7 +2612,7 @@
|
||||
"Microsoft.Azure.Cosmos.Table": "1.0.8",
|
||||
"Microsoft.Azure.NotificationHubs": "4.1.0",
|
||||
"Microsoft.Azure.ServiceBus": "5.2.0",
|
||||
"Microsoft.Extensions.Caching.Redis": "2.2.0",
|
||||
"Microsoft.Extensions.Caching.StackExchangeRedis": "6.0.6",
|
||||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.1",
|
||||
"Microsoft.Extensions.Configuration.UserSecrets": "6.0.1",
|
||||
"Microsoft.Extensions.Identity.Stores": "6.0.4",
|
||||
@ -2620,7 +2633,7 @@
|
||||
"infrastructure.dapper": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Core": "2022.5.2",
|
||||
"Core": "2022.6.0",
|
||||
"Dapper": "2.0.123",
|
||||
"System.Data.SqlClient": "4.8.3"
|
||||
}
|
||||
@ -2629,7 +2642,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "11.0.0",
|
||||
"Core": "2022.5.2",
|
||||
"Core": "2022.6.0",
|
||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.4",
|
||||
"Npgsql.EntityFrameworkCore.PostgreSQL": "6.0.4",
|
||||
"Pomelo.EntityFrameworkCore.MySql": "6.0.1",
|
||||
|
Reference in New Issue
Block a user