-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Downgrade to HTTP/1.1 on older Windows versions #22859
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
Changes from all commits
74252ca
0f7025f
8943fd4
b80d492
b6e7762
c975412
abb36a0
d487550
8f8c286
4124533
2b8034b
6f56163
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal | |
{ | ||
internal class HttpsConnectionMiddleware | ||
{ | ||
private const string EnableWindows81Http2 = "Microsoft.AspNetCore.Server.Kestrel.EnableWindows81Http2"; | ||
private readonly ConnectionDelegate _next; | ||
private readonly HttpsConnectionAdapterOptions _options; | ||
private readonly ILogger _logger; | ||
|
@@ -43,18 +44,26 @@ public HttpsConnectionMiddleware(ConnectionDelegate next, HttpsConnectionAdapter | |
throw new ArgumentNullException(nameof(options)); | ||
} | ||
|
||
_options = options; | ||
_logger = loggerFactory.CreateLogger<HttpsConnectionMiddleware>(); | ||
|
||
// This configuration will always fail per-request, preemptively fail it here. See HttpConnection.SelectProtocol(). | ||
if (options.HttpProtocols == HttpProtocols.Http2) | ||
{ | ||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) | ||
{ | ||
throw new NotSupportedException(CoreStrings.HTTP2NoTlsOsx); | ||
throw new NotSupportedException(CoreStrings.Http2NoTlsOsx); | ||
} | ||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version < new Version(6, 2)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any risk that someone got ALPN working on Windows 8 somehow and this change breaks them despite the AppContext switch? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This probably warrants a breaking changes announcement. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This definitely warrants a breaking change announcement. If someone does have ALPN working on Windows 8, changing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ALPN support was added in Windows 8.1 so I don't think that was ever a scenario that worked. I made the change since I noticed that Windows 8.1 which was where ALPN support was first added is version 6.3 instead of 6.2. Unless there was an actual reason for allowing Http2 on Windows 8? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ping @Tratcher There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No reason to allow it on Win8 without ALPN, it's the same as Win7. |
||
else if (IsWindowsVersionIncompatible()) | ||
{ | ||
throw new NotSupportedException(CoreStrings.HTTP2NoTlsWin7); | ||
throw new NotSupportedException(CoreStrings.Http2NoTlsWin81); | ||
} | ||
JunTaoLuo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
else if (options.HttpProtocols == HttpProtocols.Http1AndHttp2 && IsWindowsVersionIncompatible()) | ||
{ | ||
_logger.Http2DefaultCiphersInsufficient(); | ||
options.HttpProtocols = HttpProtocols.Http1; | ||
} | ||
|
||
_next = next; | ||
// capture the certificate now so it can't be switched after validation | ||
|
@@ -75,9 +84,6 @@ public HttpsConnectionMiddleware(ConnectionDelegate next, HttpsConnectionAdapter | |
{ | ||
EnsureCertificateIsAllowedForServerAuth(_serverCertificate); | ||
} | ||
|
||
_options = options; | ||
_logger = loggerFactory.CreateLogger<HttpsConnectionMiddleware>(); | ||
} | ||
|
||
public async Task OnConnectionAsync(ConnectionContext context) | ||
|
@@ -214,7 +220,7 @@ public async Task OnConnectionAsync(ConnectionContext context) | |
KestrelEventSource.Log.TlsHandshakeFailed(context.ConnectionId); | ||
KestrelEventSource.Log.TlsHandshakeStop(context, null); | ||
|
||
_logger.LogDebug(2, CoreStrings.AuthenticationTimedOut); | ||
_logger.AuthenticationTimedOut(); | ||
await sslStream.DisposeAsync(); | ||
return; | ||
} | ||
|
@@ -223,7 +229,7 @@ public async Task OnConnectionAsync(ConnectionContext context) | |
KestrelEventSource.Log.TlsHandshakeFailed(context.ConnectionId); | ||
KestrelEventSource.Log.TlsHandshakeStop(context, null); | ||
|
||
_logger.LogDebug(1, ex, CoreStrings.AuthenticationFailed); | ||
_logger.AuthenticationFailed(ex); | ||
await sslStream.DisposeAsync(); | ||
return; | ||
} | ||
|
@@ -232,7 +238,7 @@ public async Task OnConnectionAsync(ConnectionContext context) | |
KestrelEventSource.Log.TlsHandshakeFailed(context.ConnectionId); | ||
KestrelEventSource.Log.TlsHandshakeStop(context, null); | ||
|
||
_logger.LogDebug(1, ex, CoreStrings.AuthenticationFailed); | ||
_logger.AuthenticationFailed(ex); | ||
|
||
await sslStream.DisposeAsync(); | ||
return; | ||
|
@@ -252,7 +258,7 @@ public async Task OnConnectionAsync(ConnectionContext context) | |
|
||
KestrelEventSource.Log.TlsHandshakeStop(context, feature); | ||
|
||
_logger.LogDebug(3, CoreStrings.HttpsConnectionEstablished, context.ConnectionId, sslStream.SslProtocol); | ||
_logger.HttpsConnectionEstablished(context.ConnectionId, sslStream.SslProtocol); | ||
|
||
var originalTransport = context.Transport; | ||
|
||
|
@@ -298,5 +304,57 @@ private static X509Certificate2 ConvertToX509Certificate2(X509Certificate certif | |
|
||
return new X509Certificate2(certificate); | ||
} | ||
|
||
private static bool IsWindowsVersionIncompatible() | ||
{ | ||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | ||
{ | ||
var enableHttp2OnWindows81 = AppContext.TryGetSwitch(EnableWindows81Http2, out var enabled) && enabled; | ||
if (Environment.OSVersion.Version < new Version(6, 3) // Missing ALPN support | ||
// Win8.1 and 2012 R2 don't support the right cipher configuration by default. | ||
|| (Environment.OSVersion.Version < new Version(10, 0) && !enableHttp2OnWindows81)) | ||
JunTaoLuo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} | ||
|
||
internal static class HttpsConnectionMiddlewareLoggerExtensions | ||
{ | ||
|
||
private static readonly Action<ILogger, Exception> _authenticationFailed = | ||
LoggerMessage.Define( | ||
logLevel: LogLevel.Debug, | ||
eventId: new EventId(1, "AuthenticationFailed"), | ||
formatString: CoreStrings.AuthenticationFailed); | ||
|
||
private static readonly Action<ILogger, Exception> _authenticationTimedOut = | ||
LoggerMessage.Define( | ||
logLevel: LogLevel.Debug, | ||
eventId: new EventId(2, "AuthenticationTimedOut"), | ||
formatString: CoreStrings.AuthenticationTimedOut); | ||
|
||
private static readonly Action<ILogger, string, SslProtocols, Exception> _httpsConnectionEstablished = | ||
LoggerMessage.Define<string, SslProtocols>( | ||
logLevel: LogLevel.Debug, | ||
eventId: new EventId(3, "HttpsConnectionEstablished"), | ||
formatString: CoreStrings.HttpsConnectionEstablished); | ||
|
||
private static readonly Action<ILogger, Exception> _http2DefaultCiphersInsufficient = | ||
LoggerMessage.Define( | ||
logLevel: LogLevel.Information, | ||
halter73 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
eventId: new EventId(4, "Http2DefaultCiphersInsufficient"), | ||
formatString: CoreStrings.Http2DefaultCiphersInsufficient); | ||
|
||
public static void AuthenticationFailed(this ILogger logger, Exception exception) => _authenticationFailed(logger, exception); | ||
|
||
public static void AuthenticationTimedOut(this ILogger logger) => _authenticationTimedOut(logger, null); | ||
|
||
public static void HttpsConnectionEstablished(this ILogger logger, string connectionId, SslProtocols sslProtocol) => _httpsConnectionEstablished(logger, connectionId, sslProtocol, null); | ||
|
||
public static void Http2DefaultCiphersInsufficient(this ILogger logger) => _http2DefaultCiphersInsufficient(logger, null); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we mention how to change the enabled ciphers and re-enable HTTP/2 on older Windows versions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the best we can do is get a aka.ms link to something like https://docs.microsoft.com/en-us/windows/win32/secauthn/tls-cipher-suites-in-windows-8-1, would that be sufficient?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A breaking change announcement should be sufficient. We can follow up with anyone that's actually trying to make it work.