Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: control SSL protocol version on tls jobs #2049

Merged
merged 13 commits into from
Jan 22, 2025
23 changes: 18 additions & 5 deletions scenarios/tls.benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ jobs:
project: src/BenchmarksApps/TLS/HttpSys/HttpSys.csproj
readyStateText: Application started.
variables:
# behavioral settings
mTLS: false # enables settings on http.sys to negotiate client cert on connections
tlsRenegotiation: false # enables client cert validation
# debug settings
certValidationConsoleEnabled: false
httpSysLogs: false
statsEnabled: false
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --httpSysLogs {{httpSysLogs}}"
logRequestDetails: false
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --httpSysLogs {{httpSysLogs}} --logRequestDetails {{logRequestDetails}}"

kestrelServer:
source:
Expand All @@ -29,11 +32,15 @@ jobs:
project: src/BenchmarksApps/TLS/Kestrel/Kestrel.csproj
readyStateText: Application started.
variables:
# behavioral settings
mTLS: false
tlsRenegotiation: false
tlsProtocols: "tls12,tls13"
# debug settings
certValidationConsoleEnabled: false
statsEnabled: false
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}}"
logRequestDetails: false
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --tlsProtocols {{tlsProtocols}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --logRequestDetails {{logRequestDetails}}"

scenarios:

Expand All @@ -43,12 +50,13 @@ scenarios:
application:
job: httpSysServer
load:
job: wrk
job: httpclient
variables:
path: /hello-world
presetHeaders: connectionclose
connections: 32
serverScheme: https
sslProtocol: tls12

mTls-handshakes-httpsys:
application:
Expand All @@ -69,6 +77,7 @@ scenarios:
serverScheme: https
certPath: https://raw.githubusercontent.com/aspnet/Benchmarks/refs/heads/main/src/BenchmarksApps/TLS/HttpSys/testCert.pfx
certPwd: testPassword
sslProtocol: tls12

tls-renegotiation-httpsys:
application:
Expand All @@ -87,19 +96,21 @@ scenarios:
serverScheme: https
certPath: https://raw.githubusercontent.com/aspnet/Benchmarks/refs/heads/main/src/BenchmarksApps/TLS/HttpSys/testCert.pfx
certPwd: testPassword
sslProtocol: tls12

# Kestrel

tls-handshakes-kestrel:
application:
job: kestrelServer
load:
job: wrk
job: httpclient
variables:
path: /hello-world
presetHeaders: connectionclose
connections: 32
serverScheme: https
sslProtocol: tls12

mTls-handshakes-kestrel:
application:
Expand All @@ -116,6 +127,7 @@ scenarios:
serverScheme: https
certPath: https://raw.githubusercontent.com/aspnet/Benchmarks/refs/heads/main/src/BenchmarksApps/TLS/Kestrel/testCert.pfx
certPwd: testPassword
sslProtocol: tls12

tls-renegotiation-kestrel:
application:
Expand All @@ -132,4 +144,5 @@ scenarios:
connections: 32
serverScheme: https
certPath: https://raw.githubusercontent.com/aspnet/Benchmarks/refs/heads/main/src/BenchmarksApps/TLS/Kestrel/testCert.pfx
certPwd: testPassword
certPwd: testPassword
sslProtocol: tls12
36 changes: 32 additions & 4 deletions src/BenchmarksApps/TLS/HttpSys/Program.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
using HttpSys;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.HttpSys;

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

var writeCertValidationEventsToConsole = bool.TryParse(builder.Configuration["certValidationConsoleEnabled"], out var certValidationConsoleEnabled) && certValidationConsoleEnabled;
// behavioral
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://", "");

// debug
var writeCertValidationEventsToConsole = bool.TryParse(builder.Configuration["certValidationConsoleEnabled"], out var certValidationConsoleEnabled) && certValidationConsoleEnabled;
var statsEnabled = bool.TryParse(builder.Configuration["statsEnabled"], out var connectionStatsEnabledConfig) && connectionStatsEnabledConfig;
var logRequestDetails = bool.TryParse(builder.Configuration["logRequestDetails"], out var logRequestDetailsConfig) && logRequestDetailsConfig;

#pragma warning disable CA1416 // Can be launched only on Windows (HttpSys)
builder.WebHost.UseHttpSys(options =>
{
Expand All @@ -30,6 +36,28 @@
var connectionIds = new HashSet<string>();
var fetchedCertsCounter = 0;

if (logRequestDetails)
{
var logged = false;
Console.WriteLine("Registered request details logging middleware");
app.Use(async (context, next) =>
{
if (!logged)
{
logged = true;

var tlsHandshakeFeature = context.Features.GetRequiredFeature<ITlsHandshakeFeature>();

Console.WriteLine("Request details:");
Console.WriteLine("-----");
Console.WriteLine("TLS: " + tlsHandshakeFeature.Protocol);
Console.WriteLine("-----");
}

await next(context);
});
}

if (statsEnabled)
{
Console.WriteLine("Registered stats middleware");
Expand All @@ -38,7 +66,7 @@
connectionIds.Add(context.Connection.Id);
Console.WriteLine($"[stats] unique connections established: {connectionIds.Count}; fetched certificates: {fetchedCertsCounter}");

await next();
await next(context);
});
}

Expand Down Expand Up @@ -104,7 +132,7 @@ void OnShutdown()
// we have a client cert here, and lets imagine we do the validation here
// if (clientCert.Thumbprint != "1234567890") throw new NotImplementedException();

await next();
await next(context);
});
}

