-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
Is there an existing issue for this?
- I have searched the existing issues
Is your feature request related to a problem? Please describe the problem.
Kestrel currently allows configuration of a wide variety of TLS settings on a per-SNI basis via SslStream. (such as choosing the server TLS certificate for a given SNI, whether to negotiate a client certificate eagerly or in a delayed manner, the list of distinguished names to send in the TLS handshake, etc)
However, Kestrel does not allow the configuration of signature algorithms on any platform. Also, Kestrel does not allow configuration of cipher suites on Windows. HTTP.sys provides this functionality on Windows and I'm requesting this capability on Kestrel (on both Windows and Linux).
Note: The signature algorithms and cipher suites in Windows are centrally set via Schannel registry keys. Both Kestrel and HTTP.sys delegate to using settings chosen by Schannel. However, HTTP.Sys provides APIs to modify the centrally selected settings on a per-SNI basis - and that capability is what I'm requesting on Kestrel. For a multi-tenant reverse proxy, the ability to choose TLS settings on a per-SNI basis is crucial.
Describe the solution you'd like
I understand that the API shape exposed via ServerOptionsSelectionCallback is not quite the same as HTTP.sys. That is why I'm only describing how HTTP.sys achieves the capability I need. I'm not suggesting a particular API shape for how Kestrel may implement the feature.
The below code uses HTTP.sys APIs to configure signature algorithms and cipher suites on a per SNI basis.
Note: The code below is not meant to be used in production as-is. The intent is to provide a high-level overview of how per-SNI TLS settings are configured on HTTP.sys.
// Let's configure TLS settings for foo.com via HTTP.sys APIs. This is done in 2 steps.
// Step 1: On HTTP.sys, the TLS configuration for any SNI is created by invoking HttpSetServiceConfiguration
// and passing in a record of type HttpServiceConfigSslSniCertInfo. This incantation creates the "normal" TLS settings on the SNI.
// Step 2: If we need to configure more advanced TLS settings on the SNI, this is done by invoking
// HttpSetServiceConfiguration and passing in a record of type HttpServiceConfigSslSniCertInfoEx.
// In the below example, let's configure the "normal" TLS settings on foo.com.
// Then, we configure advanced TLS settings such as disabling:
// 1) the Diffie-Helman KeyExchangeAlgorithm, 2) the RSA_PSS signature algorithm 3) the 3DES cipher suite on the foo.com SNI.
// Step 1: Configure "normal" TLS settings on foo.com.
// The HTTP.sys SNI config. We are going to stuff all the TLS settings into this config.
HTTP_SERVICE_CONFIG_SSL_SNI_SET config = {};
// Let's set Host in the HTTP.sys SNI config.
PCWSTR Host = L"foo.com";
config.KeyDesc.Host = (PWSTR)Host;
SOCKADDR_STORAGE addr = {};
SOCKADDR_IN* inAddr = (SOCKADDR_IN*)&addr;
inAddr->sin_port = htons(443);
inAddr->sin_family = AF_INET;
inAddr->sin_addr.S_un.S_addr = 0; // for HTTP.sys SNI configuration, address must be 0.
// Let's set the IP and port in the HTTP.sys SNI config.
config.KeyDesc.IpPort = addr;
// Let's set TLS flags in the HTTP.sys SNI config.
// There are various TLS settings that can be configured here. Please see lines 2491- 2509 of http.h for a full list of flags in the additional context section of this github issue.
// In this case, we are turning on client certificate negotiation and turning off QUIC.
config.ParamDesc.DefaultFlags = HTTP_SERVICE_CONFIG_SSL_FLAG_NEGOTIATE_CLIENT_CERT | HTTP_SERVICE_CONFIG_SSL_FLAG_DISABLE_QUIC;
// Let's set the thumbprint of foo.com and the certificate store in the HTTP.sys SNI config.
char thumbprint[] = { 0x98, 0x99, ... }; // Let's assume this array is filled out properly and contains a legitimate thumbprint.
config.ParamDesc.pSslHash = thumbprint;
config.ParamDesc.pSslCertStoreName = (PWSTR)L"MY";
// Finally, let's call HTTP.sys and pass in the config for foo.com. Step 1 is complete!
HttpSetServiceConfiguration(NULL, HttpServiceConfigSslSniCertInfo, &config, sizeof(config), NULL);
// Step 2: Let's configure the advanced (extended) TLS settings on foo.com.
// The HTTP.sys extended SNI config.
HTTP_SERVICE_CONFIG_SSL_SNI_SET_EX configEx = {};
// Set Host and IP, port.
configEx.KeyDesc.Host = (PWSTR)Host;
configEx.KeyDesc.IpPort = addr;
// Create Crypto setting to disable.
CRYPTO_SETTINGS settings[3] = {};
// Create Diffie-Helman Key Exchange setting.
settings[0].eAlgorithmUsage = TlsParametersCngAlgUsageKeyExchange;
UNICODE_STRING dhString;
RtlInitUnicodeString(&dhString, L"DH");
settings[0].strCngAlgId = dhString;
// Create RSA-PSS signature algorithm setting.
settings[1].eAlgorithmUsage = TlsParametersCngAlgUsageCertSig;
UNICODE_STRING rsapssString;
RtlInitUnicodeString(&rsapssString, L"SCH_RSA_PSS_PAD");
settings[1].strCngAlgId = rsapssString;
// Create 3DES cipher suite setting.
settings[2].eAlgorithmUsage = TlsParametersCngAlgUsageCipher;
UNICODE_STRING cipherString;
RtlInitUnicodeString(&cipherString, L"3DES");
settings[2].strCngAlgId = cipherString;
// Disable the above crypto settings.
TLS_PARAMETERS params[1] = {};
params[0].pDisabledCrypto = settings;
params[0].cDisabledCrypto = 3;
HTTP_TLS_RESTRICTIONS_PARAM restrictions = {};
restrictions.RestrictionCount = 1;
restrictions.TlsRestrictions = params;
configEx.ParamDesc.ParamType = ExParamTypeTlsRestrictions;
configEx.ParamDesc.HttpTlsRestrictionsParam = restrictions;
// Call HTTP.sys again and pass in the extended config for foo.com. Step 2 is complete!
HttpSetServiceConfiguration(NULL, HttpServiceConfigSslSniCertInfoEx, &configEx, sizeof(configEx), NULL);
Additional context
- Regarding signature algorithms: Kestrel does not support configurability on any platform. Linking useful documentation that make this feature possible:
- Windows: https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-crypto_settings (This is what the above HTTP.sys code leverages to configure signature algorithms and cipher suites.)
- Linux: https://docs.openssl.org/3.0/man3/SSL_CTX_set1_sigalgs/. See: SL_CTX_set1_client_sigalgs()
-
Regarding cipher suites: I'm aware that SslServerAuthenticationOptions.CipherSuitesPolicy provides cipher suite configurability for Kestrel on Linux. However, this throws a PlatformNotSupported Exception on Windows.
-
Snippet from http.h for the various flags that can be used while configuring TLS settings on HTTP.sys (I refer to these flags in the code above and I wanted to provide a list of all possible flags):