diff --git a/src/Core/Settings/GlobalSettings.cs b/src/Core/Settings/GlobalSettings.cs index 533a8bfb5b..d32aab654a 100644 --- a/src/Core/Settings/GlobalSettings.cs +++ b/src/Core/Settings/GlobalSettings.cs @@ -520,6 +520,7 @@ public class GlobalSettings : IGlobalSettings public class DistributedIpRateLimitingSettings { + public string RedisConnectionString { get; set; } public bool Enabled { get; set; } = true; /// diff --git a/src/Core/Utilities/CustomRedisProcessingStrategy.cs b/src/Core/Utilities/CustomRedisProcessingStrategy.cs index d3883f701f..12a48e400f 100644 --- a/src/Core/Utilities/CustomRedisProcessingStrategy.cs +++ b/src/Core/Utilities/CustomRedisProcessingStrategy.cs @@ -2,6 +2,7 @@ using AspNetCoreRateLimit.Redis; using Bit.Core.Settings; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using StackExchange.Redis; @@ -26,6 +27,7 @@ public class CustomRedisProcessingStrategy : RedisProcessingStrategy private const string _redisTimeoutCacheKey = "IpRateLimitRedisTimeout"; public CustomRedisProcessingStrategy( + [FromKeyedServices("rate-limiter")] IConnectionMultiplexer connectionMultiplexer, IRateLimitConfiguration config, ILogger logger, diff --git a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs index 515eae6cbe..2f26c49393 100644 --- a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs +++ b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs @@ -49,7 +49,6 @@ 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.Hosting; @@ -667,7 +666,8 @@ public static class ServiceCollectionExtensions services.AddHostedService(); services.AddSingleton(); - if (!globalSettings.DistributedIpRateLimiting.Enabled || string.IsNullOrEmpty(globalSettings.Redis.ConnectionString)) + if (!globalSettings.DistributedIpRateLimiting.Enabled || + string.IsNullOrEmpty(globalSettings.DistributedIpRateLimiting.RedisConnectionString)) { services.AddInMemoryRateLimiting(); } @@ -679,7 +679,8 @@ public static class ServiceCollectionExtensions services.AddSingleton(); // Use a custom Redis processing strategy that skips Ip limiting if Redis is down - // Requires a registered IConnectionMultiplexer + services.AddKeyedSingleton("rate-limiter", (_, provider) => + ConnectionMultiplexer.Connect(globalSettings.DistributedIpRateLimiting.RedisConnectionString)); services.AddSingleton(); } } @@ -698,22 +699,9 @@ public static class ServiceCollectionExtensions return; } - // Register the IConnectionMultiplexer explicitly so it can be accessed via DI - // (e.g. for the IP rate limiting store) - services.AddSingleton( - _ => 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(s => + services.AddStackExchangeRedisCache(options => { - 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()) - }); + options.Configuration = globalSettings.Redis.ConnectionString; }); }