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
11 changes: 0 additions & 11 deletions scenarios/tls.benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@
variables:
serverPort: 5000

# these scripts allow to disable (or rollback changes) to the SChannel registry
# this allows to disable TLS resumption on windows level
disableTlsResumptionScript: powershell -Command "New-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\' -Name MaximumCacheSize -PropertyType DWord -Value 0 -ErrorAction Ignore; New-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\' -Name ServerCacheTime -PropertyType DWord -Value 0 -ErrorAction Ignore; Restart-Service -Name Http -Force;"
rollbackTlsResumptionScript: powershell -Command "Remove-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL' -Name MaximumCacheSize -ErrorAction Ignore; Remove-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL' -Name ServerCacheTime -ErrorAction Ignore; Restart-Service -Name Http -Force;"

jobs:
httpSysServer:
source:
Expand Down Expand Up @@ -70,8 +65,6 @@ scenarios:
tls-handshakes-httpsys:
application:
job: httpSysServer
beforeScript: "{{disableTlsResumptionScript}}"
afterScript: "{{rollbackTlsResumptionScript}}"
load:
job: httpclient
variables:
Expand All @@ -84,8 +77,6 @@ scenarios:
mTls-handshakes-httpsys:
application:
job: httpSysServer
beforeScript: "{{disableTlsResumptionScript}}"
afterScript: "{{rollbackTlsResumptionScript}}"
variables:
mTLS: true # enables settings on http.sys to negotiate client cert on connections
tlsRenegotiation: true # enables client cert validation
Expand All @@ -106,8 +97,6 @@ scenarios:
tls-renegotiation-httpsys:
application:
job: httpSysServer
beforeScript: "{{disableTlsResumptionScript}}"
afterScript: "{{rollbackTlsResumptionScript}}"
variables:
mTLS: false
tlsRenegotiation: true
Expand Down
7 changes: 4 additions & 3 deletions src/BenchmarksApps/TLS/HttpSys/NetSh/SslCertBinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
{
public class SslCertBinding
{
public string CertificateThumbprint { get; set; }

Check warning on line 5 in src/BenchmarksApps/TLS/HttpSys/NetSh/SslCertBinding.cs

View workflow job for this annotation

GitHub Actions / Build & Test (ubuntu-latest)

Non-nullable property 'CertificateThumbprint' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

public string ApplicationId { get; set; }

Check warning on line 7 in src/BenchmarksApps/TLS/HttpSys/NetSh/SslCertBinding.cs

View workflow job for this annotation

GitHub Actions / Build & Test (ubuntu-latest)

Non-nullable property 'ApplicationId' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

/// <summary>
/// if mutual TLS is enabled
Expand All @@ -24,11 +24,12 @@
""";
}

[Flags]
public enum NetShFlag
{
NotSet = 0,
NotSet = 0,

Disabled = 1,
Enable = 2
Disabled = 1,
Enable = 2,
}
}
86 changes: 55 additions & 31 deletions src/BenchmarksApps/TLS/HttpSys/NetShWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,37 @@

namespace HttpSys
{
public static class NetShWrapper
public class NetShWrapper
{
public static void DeleteBindingIfExists(string ipPort)
public bool SupportsDisableSessionId { get; }
public bool SupportsEnableSessionTicket { get; }

public NetShWrapper()
{
var sslCertCapabilitiesText = ExecuteNetShCommand($"http add sslcert help");
if (string.IsNullOrEmpty(sslCertCapabilitiesText))
{
throw new InvalidOperationException("Failed to determine http.sys capabilities");
}

if (sslCertCapabilitiesText.Contains("disablesessionid"))
{
SupportsDisableSessionId = true;
}

if (sslCertCapabilitiesText.Contains("enablesessionticket"))
{
SupportsEnableSessionTicket = true;
}

Console.WriteLine($"""
Http.SYS Capabilities:
- SupportsDisableSessionId: {SupportsDisableSessionId} (if not supported, renegotiation will most likely be enabled by default)
- SupportsEnableSessionTicket: {SupportsEnableSessionTicket}
""");
}

public void DeleteBindingIfExists(string ipPort)
{
try
{
Expand All @@ -19,7 +47,7 @@ public static void DeleteBindingIfExists(string ipPort)
}
}

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

Expand All @@ -29,9 +57,7 @@ public static void DeleteBinding(string ipPort)
Console.WriteLine("Disabled http.sys settings for mTLS");
}



public static bool TryGetSslCertBinding(string ipPort, out SslCertBinding result)
public bool TryGetSslCertBinding(string ipPort, out SslCertBinding result)
{
result = new SslCertBinding();

Expand Down Expand Up @@ -71,7 +97,7 @@ public static bool TryGetSslCertBinding(string ipPort, out SslCertBinding result
Max Settings Per Frame : 2796202
Max Settings Per Minute : 4294967295
*/
var bindings = ExecuteNetShCommand($"http show sslcert ipport={ipPort}");
var bindings = ExecuteNetShCommand($"http show sslcert ipport={ipPort}", ignoreErrorExit: true);
if (string.IsNullOrEmpty(bindings) || !bindings.Contains(ipPort))
{
return false;
Expand Down Expand Up @@ -123,12 +149,12 @@ public static bool TryGetSslCertBinding(string ipPort, out SslCertBinding result
};
}

public static void LogSslCertBinding(string ipPort)
public void LogSslCertBinding(string ipPort)
{
ExecuteNetShCommand($"http show sslcert ipport={ipPort}", alwaysLogOutput: true);
}

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

Expand All @@ -148,7 +174,7 @@ public static void SetTestCertBinding(string ipPort, bool enableClientCertNegoti
Console.WriteLine("Configured binding for testCert for http.sys");
}

public static bool TrySelfSignCertificate(string ipPort, out string certThumbprint)
public bool TrySelfSignCertificate(string ipPort, out string certThumbprint)
{
certThumbprint = string.Empty;
try
Expand All @@ -175,31 +201,31 @@ public static bool TrySelfSignCertificate(string ipPort, out string certThumbpri
}
}

public static void AddCertBinding(
public void AddCertBinding(
string ipPort, string certThumbprint,
string? appId = null,
NetShFlag clientCertNegotiation = NetShFlag.Disabled,
NetShFlag disablesessionid = NetShFlag.Enable,
NetShFlag enablesessionticket = NetShFlag.Disabled)
=> CertBindingCore("add", ipPort, certThumbprint, appId, clientCertNegotiation, disablesessionid, enablesessionticket);

public static void UpdateCertBinding(string ipPort, SslCertBinding binding) => UpdateCertBinding(
public void UpdateCertBinding(string ipPort, SslCertBinding binding) => UpdateCertBinding(
ipPort,
binding.CertificateThumbprint,
binding.ApplicationId,
binding.NegotiateClientCertificate ,
binding.DisableSessionIdTlsResumption,
binding.EnableSessionTicketTlsResumption);

public static void UpdateCertBinding(
public void UpdateCertBinding(
string ipPort, string certThumbprint,
string? appId = null,
NetShFlag clientCertNegotiation = NetShFlag.Disabled,
NetShFlag disablesessionid = NetShFlag.Enable,
NetShFlag enablesessionticket = NetShFlag.Disabled)
=> CertBindingCore("update", ipPort, certThumbprint, appId, clientCertNegotiation, disablesessionid, enablesessionticket);

private static void CertBindingCore(
private void CertBindingCore(
string httpOperation,
string ipPort, string certThumbprint,
string? appId = null,
Expand All @@ -224,17 +250,15 @@ private static void CertBindingCore(

// below options are supported only in later versions of HTTP.SYS
// you can identify if it is available by running `netsh http add sslcert help`
// ---
// workaround is to control SChannel settings via registry

//if (disablesessionidFlag != null)
//{
// command += $" disablesessionid={disablesessionidFlag}";
//}
//if (enablesessionticketFlag != null)
//{
// command += $" enablesessionticket={enablesessionticketFlag}";
//}

if (SupportsDisableSessionId && disablesessionidFlag != null)
{
command += $" disablesessionid={disablesessionidFlag}";
}
if (SupportsEnableSessionTicket && enablesessionticketFlag != null)
{
command += $" enablesessionticket={enablesessionticketFlag}";
}

ExecuteNetShCommand(command, alwaysLogOutput: true);
Console.WriteLine($"Performed cert binding for {ipPort}");
Expand All @@ -248,13 +272,13 @@ private static void CertBindingCore(
};
}

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

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

private static string ExecuteCommand(string fileName, string command, bool logOutput = false)
private static string ExecuteCommand(string fileName, string command, bool ignoreErrorExit = false, bool logOutput = false)
{
ProcessStartInfo processInfo = new ProcessStartInfo(fileName, command)
{
Expand All @@ -274,7 +298,7 @@ private static string ExecuteCommand(string fileName, string command, bool logOu
Console.WriteLine(output);
}

if (process.ExitCode != 0)
if (!ignoreErrorExit && process.ExitCode != 0)
{
throw new InvalidOperationException($"{fileName} command execution failure: {output}");
}
Expand Down
25 changes: 14 additions & 11 deletions src/BenchmarksApps/TLS/HttpSys/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,36 @@

var mTLSNetShFlag = mTlsEnabled ? NetShFlag.Enable : NetShFlag.Disabled;

var netshWrapper = new NetShWrapper();

// verify there is an netsh http sslcert binding for specified ip:port
if (!NetShWrapper.TryGetSslCertBinding(httpsIpPort, out var sslCertBinding))
if (!netshWrapper.TryGetSslCertBinding(httpsIpPort, out var sslCertBinding))
{
Console.WriteLine($"No binding existed. Need to self-sign it and bind to '{httpsIpPort}'");
if (!NetShWrapper.TrySelfSignCertificate(httpsIpPort, out var originalCertThumbprint))
if (!netshWrapper.TrySelfSignCertificate(httpsIpPort, out var originalCertThumbprint))
{
throw new ApplicationException($"Failed to setup ssl binding for '{httpsIpPort}'. Please unblock the VM.");
}
NetShWrapper.AddCertBinding(
netshWrapper.AddCertBinding(
httpsIpPort,
originalCertThumbprint,
disablesessionid: NetShFlag.Enable,
enablesessionticket: NetShFlag.Disabled,
clientCertNegotiation: mTLSNetShFlag);
}

Console.WriteLine("Current netsh ssl certificate binding: " + sslCertBinding);
Console.WriteLine("Current netsh ssl certificate binding: \n" + sslCertBinding);

if (
// those flags can be set only on later versions of HTTP.SYS; so only considering mTLS here
// sslCertBinding.DisableSessionIdTlsResumption != NetShFlag.Enable || sslCertBinding.EnableSessionTicketTlsResumption != NetShFlag.Disabled ||
sslCertBinding.NegotiateClientCertificate != mTLSNetShFlag)
(netshWrapper.SupportsDisableSessionId && sslCertBinding.DisableSessionIdTlsResumption != NetShFlag.Enable)
|| (netshWrapper.SupportsEnableSessionTicket && (sslCertBinding.EnableSessionTicketTlsResumption == NetShFlag.Enable))
|| sslCertBinding.NegotiateClientCertificate != mTLSNetShFlag)
{
Console.WriteLine($"Need to prepare ssl-cert binding for the run.");
Console.WriteLine($"Expected configuration: mTLS={mTLSNetShFlag}");
Console.WriteLine($"Expected configuration: mTLS={mTLSNetShFlag}; disableSessionId={NetShFlag.Enable}; enableSessionTicket={NetShFlag.Disabled}");

NetShWrapper.UpdateCertBinding(
netshWrapper.UpdateCertBinding(
httpsIpPort,
sslCertBinding.CertificateThumbprint,
appId: sslCertBinding.ApplicationId,
Expand Down Expand Up @@ -140,7 +143,7 @@

await app.StartAsync();

NetShWrapper.LogSslCertBinding(httpsIpPort);
netshWrapper.LogSslCertBinding(httpsIpPort);

Console.WriteLine("Application Info:");
if (mTlsEnabled)
Expand All @@ -162,11 +165,11 @@
await app.WaitForShutdownAsync();
Console.WriteLine("Application stopped.");

if (NetShWrapper.TryGetSslCertBinding(httpsIpPort, out sslCertBinding) && mTLSNetShFlag == NetShFlag.Enable)
if (netshWrapper.TryGetSslCertBinding(httpsIpPort, out sslCertBinding) && mTLSNetShFlag == NetShFlag.Enable)
{
// update the sslCert binding to disable "negotiate client cert" (aka mTLS) to not break other tests.
Console.WriteLine($"Rolling back mTLS setting for sslCert binding at '{httpsIpPort}'");

sslCertBinding.NegotiateClientCertificate = NetShFlag.Disabled;
NetShWrapper.UpdateCertBinding(httpsIpPort, sslCertBinding);
netshWrapper.UpdateCertBinding(httpsIpPort, sslCertBinding);
}
Loading