From 35c07da1331ecac233fdf9e28b65f0965233bbc8 Mon Sep 17 00:00:00 2001 From: Marc Hayek Date: Fri, 16 Oct 2026 15:37:15 +0100 Subject: [PATCH] Restore TLS certificate validation for health-check clients Remove unconditional certificate acceptance from named and legacy health-check clients while preserving existing handler settings. Update documentation and add regression coverage for the registered handlers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 97fd4f9b-6689-4de9-bea3-3cbe15794b28 --- ...ckHttpClientServiceCollectionExtensions.cs | 12 ++--- .../ServiceCollectionExtensions.cs | 3 +- .../ServiceCollectionExtensions.cs | 46 +++++++++++++++++++ 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/Diagnostics.HealthChecks/HealthCheckHttpClientServiceCollectionExtensions.cs b/src/Diagnostics.HealthChecks/HealthCheckHttpClientServiceCollectionExtensions.cs index a6c8195f8..65f7a61b7 100644 --- a/src/Diagnostics.HealthChecks/HealthCheckHttpClientServiceCollectionExtensions.cs +++ b/src/Diagnostics.HealthChecks/HealthCheckHttpClientServiceCollectionExtensions.cs @@ -14,19 +14,13 @@ public static class HealthCheckHttpClientServiceCollectionExtensions { /// /// Defines a in the DI designed for Health Checks. - /// The main difference is that these clients in development will ignore SSL errors. - /// This is an acceptable compromise considering that the health checks are executed in the same machine - /// as the Service Fabric service itself, and that each client will be specifically used for the - /// health checks, as the name parameter in input is mandatory. - /// Every other uses of this extension methods apart from health checks should be avoided. + /// Uses platform certificate validation in all environments. HTTPS endpoints must present + /// trusted certificates that match the configured host. /// /// The services. /// The logical name of the to configure. /// The . public static IHttpClientBuilder AddHealthCheckHttpClient(this IServiceCollection services, string name) => services.AddHttpClient(name) - .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler - { - ServerCertificateCustomValidationCallback = (_, _, _, _) => true - }); + .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler()); } diff --git a/src/Diagnostics.HealthChecks/ServiceCollectionExtensions.cs b/src/Diagnostics.HealthChecks/ServiceCollectionExtensions.cs index 002d7be9a..68449a701 100644 --- a/src/Diagnostics.HealthChecks/ServiceCollectionExtensions.cs +++ b/src/Diagnostics.HealthChecks/ServiceCollectionExtensions.cs @@ -31,8 +31,7 @@ private static IServiceCollection AddPublisherDependencies(this IServiceCollecti .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { AllowAutoRedirect = false, - Credentials = CredentialCache.DefaultCredentials, - ServerCertificateCustomValidationCallback = (sender, x509Certificate, chain, errors) => true + Credentials = CredentialCache.DefaultCredentials }); serviceCollection.TryAddSingleton(); diff --git a/tests/Diagnostics.HealthChecks.UnitTests/ServiceCollectionExtensions.cs b/tests/Diagnostics.HealthChecks.UnitTests/ServiceCollectionExtensions.cs index b9a44b1dc..b0db39ed9 100644 --- a/tests/Diagnostics.HealthChecks.UnitTests/ServiceCollectionExtensions.cs +++ b/tests/Diagnostics.HealthChecks.UnitTests/ServiceCollectionExtensions.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Fabric; using System.Linq; +using System.Net; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -20,6 +22,50 @@ namespace Microsoft.Omex.Extensions.Diagnostics.HealthChecks.UnitTests [TestClass] public class ServiceCollectionExtensions { + [TestMethod] + public void AddHealthCheckHttpClient_UsesDefaultCertificateValidation() + { + const string clientName = "HealthCheck"; + ServiceCollection services = new(); + + IHttpClientBuilder builder = services.AddHealthCheckHttpClient(clientName); + using ServiceProvider provider = services.BuildServiceProvider(); + + Assert.AreEqual(clientName, builder.Name); + HttpClientHandler handler = GetPrimaryHttpClientHandler(provider, clientName); + Assert.IsNull(handler.ServerCertificateCustomValidationCallback, + "Health-check clients must use platform certificate validation in every environment."); + } + + [TestMethod] + public void AddServiceFabricHealthChecks_UsesDefaultCertificateValidationAndPreservesClientSettings() + { + using ServiceProvider provider = new ServiceCollection() + .AddServiceFabricHealthChecks() + .Services + .BuildServiceProvider(); + + HttpClientHandler handler = GetPrimaryHttpClientHandler(provider, HealthCheckConstants.HttpClientLogicalName); + + Assert.IsNull(handler.ServerCertificateCustomValidationCallback, + "The legacy health-check client must use platform certificate validation."); + Assert.IsFalse(handler.AllowAutoRedirect); + Assert.AreSame(CredentialCache.DefaultCredentials, handler.Credentials); + } + + private static HttpClientHandler GetPrimaryHttpClientHandler(IServiceProvider provider, string clientName) + { + HttpMessageHandler handler = provider.GetRequiredService().CreateHandler(clientName); + while (handler is DelegatingHandler delegatingHandler) + { + NullableAssert.IsNotNull(delegatingHandler.InnerHandler); + handler = delegatingHandler.InnerHandler; + } + + Assert.IsInstanceOfType(handler, typeof(HttpClientHandler)); + return (HttpClientHandler)handler; + } + [TestMethod] public void AddHealthCheckPublisher_RegisterPublisherWithoutOverride() {