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
5 changes: 1 addition & 4 deletions scenarios/tls.benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ jobs:
tlsRenegotiation: false # enables client cert validation
# 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}} --logRequestDetails {{logRequestDetails}}"

kestrelServer:
source:
Expand Down Expand Up @@ -81,7 +80,6 @@ scenarios:
variables:
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:
Expand All @@ -102,7 +100,6 @@ scenarios:
variables:
mTLS: false
tlsRenegotiation: true
httpSysLogs: false # only for debug purposes
certValidationConsoleEnabled: false # only for debug purposes
load:
job: httpclient
Expand Down
99 changes: 86 additions & 13 deletions src/BenchmarksApps/TLS/HttpSys/NetShWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;

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

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

Expand All @@ -27,14 +28,42 @@
Console.WriteLine("Disabled http.sys settings for mTLS");
}

public static bool BindingExists(string ipPort, out string certThumbprint, out string appId)
{
certThumbprint = string.Empty;
appId = string.Empty;

var bindings = ExecuteNetShCommand("http show sslcert");
if (string.IsNullOrEmpty(bindings) || !bindings.Contains(ipPort))
{
return false;
}

// Extract the certificate thumbprint
var thumbprintMatch = Regex.Match(bindings, @"Certificate Hash\s+:\s+([a-fA-F0-9]+)");
if (thumbprintMatch.Success)
{
certThumbprint = thumbprintMatch.Groups[1].Value;
}

// Extract the application ID
var appIdMatch = Regex.Match(bindings, @"Application ID\s+:\s+{([a-fA-F0-9-]+)}");
if (appIdMatch.Success)
{
appId = appIdMatch.Groups[1].Value;
}

return true;
}

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

public static void EnableHttpSysMutualTls(string ipPort)
public static void SetTestCertBinding(string ipPort, bool enableClientCertNegotiation)
{
Console.WriteLine("Setting up mTLS for http.sys");
Console.WriteLine("Setting up binding for testCert for http.sys");

var certificate = LoadCertificate();
Console.WriteLine("Loaded `testCert.pfx` from local file system");
Expand All @@ -47,25 +76,67 @@
}

string certThumbprint = certificate.Thumbprint;
string appId = Guid.NewGuid().ToString();
SetCertBinding(ipPort, certThumbprint, enableClientCertNegotiation: enableClientCertNegotiation);

string command = $"http add sslcert ipport={ipPort} certstorename=MY certhash={certThumbprint} appid={{{appId}}} clientcertnegotiation=enable";
ExecuteNetShCommand(command);
Console.WriteLine("Configured binding for testCert for http.sys");
}

Console.WriteLine("Configured http.sys settings for mTLS");
public static bool TrySelfSignCertificate(string ipPort, out string certThumbprint)
{
certThumbprint = string.Empty;
try
{
// Extract the IP address from ipPort
string ipAddress = ipPort.Split(':')[0];

// Generate a self-signed certificate using PowerShell
string command = $"New-SelfSignedCertificate -CertStoreLocation cert:\\LocalMachine\\My -DnsName {ipAddress}";
string output = ExecutePowershellCommand(command);

// Extract the thumbprint from the output
var lines = output.Split("\r\n", StringSplitOptions.RemoveEmptyEntries);
var lastLine = lines[^1];
certThumbprint = lastLine.Split(" ", StringSplitOptions.RemoveEmptyEntries)[0];

Console.WriteLine($"Self-signed certificate for {ipAddress}");
return true;
}
catch (Exception ex)
{
Console.WriteLine("Failed to self-sign the certificate: " + ex.Message);
return false;
}
}

public static void SetCertBinding(string ipPort, string certThumbprint, string appId = null, bool enableClientCertNegotiation = false)

Check warning on line 111 in src/BenchmarksApps/TLS/HttpSys/NetShWrapper.cs

View workflow job for this annotation

GitHub Actions / Build & Test (ubuntu-latest)