Expand Down
70 changes: 66 additions & 4 deletions src/BenchmarksApps/TLS/Kestrel/Program.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
using System.Net;
using System.Net.Security;
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;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https;

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

var writeCertValidationEventsToConsole = bool.TryParse(builder.Configuration["certValidationConsoleEnabled"], out var certValidationConsoleEnabled) && certValidationConsoleEnabled;
// behavioral
var mTlsEnabled = bool.TryParse(builder.Configuration["mTLS"], out var mTlsEnabledConfig) && mTlsEnabledConfig;
var tlsRenegotiationEnabled = bool.TryParse(builder.Configuration["tlsRenegotiation"], out var tlsRenegotiationEnabledConfig) && tlsRenegotiationEnabledConfig;
var statsEnabled = bool.TryParse(builder.Configuration["statsEnabled"], out var connectionStatsEnabledConfig) && connectionStatsEnabledConfig;
var listeningEndpoints = builder.Configuration["urls"] ?? "https://localhost:5000/";
var supportedTlsVersions = ParseSslProtocols(builder.Configuration["tlsProtocols"]);

// debug
var writeCertValidationEventsToConsole = bool.TryParse(builder.Configuration["certValidationConsoleEnabled"], out var certValidationConsoleEnabled) && certValidationConsoleEnabled;
var statsEnabled = bool.TryParse(builder.Configuration["statsEnabled"], out var connectionStatsEnabledConfig) && connectionStatsEnabledConfig;
var logRequestDetails = bool.TryParse(builder.Configuration["logRequestDetails"], out var logRequestDetailsConfig) && logRequestDetailsConfig;

if (mTlsEnabled && tlsRenegotiationEnabled)
{
Expand All @@ -40,6 +48,11 @@
// [SuppressMessage("Microsoft.Security", "CSCAN0220.DefaultPasswordContexts", Justification="Benchmark code, not a secret")]
listenOptions.UseHttps("testCert.pfx", "testPassword", options =>
{
if (supportedTlsVersions is not null)
{
options.SslProtocols = supportedTlsVersions.Value;
}

if (mTlsEnabled)
{
options.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
Expand Down Expand Up @@ -81,6 +94,28 @@
return true;
}

if (logRequestDetails)
{
var logged = false;
Console.WriteLine("Registered request details logging middleware");
app.Use(async (context, next) =>
{
if (!logged)
{
logged = true;

var tlsHandshakeFeature = context.Features.GetRequiredFeature<ITlsHandshakeFeature>();

Console.WriteLine("Request details:");
Console.WriteLine("-----");
Console.WriteLine("TLS: " + tlsHandshakeFeature.Protocol);
Console.WriteLine("-----");
}

await next(context);
});
}

if (statsEnabled)
{
Console.WriteLine("Registered stats middleware");
Expand All @@ -89,7 +124,7 @@
connectionIds.Add(context.Connection.Id);
Console.WriteLine($"[stats] unique connections established: {connectionIds.Count}; fetched certificates: {fetchedCertsCounter}");

await next();
await next(context);
});
}

Expand All @@ -109,7 +144,7 @@
Console.WriteLine($"client certificate ({clientCert.Thumbprint}) already exists on the connection {context.Connection.Id}");
}

await next();
await next(context);
});
}

Expand Down Expand Up @@ -137,6 +172,7 @@
{
Console.WriteLine($"\tenabled logging stats to console");
}
Console.WriteLine($"\tsupported TLS versions: {supportedTlsVersions}");
Console.WriteLine($"\tlistening endpoints: {listeningEndpoints}");
Console.WriteLine("--------------------------------");

Expand All @@ -151,10 +187,36 @@
{
ip = IPAddress.Loopback;
}
else if (!IPAddress.TryParse(urlPrefix.Host, out ip))

Check warning on line 190 in src/BenchmarksApps/TLS/Kestrel/Program.cs

View workflow job for this annotation

GitHub Actions / Build & Test (ubuntu-latest)

Converting null literal or possible null value to non-nullable type.
{
ip = IPAddress.IPv6Any;
}

return new IPEndPoint(ip, urlPrefix.PortValue);
}

static SslProtocols? ParseSslProtocols(string? supportedTlsVersions)
{
var protocols = SslProtocols.None;
if (string.IsNullOrEmpty(supportedTlsVersions) || supportedTlsVersions == "any")
{
return null;
}

foreach (var version in supportedTlsVersions.Split(','))
{
switch (version.Trim().ToLower())
{
case "tls12":
protocols |= SslProtocols.Tls12;
break;
case "tls13":
protocols |= SslProtocols.Tls13;
break;
default:
throw new ArgumentException($"Unsupported TLS version: {version}");
}
}

return protocols;
}
Loading