Skip to content

Commit

Permalink
fix: integrate mutual TLS scenario with underlying http.sys server (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
DeagleGross authored Jan 15, 2025
1 parent 9b5402f commit 55b23b5
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 6 deletions.
3 changes: 3 additions & 0 deletions build/trend-scenarios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ parameters:
- displayName: "HttpSys Windows: mTLS Handshakes"
arguments: --scenario mTls-handshakes-httpsys $(tlsJobs) --property scenario=HttpSysMutualTLSHandshakes --application.options.requiredOperatingSystem windows

- displayName: "HttpSys Windows: TLS Renegotiation"
arguments: --scenario tls-renegotiation-httpsys $(tlsJobs) --property scenario=HttpSysTLSRenegotiation --application.options.requiredOperatingSystem windows

- displayName: "Kestrel Linux: TLS Handshakes"
arguments: --scenario tls-handshakes-kestrel $(tlsJobs) --property scenario=KestrelTLSHandshakes --application.options.requiredOperatingSystem linux

Expand Down
30 changes: 27 additions & 3 deletions scenarios/tls.benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ jobs:
project: src/BenchmarksApps/TLS/HttpSys/HttpSys.csproj
readyStateText: Application started.
variables:
mTLS: false
mTLS: false # enables settings on http.sys to negotiate client cert on connections
tlsRenegotiation: false # enables client cert validation
certValidationConsoleEnabled: false
httpSysLogs: false
statsEnabled: false
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}}"
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --httpSysLogs {{httpSysLogs}}"

kestrelServer:
source:
Expand Down Expand Up @@ -52,7 +54,29 @@ scenarios:
application:
job: httpSysServer
variables:
mTLS: true
mTLS: true # enables settings on http.sys to negotiate client cert on connections
tlsRenegotiation: true # enables client cert validation
httpSysLogs: false # only for debug purposes
certValidationConsoleEnabled: false # only for debug purposes
serverPort: 8080 # IMPORTANT: not to intersect with other tests in case http.sys configuration impacts other benchmarks
load:
job: httpclient
variables:
serverPort: 8080 # in sync with server
path: /hello-world
presetHeaders: connectionclose
connections: 32
serverScheme: https
certPath: https://raw.githubusercontent.com/aspnet/Benchmarks/refs/heads/main/src/BenchmarksApps/TLS/HttpSys/testCert.pfx
certPwd: testPassword

tls-renegotiation-httpsys:
application:
job: httpSysServer
variables:
mTLS: false
tlsRenegotiation: true
httpSysLogs: false # only for debug purposes
certValidationConsoleEnabled: false # only for debug purposes
load:
job: httpclient
Expand Down
6 changes: 6 additions & 0 deletions src/BenchmarksApps/TLS/HttpSys/HttpSys.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<None Update="testCert.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
89 changes: 89 additions & 0 deletions src/BenchmarksApps/TLS/HttpSys/NetShWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;

namespace HttpSys
{
public static class NetShWrapper
{
public static void DisableHttpSysMutualTlsIfExists(string ipPort)
{
try
{
DisableHttpSysMutualTls(ipPort);
}
catch
{
// ignore
}
}

public static void DisableHttpSysMutualTls(string ipPort)
{
Console.WriteLine("Disabling mTLS for http.sys");

string command = $"http delete sslcert ipport={ipPort}";
ExecuteNetShCommand(command);

Console.WriteLine("Disabled http.sys settings for mTLS");
}

public static void Show()
{
ExecuteNetShCommand("http show sslcert", alwaysLogOutput: true);
}

public static void EnableHttpSysMutualTls(string ipPort)
{
Console.WriteLine("Setting up mTLS for http.sys");

var certificate = LoadCertificate();
Console.WriteLine("Loaded `testCert.pfx` from local file system");
using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
Console.WriteLine("Added `testCert.pfx` to localMachine cert store");
store.Close();
}

string certThumbprint = certificate.Thumbprint;
string appId = Guid.NewGuid().ToString();

string command = $"http add sslcert ipport={ipPort} certstorename=MY certhash={certThumbprint} appid={{{appId}}} clientcertnegotiation=enable";
ExecuteNetShCommand(command);

Console.WriteLine("Configured http.sys settings for mTLS");
}

private static void ExecuteNetShCommand(string command, bool alwaysLogOutput = false)
{
ProcessStartInfo processInfo = new ProcessStartInfo("netsh", command)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};

Console.WriteLine($"Executing command: `netsh {command}`");
using Process process = Process.Start(processInfo)!;
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();

if (alwaysLogOutput)
{
Console.WriteLine(output);
}

if (process.ExitCode != 0)
{
throw new InvalidOperationException($"netsh command execution failure: {output}");
}
}