Cannot convert null literal to non-nullable reference type.
{
var negotiateClientCert = enableClientCertNegotiation ? "enable" : "disable";
if (string.IsNullOrEmpty(appId))
{
appId = "00000000-0000-0000-0000-000000000000";
}
string command = $"http add sslcert ipport={ipPort} certstorename=MY certhash={certThumbprint} appid={{{appId}}} clientcertnegotiation={negotiateClientCert}";
ExecuteNetShCommand(command);
Console.WriteLine($"Performed cert bindign for {ipPort}");
}

private static void ExecuteNetShCommand(string command, bool alwaysLogOutput = false)
private static string ExecutePowershellCommand(string command, bool alwaysLogOutput = false)
=> ExecuteCommand("powershell.exe", command, alwaysLogOutput);

private static string ExecuteNetShCommand(string command, bool alwaysLogOutput = false)
=> ExecuteCommand("netsh", command, alwaysLogOutput);

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

Console.WriteLine($"Executing command: `netsh {command}`");
Console.WriteLine($"Executing command: `{fileName} {command}`");
using Process process = Process.Start(processInfo)!;
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
Expand All @@ -77,8 +148,10 @@

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

return output;
}

private static X509Certificate2 LoadCertificate()
Expand Down
26 changes: 17 additions & 9 deletions src/BenchmarksApps/TLS/HttpSys/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
builder.Logging.ClearProviders();

// behavioral
var httpSysLoggingEnabled = bool.TryParse(builder.Configuration["httpSysLogs"], out var httpSysLogsEnabled) && httpSysLogsEnabled;
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/";
Expand All @@ -18,6 +17,17 @@
var statsEnabled = bool.TryParse(builder.Configuration["statsEnabled"], out var connectionStatsEnabledConfig) && connectionStatsEnabledConfig;
var logRequestDetails = bool.TryParse(builder.Configuration["logRequestDetails"], out var logRequestDetailsConfig) && logRequestDetailsConfig;

// existing netsh bindings to restore after the benchmark run
if (!NetShWrapper.BindingExists(httpsIpPort, out var originalCertThumbprint, out var originalAppId))
{
Console.WriteLine($"No binding existed. Need to self-sign it and bind to '{httpsIpPort}'");
if (!NetShWrapper.TrySelfSignCertificate(httpsIpPort, out originalCertThumbprint))
{
throw new ApplicationException($"Failed to setup ssl binding for '{httpsIpPort}'. Please unblock the VM.");
}
NetShWrapper.SetCertBinding(httpsIpPort, originalCertThumbprint);
}

#pragma warning disable CA1416 // Can be launched only on Windows (HttpSys)
builder.WebHost.UseHttpSys(options =>
{
Expand Down Expand Up @@ -81,7 +91,9 @@ void OnShutdown()

try
{
NetShWrapper.DisableHttpSysMutualTls(ipPort: httpsIpPort);
NetShWrapper.DeleteBinding(ipPort: httpsIpPort);
NetShWrapper.SetCertBinding(ipPort: httpsIpPort, certThumbprint: originalCertThumbprint, appId: originalAppId);
NetShWrapper.Show();
}
catch
{
Expand All @@ -93,9 +105,8 @@ void OnShutdown()
try
{
// if not executed, following command (enable http.sys mutual tls) will fail because binding exists
NetShWrapper.DisableHttpSysMutualTlsIfExists(ipPort: httpsIpPort);

NetShWrapper.EnableHttpSysMutualTls(ipPort: httpsIpPort);
NetShWrapper.DeleteBindingIfExists(ipPort: httpsIpPort);
NetShWrapper.SetTestCertBinding(ipPort: httpsIpPort, enableClientCertNegotiation: true);
}
catch
{
Expand Down Expand Up @@ -138,10 +149,7 @@ void OnShutdown()

await app.StartAsync();

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

Console.WriteLine("Application Info:");
if (mTlsEnabled)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "hello-world",
"applicationUrl": "https://localhost:5000;http://localhost:5001",
"applicationUrl": "https://127.0.0.1:5000;http://localhost:5001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
Expand Down
Loading