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
10 changes: 5 additions & 5 deletions src/OrchardCoreContrib.Garnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,22 @@ This module has no dependencies.
2. Go to the admin site
3. Select **Configuration -> Features** menu.

## Garnet
#### Garnet

4. Enable the `Garnet` feature.

## Garnet Cache
#### Garnet Cache

4. Enable the `Garnet Cache` feature.

## Garnet Bus
#### Garnet Bus

4. Enable the `Garnet Bus` feature.

## Garnet DataProtection
#### Garnet DataProtection

4. Enable the `Garnet DataProtection` feature.

## Garnet Lock
#### Garnet Lock

4. Enable the `Garnet Lock` feature.
13 changes: 12 additions & 1 deletion src/OrchardCoreContrib.HealthChecks/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,16 @@

public class Constants
{
public const string ConfigurationKey = "OrchardCoreContrib_HealthChecks";
/// <summary>
/// Defines the configuration key for health checks settings in the Orchard Core configuration system.
/// </summary>
public const string HealthChecksConfigurationKey = "OrchardCoreContrib_HealthChecks";
/// <summary>
/// Defines the configuration key for health checks access settings in the Orchard Core configuration system.
/// </summary>
public const string HealthChecksAccessConfigurationKey = "OrchardCoreContrib_HealthChecks:Access";
/// <summary>
/// Defines the configuration key for health checks rate limiting settings in the Orchard Core configuration system.
/// </summary>
public const string HealthChecksRateLimitingConfigurationKey = "OrchardCoreContrib_HealthChecks:RateLimiting";
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
namespace OrchardCoreContrib.HealthChecks;

/// <summary>
/// Provides configuration options for controlling access to health check endpoints, such as allowed IP addresses.
/// </summary>
public class HealthChecksAccessOptions
{
/// <summary>
/// Gets or sets the collection of IP addresses that are permitted access.
/// </summary>
public HashSet<string> AllowedIPs { get; set; } = [];
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

namespace OrchardCoreContrib.HealthChecks;

/// <summary>
/// Middleware that enforces rate limiting and temporary blocking for health check endpoints based on client IP address.
/// </summary>
public class HealthChecksBlockingRateLimitingMiddleware
{
private static readonly ConcurrentDictionary<string, DateTime> _blockedIPs = new();
Expand Down Expand Up @@ -39,6 +42,7 @@ public HealthChecksBlockingRateLimitingMiddleware(
});
}

/// <inheritdoc/>
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Path.Equals(_healthChecksOptions.Url))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
namespace OrchardCoreContrib.HealthChecks;

