diff --git a/scenarios/rejection.benchmarks.yml b/scenarios/rejection.benchmarks.yml index df329b687..7aee3abd8 100644 --- a/scenarios/rejection.benchmarks.yml +++ b/scenarios/rejection.benchmarks.yml @@ -4,6 +4,8 @@ imports: - https://raw.githubusercontent.com/dotnet/crank/main/src/Microsoft.Crank.Jobs.Bombardier/bombardier.yml + - https://raw.githubusercontent.com/dotnet/crank/main/src/Microsoft.Crank.Jobs.HttpClient/httpclient.yml + - https://raw.githubusercontent.com/dotnet/crank/main/src/Microsoft.Crank.Jobs.Wrk/wrk.yml - https://github.com/aspnet/Benchmarks/blob/main/scenarios/aspnet.profiles.yml?raw=true variables: @@ -20,12 +22,14 @@ jobs: # behavioral settings mTLS: false # enables settings on http.sys to negotiate client cert on connections tlsRenegotiation: false # enables client cert validation + certPublicKeyLength: 2048 + httpSysUrlPrefix: "" # enables host header validation on http.sys layer # debug settings certValidationConsoleEnabled: false httpSysLogs: false statsEnabled: false logRequestDetails: false - arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --httpSysLogs {{httpSysLogs}} --logRequestDetails {{logRequestDetails}}" + arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --httpSysLogs {{httpSysLogs}} --logRequestDetails {{logRequestDetails}} --httpSysUrlPrefix {{httpSysUrlPrefix}}" kestrelServer: source: @@ -38,11 +42,13 @@ jobs: mTLS: false tlsRenegotiation: false tlsProtocols: "tls12,tls13" + certPublicKeyLength: 2048 # controls cert with such a length is used for the test + enableHostHeaderValidation: false # enables host header validation middleware # debug settings certValidationConsoleEnabled: false statsEnabled: false logRequestDetails: false - arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --tlsProtocols {{tlsProtocols}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --logRequestDetails {{logRequestDetails}}" + arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --tlsProtocols {{tlsProtocols}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --logRequestDetails {{logRequestDetails}} --enableHostHeaderValidation {{enableHostHeaderValidation}}" scenarios: @@ -52,9 +58,9 @@ scenarios: application: job: httpSysServer load: - job: bombardier + job: httpclient variables: - path: /unknown/%09 + path: /unknown/%GG presetHeaders: connectionclose connections: 32 serverScheme: https @@ -63,7 +69,7 @@ scenarios: application: job: httpSysServer load: - job: bombardier + job: httpclient variables: path: /hello-world connections: 32 @@ -74,8 +80,10 @@ scenarios: httpsys-hostheader-mismatch: application: job: httpSysServer + variables: + httpSysUrlPrefix: "https://testserver:{{serverPort}}" load: - job: bombardier + job: wrk variables: path: /hello-world connections: 32 @@ -89,9 +97,9 @@ scenarios: application: job: kestrelServer load: - job: bombardier + job: httpclient variables: - path: /unknown/%09 + path: /unknown/%GG presetHeaders: connectionclose connections: 32 serverScheme: https @@ -100,7 +108,7 @@ scenarios: application: job: kestrelServer load: - job: bombardier + job: httpclient variables: path: /hello-world connections: 32 @@ -111,8 +119,10 @@ scenarios: kestrel-hostheader-mismatch: application: job: kestrelServer + variables: + enableHostHeaderValidation: true load: - job: bombardier + job: wrk variables: path: /hello-world connections: 32 diff --git a/src/BenchmarksApps/TLS/HttpSys/NetSh/SslCertBinding.cs b/src/BenchmarksApps/TLS/HttpSys/NetSh/SslCertBinding.cs index c19d35822..7c9fede4d 100644 --- a/src/BenchmarksApps/TLS/HttpSys/NetSh/SslCertBinding.cs +++ b/src/BenchmarksApps/TLS/HttpSys/NetSh/SslCertBinding.cs @@ -2,9 +2,9 @@ { public class SslCertBinding { - public string CertificateThumbprint { get; set; } + public string? CertificateThumbprint { get; set; } - public string ApplicationId { get; set; } + public string? ApplicationId { get; set; } /// /// if mutual TLS is enabled diff --git a/src/BenchmarksApps/TLS/HttpSys/Program.cs b/src/BenchmarksApps/TLS/HttpSys/Program.cs index ad2007b1a..53b724781 100644 --- a/src/BenchmarksApps/TLS/HttpSys/Program.cs +++ b/src/BenchmarksApps/TLS/HttpSys/Program.cs @@ -12,6 +12,7 @@ var tlsRenegotiationEnabled = bool.TryParse(builder.Configuration["tlsRenegotiation"], out var tlsRenegotiationEnabledConfig) && tlsRenegotiationEnabledConfig; var certPublicKeySpecified = int.TryParse(builder.Configuration["certPublicKeyLength"], out var certPublicKeyConfig); var certPublicKeyLength = certPublicKeySpecified ? certPublicKeyConfig : 2048; +var urlPrefix = builder.Configuration["httpSysUrlPrefix"]; // endpoints var listeningEndpoints = builder.Configuration["urls"] ?? "https://localhost:5000/"; @@ -37,6 +38,14 @@ { // meaning client can send a certificate, but it can be explicitly requested by server as well (renegotiation) options.ClientCertificateMethod = ClientCertificateMethod.AllowRenegotation; + + if (!string.IsNullOrEmpty(urlPrefix)) + { + // Specific "hostname" to listen on. + // This turns on host validation on http.sys layer + options.UrlPrefixes.Add(urlPrefix); + Console.WriteLine("Set specific url-prefix for Http.Sys: " + urlPrefix); + } }); #pragma warning restore CA1416 // Can be launched only on Windows (HttpSys) diff --git a/src/BenchmarksApps/TLS/HttpSys/appsettings.Development.json b/src/BenchmarksApps/TLS/HttpSys/appsettings.Development.json index 116355112..a7a6b1a26 100644 --- a/src/BenchmarksApps/TLS/HttpSys/appsettings.Development.json +++ b/src/BenchmarksApps/TLS/HttpSys/appsettings.Development.json @@ -7,6 +7,7 @@ }, "mTLS": "false", "httpSysLogs": "true", - "tlsRenegotiation": "true", - "certValidationConsoleEnabled": "true" + "tlsRenegotiation": "false", + "certValidationConsoleEnabled": "false", + "httpSysUrlPrefix": "https://testserver:5000/" } diff --git a/src/BenchmarksApps/TLS/HttpSys/appsettings.json b/src/BenchmarksApps/TLS/HttpSys/appsettings.json index 10f68b8c8..0c208ae91 100644 --- a/src/BenchmarksApps/TLS/HttpSys/appsettings.json +++ b/src/BenchmarksApps/TLS/HttpSys/appsettings.json @@ -4,6 +4,5 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } - }, - "AllowedHosts": "*" + } } diff --git a/src/BenchmarksApps/TLS/Kestrel/Kestrel.csproj b/src/BenchmarksApps/TLS/Kestrel/Kestrel.csproj index de39cbecb..fa4d10528 100644 --- a/src/BenchmarksApps/TLS/Kestrel/Kestrel.csproj +++ b/src/BenchmarksApps/TLS/Kestrel/Kestrel.csproj @@ -21,8 +21,6 @@ - - PreserveNewest diff --git a/src/BenchmarksApps/TLS/Kestrel/Program.cs b/src/BenchmarksApps/TLS/Kestrel/Program.cs index e81ec832f..73bc3608e 100644 --- a/src/BenchmarksApps/TLS/Kestrel/Program.cs +++ b/src/BenchmarksApps/TLS/Kestrel/Program.cs @@ -1,10 +1,9 @@ using System.Diagnostics; using System.Net; using System.Net.Security; -using System.Runtime.InteropServices; +using System.Reflection; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; -using Microsoft.AspNetCore.Authentication.Certificate; using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.HttpSys; @@ -21,6 +20,7 @@ var tlsRenegotiationEnabled = bool.TryParse(builder.Configuration["tlsRenegotiation"], out var tlsRenegotiationEnabledConfig) && tlsRenegotiationEnabledConfig; var certPublicKeySpecified = int.TryParse(builder.Configuration["certPublicKeyLength"], out var certPublicKeyConfig); var certPublicKeyLength = certPublicKeySpecified ? certPublicKeyConfig : 2048; +var enableHostHeaderValidation = bool.TryParse(builder.Configuration["enableHostHeaderValidation"], out var enableHostHeaderValidationConfig) && enableHostHeaderValidationConfig; // endpoints var listeningEndpoints = builder.Configuration["urls"] ?? "https://localhost:5000/"; @@ -39,6 +39,24 @@ var connectionIds = new HashSet(); var fetchedCertsCounter = 0; +if (enableHostHeaderValidation) +{ + builder.Services.Configure(options => + { + var allowedHosts = new HashSet(); + foreach (var endpoint in listeningEndpoints.Split([';'], StringSplitOptions.RemoveEmptyEntries)) + { + var urlPrefix = UrlPrefix.Create(endpoint); + allowedHosts.Add(urlPrefix.Host); + } + + Console.WriteLine("Configured HostFilteringOptions. Hosts: " + string.Join(';', allowedHosts)); + options.AllowedHosts = allowedHosts.ToArray(); + options.IncludeFailureMessage = true; // Suppresses the failure message in response body. It should be `true` to match http.sys behavior. + options.AllowEmptyHosts = true; + }); +} + builder.WebHost.UseKestrel(options => { foreach (var value in listeningEndpoints.Split([';'], StringSplitOptions.RemoveEmptyEntries)) @@ -56,8 +74,15 @@ void ConfigureListen(KestrelServerOptions serverOptions, IConfigurationRoot conf var certificatePath = Path.Combine("certificates", $"testCert-{certPublicKeyLength}.pfx"); Console.WriteLine($"Using certificate: {certificatePath}"); + var certPath = +#if DEBUG + Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!, certificatePath); // exe location +#else + certificatePath; +#endif + // [SuppressMessage("Microsoft.Security", "CSCAN0220.DefaultPasswordContexts", Justification="Benchmark code, not a secret")] - listenOptions.UseHttps(certificatePath, "testPassword", options => + listenOptions.UseHttps(certPath, "testPassword", options => { if (supportedTlsVersions is not null) { @@ -98,6 +123,12 @@ void ConfigureListen(KestrelServerOptions serverOptions, IConfigurationRoot conf var app = builder.Build(); +if (enableHostHeaderValidation) +{ + Console.WriteLine("Enabled host header filtering middleware."); + app.UseHostFiltering(); +} + bool AllowAnyCertificateValidationWithLogging(X509Certificate2 certificate, X509Chain? chain, SslPolicyErrors errors) { fetchedCertsCounter++; diff --git a/src/BenchmarksApps/TLS/Kestrel/appsettings.Development.json b/src/BenchmarksApps/TLS/Kestrel/appsettings.Development.json index 946a128fa..5305412c5 100644 --- a/src/BenchmarksApps/TLS/Kestrel/appsettings.Development.json +++ b/src/BenchmarksApps/TLS/Kestrel/appsettings.Development.json @@ -6,6 +6,7 @@ } }, "mTLS": "false", - "tlsRenegotiation": "true", - "certValidationConsoleEnabled": "true" + "tlsRenegotiation": "false", + "certValidationConsoleEnabled": "false", + "enableHostHeaderValidation": "false" } diff --git a/src/BenchmarksApps/TLS/Kestrel/appsettings.json b/src/BenchmarksApps/TLS/Kestrel/appsettings.json index 10f68b8c8..0c208ae91 100644 --- a/src/BenchmarksApps/TLS/Kestrel/appsettings.json +++ b/src/BenchmarksApps/TLS/Kestrel/appsettings.json @@ -4,6 +4,5 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } - }, - "AllowedHosts": "*" + } }