From 473bc2e9e62f0bce18d8e8c8021a09f9019c4464 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Fri, 17 Apr 2026 20:31:37 +0300 Subject: [PATCH 1/2] Add docs for Health Checks module --- src/OrchardCoreContrib.Garnet/README.md | 10 ++-- .../Constants.cs | 13 ++++- .../HealthChecksAccessOptions.cs | 6 +++ ...lthChecksBlockingRateLimitingMiddleware.cs | 4 ++ ...HealthChecksBlockingRateLimitingOptions.cs | 6 +++ .../HealthChecksIPRestrictionMiddleware.cs | 8 +++ .../HealthChecksRateLimitingMiddleware.cs | 4 ++ .../HealthChecksRateLimitingOptions.cs | 15 ++++++ .../Startups/BlockingRateLimitingStartup.cs | 31 ++++++++++++ .../Startups/IPRestrictionStartup.cs | 26 ++++++++++ .../Startups/RateLimitingStartup.cs | 26 ++++++++++ .../{ => Startups}/Startup.cs | 50 +++---------------- 12 files changed, 150 insertions(+), 49 deletions(-) create mode 100644 src/OrchardCoreContrib.HealthChecks/Startups/BlockingRateLimitingStartup.cs create mode 100644 src/OrchardCoreContrib.HealthChecks/Startups/IPRestrictionStartup.cs create mode 100644 src/OrchardCoreContrib.HealthChecks/Startups/RateLimitingStartup.cs rename src/OrchardCoreContrib.HealthChecks/{ => Startups}/Startup.cs (55%) diff --git a/src/OrchardCoreContrib.Garnet/README.md b/src/OrchardCoreContrib.Garnet/README.md index 8f955e8f..d76e1382 100644 --- a/src/OrchardCoreContrib.Garnet/README.md +++ b/src/OrchardCoreContrib.Garnet/README.md @@ -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. diff --git a/src/OrchardCoreContrib.HealthChecks/Constants.cs b/src/OrchardCoreContrib.HealthChecks/Constants.cs index ea6d4882..233cabda 100644 --- a/src/OrchardCoreContrib.HealthChecks/Constants.cs +++ b/src/OrchardCoreContrib.HealthChecks/Constants.cs @@ -2,5 +2,16 @@ public class Constants { - public const string ConfigurationKey = "OrchardCoreContrib_HealthChecks"; + /// + /// Defines the configuration key for health checks settings in the Orchard Core configuration system. + /// + public const string HealthChecksConfigurationKey = "OrchardCoreContrib_HealthChecks"; + /// + /// Defines the configuration key for health checks access settings in the Orchard Core configuration system. + /// + public const string HealthChecksAccessConfigurationKey = "OrchardCoreContrib_HealthChecks:Access"; + /// + /// Defines the configuration key for health checks rate limiting settings in the Orchard Core configuration system. + /// + public const string HealthChecksRateLimitingConfigurationKey = "OrchardCoreContrib_HealthChecks:RateLimiting"; } diff --git a/src/OrchardCoreContrib.HealthChecks/HealthChecksAccessOptions.cs b/src/OrchardCoreContrib.HealthChecks/HealthChecksAccessOptions.cs index 025d8b24..c77775b8 100644 --- a/src/OrchardCoreContrib.HealthChecks/HealthChecksAccessOptions.cs +++ b/src/OrchardCoreContrib.HealthChecks/HealthChecksAccessOptions.cs @@ -1,6 +1,12 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Provides configuration options for controlling access to health check endpoints, such as allowed IP addresses. +/// public class HealthChecksAccessOptions { + /// + /// Gets or sets the collection of IP addresses that are permitted access. + /// public HashSet AllowedIPs { get; set; } = []; } diff --git a/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingMiddleware.cs b/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingMiddleware.cs index c3817dc9..3744ea8f 100644 --- a/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingMiddleware.cs +++ b/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingMiddleware.cs @@ -6,6 +6,9 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Middleware that enforces rate limiting and temporary blocking for health check endpoints based on client IP address. +/// public class HealthChecksBlockingRateLimitingMiddleware { private static readonly ConcurrentDictionary _blockedIPs = new(); @@ -39,6 +42,7 @@ public HealthChecksBlockingRateLimitingMiddleware( }); } + /// public async Task InvokeAsync(HttpContext context) { if (context.Request.Path.Equals(_healthChecksOptions.Url)) diff --git a/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingOptions.cs b/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingOptions.cs index 1524d12d..782ca653 100644 --- a/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingOptions.cs +++ b/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingOptions.cs @@ -1,6 +1,12 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Provides configuration options for blocking rate limiting applied to health check endpoints. +/// public class HealthChecksBlockingRateLimitingOptions : HealthChecksRateLimitingOptions { + /// + /// Gets or sets the duration for which a client is blocked when the rate limit is exceeded. Defaults to 1 minute. + /// public TimeSpan BlockDuration { get; set; } = TimeSpan.FromMinutes(1); } diff --git a/src/OrchardCoreContrib.HealthChecks/HealthChecksIPRestrictionMiddleware.cs b/src/OrchardCoreContrib.HealthChecks/HealthChecksIPRestrictionMiddleware.cs index 5b74ae27..371272fe 100644 --- a/src/OrchardCoreContrib.HealthChecks/HealthChecksIPRestrictionMiddleware.cs +++ b/src/OrchardCoreContrib.HealthChecks/HealthChecksIPRestrictionMiddleware.cs @@ -4,6 +4,13 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Middleware that restricts access to health check endpoints based on allowed IP addresses. +/// +/// The representing the next middleware in the pipeline. +/// The containing health check configuration. +/// The containing IP access configuration. +/// The used for logging. public class HealthChecksIPRestrictionMiddleware( RequestDelegate next, IOptions healthChecksOptions, @@ -12,6 +19,7 @@ public class HealthChecksIPRestrictionMiddleware( { private readonly HealthChecksOptions _healthChecksOptions = healthChecksOptions.Value; + /// public async Task InvokeAsync(HttpContext context) { if (context.Request.Path.Equals(_healthChecksOptions.Url)) diff --git a/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingMiddleware.cs b/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingMiddleware.cs index f9ebb8c3..d7cc4990 100644 --- a/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingMiddleware.cs +++ b/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingMiddleware.cs @@ -5,6 +5,9 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Middleware that enforces rate limiting on health check endpoints to prevent excessive requests. +/// public class HealthChecksRateLimitingMiddleware { private readonly RequestDelegate _next; @@ -33,6 +36,7 @@ public HealthChecksRateLimitingMiddleware( }); } + /// public async Task InvokeAsync(HttpContext context) { if (context.Request.Path.Equals(_healthChecksOptions.Url)) diff --git a/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingOptions.cs b/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingOptions.cs index d0ce9bf6..59eeb1ab 100644 --- a/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingOptions.cs +++ b/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingOptions.cs @@ -1,13 +1,28 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Provides configuration options for rate limiting applied to health check endpoints. +/// public class HealthChecksRateLimitingOptions { + /// + /// Gets or sets the maximum number of concurrent permits that can be acquired. Defaults to 5. + /// public int PermitLimit { get; set; } = 5; + /// + /// Gets or sets the time window for which the rate limiting is applied. Defaults to 10 seconds. + /// public TimeSpan Window { get; set; } = TimeSpan.FromSeconds(10); + /// + /// Gets or sets the number of segments the time window is divided into for rate limiting purposes. Defaults to 10. + /// public int SegmentsPerWindow { get; set; } = 10; + /// + /// Gets or sets the maximum number of requests that can be queued when the rate limit is exceeded. Defaults to 0. + /// public int QueueLimit { get; set; } = 0; } diff --git a/src/OrchardCoreContrib.HealthChecks/Startups/BlockingRateLimitingStartup.cs b/src/OrchardCoreContrib.HealthChecks/Startups/BlockingRateLimitingStartup.cs new file mode 100644 index 00000000..b6d866b2 --- /dev/null +++ b/src/OrchardCoreContrib.HealthChecks/Startups/BlockingRateLimitingStartup.cs @@ -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.Startups; + +/// +/// Configures blocking rate limiting for health check endpoints during application startup. +/// +/// The . +[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.HealthChecksRateLimitingConfigurationKey); + + services.Configure(rateLimitingSection); + services.Configure(rateLimitingSection); + } + + /// + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + => app.UseMiddleware(); +} diff --git a/src/OrchardCoreContrib.HealthChecks/Startups/IPRestrictionStartup.cs b/src/OrchardCoreContrib.HealthChecks/Startups/IPRestrictionStartup.cs new file mode 100644 index 00000000..c9ae3d87 --- /dev/null +++ b/src/OrchardCoreContrib.HealthChecks/Startups/IPRestrictionStartup.cs @@ -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; + +/// +/// Configures IP-based access restrictions for health check endpoints during application startup. +/// +/// The . +[Feature("OrchardCoreContrib.HealthChecks.IPRestriction")] +public class IPRestrictionStartup(IShellConfiguration shellConfiguration) : StartupBase +{ + /// + public override int Order => 10; + + /// + public override void ConfigureServices(IServiceCollection services) + => services.Configure(shellConfiguration.GetSection(Constants.HealthChecksAccessConfigurationKey)); + + /// + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + => app.UseMiddleware(); +} diff --git a/src/OrchardCoreContrib.HealthChecks/Startups/RateLimitingStartup.cs b/src/OrchardCoreContrib.HealthChecks/Startups/RateLimitingStartup.cs new file mode 100644 index 00000000..abede72f --- /dev/null +++ b/src/OrchardCoreContrib.HealthChecks/Startups/RateLimitingStartup.cs @@ -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; + +/// +/// Configures rate limiting for health check endpoints during application startup. +/// +/// The . +[Feature("OrchardCoreContrib.HealthChecks.RateLimiting")] +public class RateLimitingStartup(IShellConfiguration shellConfiguration) : StartupBase +{ + /// + public override int Order => 30; + + /// + public override void ConfigureServices(IServiceCollection services) + => services.Configure(shellConfiguration.GetSection(Constants.HealthChecksRateLimitingConfigurationKey)); + + /// + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + => app.UseMiddleware(); +} diff --git a/src/OrchardCoreContrib.HealthChecks/Startup.cs b/src/OrchardCoreContrib.HealthChecks/Startups/Startup.cs similarity index 55% rename from src/OrchardCoreContrib.HealthChecks/Startup.cs rename to src/OrchardCoreContrib.HealthChecks/Startups/Startup.cs index 4d1f89b9..04d3d06e 100644 --- a/src/OrchardCoreContrib.HealthChecks/Startup.cs +++ b/src/OrchardCoreContrib.HealthChecks/Startups/Startup.cs @@ -13,17 +13,23 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Configures health check endpoints during application startup. +/// +/// The . public class Startup(IShellConfiguration shellConfiguration) : StartupBase { private static readonly JsonSerializerOptions _jsonSerializerOptions = new() { WriteIndented = true }; + /// public override void ConfigureServices(IServiceCollection services) { services.AddHealthChecks(); - services.Configure(shellConfiguration.GetSection(Constants.ConfigurationKey)); + services.Configure(shellConfiguration.GetSection(Constants.HealthChecksConfigurationKey)); } + /// public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) { var healthChecksOptions = serviceProvider.GetService>().Value; @@ -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(shellConfiguration.GetSection($"{Constants.ConfigurationKey}:Access")); - - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - => app.UseMiddleware(); -} - -[Feature("OrchardCoreContrib.HealthChecks.RateLimiting")] -public class RateLimitingStartup(IShellConfiguration shellConfiguration) : StartupBase -{ - public override int Order => 30; - - public override void ConfigureServices(IServiceCollection services) - => services.Configure(shellConfiguration.GetSection($"{Constants.ConfigurationKey}:RateLimiting")); - - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - => app.UseMiddleware(); -} - -[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(rateLimitingSection); - services.Configure(rateLimitingSection); - } - - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - => app.UseMiddleware(); -} - From fb73b3586010b01665af3606a89cb324d305ed48 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Fri, 17 Apr 2026 20:37:25 +0300 Subject: [PATCH 2/2] Fix the build --- .../Startups/BlockingRateLimitingStartup.cs | 2 +- .../OrchardCoreStartup.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/OrchardCoreContrib.HealthChecks/Startups/BlockingRateLimitingStartup.cs b/src/OrchardCoreContrib.HealthChecks/Startups/BlockingRateLimitingStartup.cs index b6d866b2..30487214 100644 --- a/src/OrchardCoreContrib.HealthChecks/Startups/BlockingRateLimitingStartup.cs +++ b/src/OrchardCoreContrib.HealthChecks/Startups/BlockingRateLimitingStartup.cs @@ -4,7 +4,7 @@ using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Modules; -namespace OrchardCoreContrib.HealthChecks.Startups; +namespace OrchardCoreContrib.HealthChecks; /// /// Configures blocking rate limiting for health check endpoints during application startup. diff --git a/test/OrchardCoreContrib.HealthChecks.Tests/OrchardCoreStartup.cs b/test/OrchardCoreContrib.HealthChecks.Tests/OrchardCoreStartup.cs index 4e7a79d3..f715af05 100644 --- a/test/OrchardCoreContrib.HealthChecks.Tests/OrchardCoreStartup.cs +++ b/test/OrchardCoreContrib.HealthChecks.Tests/OrchardCoreStartup.cs @@ -48,9 +48,9 @@ private IConfigurationRoot AddHealthChecksConfiguration() { var newConfiguration = new Dictionary { - { $"{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()