private static X509Certificate2 LoadCertificate()
=> File.Exists("testCert.pfx")
? X509CertificateLoader.LoadPkcs12FromFile("testCert.pfx", "testPassword", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable)
: X509CertificateLoader.LoadPkcs12FromFile("../testCert.pfx", "testPassword", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
}
}
47 changes: 45 additions & 2 deletions src/BenchmarksApps/TLS/HttpSys/Program.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using HttpSys;
using Microsoft.AspNetCore.Server.HttpSys;

var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();

var writeCertValidationEventsToConsole = bool.TryParse(builder.Configuration["certValidationConsoleEnabled"], out var certValidationConsoleEnabled) && certValidationConsoleEnabled;
var httpSysLoggingEnabled = bool.TryParse(builder.Configuration["httpSysLogs"], out var httpSysLogsEnabled) && httpSysLogsEnabled;
var statsEnabled = bool.TryParse(builder.Configuration["statsEnabled"], out var connectionStatsEnabledConfig) && connectionStatsEnabledConfig;
var mTlsEnabled = bool.TryParse(builder.Configuration["mTLS"], out var mTlsEnabledConfig) && mTlsEnabledConfig;
var tlsRenegotiationEnabled = bool.TryParse(builder.Configuration["tlsRenegotiation"], out var tlsRenegotiationEnabledConfig) && tlsRenegotiationEnabledConfig;
var listeningEndpoints = builder.Configuration["urls"] ?? "https://localhost:5000/";
var httpsIpPort = listeningEndpoints.Split(";").First(x => x.Contains("https")).Replace("https://", "");

#pragma warning disable CA1416 // Can be launched only on Windows (HttpSys)
builder.WebHost.UseHttpSys(options =>
Expand All @@ -17,7 +20,7 @@
});
#pragma warning restore CA1416 // Can be launched only on Windows (HttpSys)

var app = builder.Build();
var app = builder.Build();

app.MapGet("/hello-world", () =>
{
Expand All @@ -40,6 +43,40 @@
}

if (mTlsEnabled)
{
var hostAppLifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();
hostAppLifetime!.ApplicationStopping.Register(OnShutdown);

void OnShutdown()
{
Console.WriteLine("Application shutdown started.");

try
{
NetShWrapper.DisableHttpSysMutualTls(ipPort: httpsIpPort);
}
catch
{
Console.WriteLine("Failed to disable HTTP.SYS mTLS settings");
throw;
}
}

try
{
// if not executed, following command (enable http.sys mutual tls) will fail because binding exists
NetShWrapper.DisableHttpSysMutualTlsIfExists(ipPort: httpsIpPort);

NetShWrapper.EnableHttpSysMutualTls(ipPort: httpsIpPort);
}
catch
{
Console.WriteLine($"Http.Sys configuration for mTLS failed");
throw;
}
}

if (tlsRenegotiationEnabled)
{
// this is an http.sys middleware to get a cert
Console.WriteLine("Registered client cert validation middleware");
Expand Down Expand Up @@ -72,6 +109,12 @@
}

await app.StartAsync();

if (httpSysLoggingEnabled)
{
NetShWrapper.Show();
}

Console.WriteLine("Application Info:");
if (mTlsEnabled)
{
Expand Down
4 changes: 3 additions & 1 deletion src/BenchmarksApps/TLS/HttpSys/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"Microsoft.AspNetCore": "Warning"
}
},
"mTLS": "true",
"mTLS": "false",
"httpSysLogs": "true",
"tlsRenegotiation": "true",
"certValidationConsoleEnabled": "true"
}

0 comments on commit 55b23b5

Please sign in to comment.