/// <summary>
/// Provides configuration options for blocking rate limiting applied to health check endpoints.
/// </summary>
public class HealthChecksBlockingRateLimitingOptions : HealthChecksRateLimitingOptions
{
/// <summary>
/// Gets or sets the duration for which a client is blocked when the rate limit is exceeded. Defaults to 1 minute.
/// </summary>
public TimeSpan BlockDuration { get; set; } = TimeSpan.FromMinutes(1);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

namespace OrchardCoreContrib.HealthChecks;

/// <summary>
/// Middleware that restricts access to health check endpoints based on allowed IP addresses.
/// </summary>
/// <param name="next">The <see cref="RequestDelegate"/> representing the next middleware in the pipeline.</param>
/// <param name="healthChecksOptions">The <see cref="HealthChecksOptions"/> containing health check configuration.</param>
/// <param name="healthChecksAccessOptions">The <see cref="HealthChecksAccessOptions"/> containing IP access configuration.</param>
/// <param name="logger">The <see cref="ILogger{HealthChecksIPRestrictionMiddleware}"/> used for logging.</param>
public class HealthChecksIPRestrictionMiddleware(
RequestDelegate next,
IOptions<HealthChecksOptions> healthChecksOptions,
Expand All @@ -12,6 +19,7 @@ public class HealthChecksIPRestrictionMiddleware(
{
private readonly HealthChecksOptions _healthChecksOptions = healthChecksOptions.Value;

/// <inheritdoc/>
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Path.Equals(_healthChecksOptions.Url))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

namespace OrchardCoreContrib.HealthChecks;

/// <summary>
/// Middleware that enforces rate limiting on health check endpoints to prevent excessive requests.
/// </summary>
public class HealthChecksRateLimitingMiddleware
{
private readonly RequestDelegate _next;
Expand Down Expand Up @@ -33,6 +36,7 @@ public HealthChecksRateLimitingMiddleware(
});
}

/// <inheritdoc/>
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Path.Equals(_healthChecksOptions.Url))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
namespace OrchardCoreContrib.HealthChecks;

/// <summary>
/// Provides configuration options for rate limiting applied to health check endpoints.
/// </summary>
public class HealthChecksRateLimitingOptions
{
/// <summary>
/// Gets or sets the maximum number of concurrent permits that can be acquired. Defaults to 5.
/// </summary>
public int PermitLimit { get; set; } = 5;

/// <summary>
/// Gets or sets the time window for which the rate limiting is applied. Defaults to 10 seconds.
/// </summary>
public TimeSpan Window { get; set; } = TimeSpan.FromSeconds(10);

/// <summary>
/// Gets or sets the number of segments the time window is divided into for rate limiting purposes. Defaults to 10.
/// </summary>
public int SegmentsPerWindow { get; set; } = 10;

/// <summary>
/// Gets or sets the maximum number of requests that can be queued when the rate limit is exceeded. Defaults to 0.
/// </summary>
public int QueueLimit { get; set; } = 0;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.Environment.Shell.Configuration;
using OrchardCore.Modules;

namespace OrchardCoreContrib.HealthChecks;

/// <summary>
/// Configures blocking rate limiting for health check endpoints during application startup.
/// </summary>
/// <param name="shellConfiguration">The <see cref="IShellConfiguration"/>.</param>
[Feature("OrchardCoreContrib.HealthChecks.BlockingRateLimiting")]
public class BlockingRateLimitingStartup(IShellConfiguration shellConfiguration) : StartupBase
{
/// <inheritdoc/>
public override int Order => 20;

/// <inheritdoc/>
public override void ConfigureServices(IServiceCollection services)
{
var rateLimitingSection = shellConfiguration.GetSection(Constants.HealthChecksRateLimitingConfigurationKey);

services.Configure<HealthChecksRateLimitingOptions>(rateLimitingSection);
services.Configure<HealthChecksBlockingRateLimitingOptions>(rateLimitingSection);
}

/// <inheritdoc/>
public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
=> app.UseMiddleware<HealthChecksBlockingRateLimitingMiddleware>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.Environment.Shell.Configuration;
using OrchardCore.Modules;

namespace OrchardCoreContrib.HealthChecks;

/// <summary>
/// Configures IP-based access restrictions for health check endpoints during application startup.
/// </summary>
/// <param name="shellConfiguration">The <see cref="IShellConfiguration"/>.</param>
[Feature("OrchardCoreContrib.HealthChecks.IPRestriction")]
public class IPRestrictionStartup(IShellConfiguration shellConfiguration) : StartupBase
{
/// <inheritdoc/>
public override int Order => 10;

/// <inheritdoc/>
public override void ConfigureServices(IServiceCollection services)
=> services.Configure<HealthChecksAccessOptions>(shellConfiguration.GetSection(Constants.HealthChecksAccessConfigurationKey));

/// <inheritdoc/>
public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
=> app.UseMiddleware<HealthChecksIPRestrictionMiddleware>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.Environment.Shell.Configuration;
using OrchardCore.Modules;

namespace OrchardCoreContrib.HealthChecks;

/// <summary>
/// Configures rate limiting for health check endpoints during application startup.
/// </summary>
/// <param name="shellConfiguration">The <see cref="IShellConfiguration"/>.</param>
[Feature("OrchardCoreContrib.HealthChecks.RateLimiting")]
public class RateLimitingStartup(IShellConfiguration shellConfiguration) : StartupBase
{
/// <inheritdoc/>
public override int Order => 30;

/// <inheritdoc/>
public override void ConfigureServices(IServiceCollection services)
=> services.Configure<HealthChecksRateLimitingOptions>(shellConfiguration.GetSection(Constants.HealthChecksRateLimitingConfigurationKey));

/// <inheritdoc/>
public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
=> app.UseMiddleware<HealthChecksRateLimitingMiddleware>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,23 @@

namespace OrchardCoreContrib.HealthChecks;

/// <summary>
/// Configures health check endpoints during application startup.
/// </summary>
/// <param name="shellConfiguration">The <see cref="IShellConfiguration"/>.</param>
public class Startup(IShellConfiguration shellConfiguration) : StartupBase
{
private static readonly JsonSerializerOptions _jsonSerializerOptions = new() { WriteIndented = true };

/// <inheritdoc/>
public override void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks();

services.Configure<HealthChecksOptions>(shellConfiguration.GetSection(Constants.ConfigurationKey));
services.Configure<HealthChecksOptions>(shellConfiguration.GetSection(Constants.HealthChecksConfigurationKey));
}

/// <inheritdoc/>
public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
{
var healthChecksOptions = serviceProvider.GetService<IOptions<HealthChecksOptions>>().Value;
Expand Down Expand Up @@ -67,45 +73,3 @@ private static async Task WriteResponse(HttpContext context, HealthReport report
await context.Response.WriteAsync(JsonSerializer.Serialize(response, response.GetType(), options: _jsonSerializerOptions));
}
}

[Feature("OrchardCoreContrib.HealthChecks.IPRestriction")]
public class IPRestrictionStartup(IShellConfiguration shellConfiguration) : StartupBase
{
public override int Order => 10;

public override void ConfigureServices(IServiceCollection services)
=> services.Configure<HealthChecksAccessOptions>(shellConfiguration.GetSection($"{Constants.ConfigurationKey}:Access"));

public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
=> app.UseMiddleware<HealthChecksIPRestrictionMiddleware>();
}

[Feature("OrchardCoreContrib.HealthChecks.RateLimiting")]
public class RateLimitingStartup(IShellConfiguration shellConfiguration) : StartupBase
{
public override int Order => 30;

public override void ConfigureServices(IServiceCollection services)
=> services.Configure<HealthChecksRateLimitingOptions>(shellConfiguration.GetSection($"{Constants.ConfigurationKey}:RateLimiting"));

public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
=> app.UseMiddleware<HealthChecksRateLimitingMiddleware>();
}

[Feature("OrchardCoreContrib.HealthChecks.BlockingRateLimiting")]
public class BlockingRateLimitingStartup(IShellConfiguration shellConfiguration) : StartupBase
{
public override int Order => 20;

public override void ConfigureServices(IServiceCollection services)
{
var rateLimitingSection = shellConfiguration.GetSection($"{Constants.ConfigurationKey}:RateLimiting");

services.Configure<HealthChecksRateLimitingOptions>(rateLimitingSection);
services.Configure<HealthChecksBlockingRateLimitingOptions>(rateLimitingSection);
}

public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
=> app.UseMiddleware<HealthChecksBlockingRateLimitingMiddleware>();
}

Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ private IConfigurationRoot AddHealthChecksConfiguration()
{
var newConfiguration = new Dictionary<string, string>
{
{ $"{Constants.ConfigurationKey}:{nameof(HealthChecksOptions.Url)}", "/health" },
{ $"{Constants.ConfigurationKey}:Access:AllowedIPs:0", "127.0.0.1" },
{ $"{Constants.ConfigurationKey}:Access:AllowedIPs:1", "::1" }
{ $"{Constants.HealthChecksConfigurationKey}:{nameof(HealthChecksOptions.Url)}", "/health" },
{ $"{Constants.HealthChecksAccessConfigurationKey}:AllowedIPs:0", "127.0.0.1" },
{ $"{Constants.HealthChecksAccessConfigurationKey}:AllowedIPs:1", "::1" }
};

return new ConfigurationBuilder()
Expand Down
Loading