Skip to content

Commit

Permalink
Add support for TLS to Kestrel on macOS (dotnet#45722)
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK authored Dec 22, 2022
1 parent 2e33c67 commit 8d8e5a8
Show file tree
Hide file tree
Showing 20 changed files with 60 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

// Additional configuration is required to successfully run gRPC on macOS.
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682

// Add services to the container.
builder.Services.AddGrpc();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

var builder = WebApplication.CreateBuilder(args);

// Additional configuration is required to successfully run gRPC on macOS.
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682

// Add services to the container.
builder.Services.AddGrpc();

Expand Down
24 changes: 4 additions & 20 deletions src/ProjectTemplates/test/Templates.Tests/GrpcTemplateTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,8 @@ public async Task GrpcTemplate(bool useProgramMain)

using (var serverProcess = project.StartBuiltProjectAsync(hasListeningUri: !unsupported, logger: Logger))
{
// These templates are HTTPS + HTTP/2 only which is not supported on Mac due to missing ALPN support.
// https://github.com/dotnet/aspnetcore/issues/11061
if (isOsx)
{
serverProcess.Process.WaitForExit(assertSuccess: false);
Assert.True(serverProcess.Process.HasExited, "built");
Assert.Contains("System.NotSupportedException: HTTP/2 over TLS is not supported on macOS due to missing ALPN support.",
ErrorMessages.GetFailedProcessMessageOrEmpty("Run built service", project, serverProcess.Process));
}
else if (isWindowsOld)
// These templates are HTTPS + HTTP/2 only which is not supported on some platforms.
if (isWindowsOld)
{
serverProcess.Process.WaitForExit(assertSuccess: false);
Assert.True(serverProcess.Process.HasExited, "built");
Expand All @@ -86,16 +78,8 @@ public async Task GrpcTemplate(bool useProgramMain)

using (var aspNetProcess = project.StartPublishedProjectAsync(hasListeningUri: !unsupported))
{
// These templates are HTTPS + HTTP/2 only which is not supported on Mac due to missing ALPN support.
// https://github.com/dotnet/aspnetcore/issues/11061
if (isOsx)
{
aspNetProcess.Process.WaitForExit(assertSuccess: false);
Assert.True(aspNetProcess.Process.HasExited, "published");
Assert.Contains("System.NotSupportedException: HTTP/2 over TLS is not supported on macOS due to missing ALPN support.",
ErrorMessages.GetFailedProcessMessageOrEmpty("Run published service", project, aspNetProcess.Process));
}
else if (isWindowsOld)
// These templates are HTTPS + HTTP/2 only which is not supported on some platforms.
if (isWindowsOld)
{
aspNetProcess.Process.WaitForExit(assertSuccess: false);
Assert.True(aspNetProcess.Process.HasExited, "published");
Expand Down
4 changes: 2 additions & 2 deletions src/Servers/Kestrel/Core/src/CoreStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -548,8 +548,8 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
<data name="RequestTrailersNotAvailable" xml:space="preserve">
<value>The request trailers are not available yet. They may not be available until the full request body is read.</value>
</data>
<data name="Http2NoTlsOsx" xml:space="preserve">
<value>HTTP/2 over TLS is not supported on macOS due to missing ALPN support.</value>
<data name="Http2NoTlsAlpn" xml:space="preserve">
<value>HTTP/2 over TLS is not supported due to missing ALPN support.</value>
</data>
<data name="Http2StreamResetByApplication" xml:space="preserve">
<value>The HTTP/2 stream was reset by the application with error code {errorCode}.</value>
Expand Down
10 changes: 10 additions & 0 deletions src/Servers/Kestrel/Core/src/Internal/Infrastructure/TlsAlpn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;

internal static class TlsAlpn
{
// Replace with https://github.com/dotnet/runtime/issues/79687
public static bool IsSupported { get; } = true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -466,9 +466,9 @@ internal static HttpProtocols ValidateAndNormalizeHttpProtocols(HttpProtocols ht
// This configuration will always fail per-request, preemptively fail it here. See HttpConnection.SelectProtocol().
if (httpProtocols == HttpProtocols.Http2)
{
if (OperatingSystem.IsMacOS())
if (!TlsAlpn.IsSupported)
{
throw new NotSupportedException(CoreStrings.Http2NoTlsOsx);
throw new NotSupportedException(CoreStrings.Http2NoTlsAlpn);
}
else if (_isWindowsVersionIncompatibleWithHttp2)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,15 @@ public void ConfigureEndpoint_DoesNotThrowWhen_HttpsConfigIsDeclaredInEndpointDe
public void DefaultConfigSectionCanSetProtocols_MacAndWin7(string input, HttpProtocols expected)
=> DefaultConfigSectionCanSetProtocols(input, expected);

[ConditionalTheory]
[InlineData("http1", HttpProtocols.Http1)]
[InlineData("http2", HttpProtocols.Http2)]
[InlineData("http1AndHttp2", HttpProtocols.Http1AndHttp2)]
// [InlineData("http1AndHttp2andHttp3", HttpProtocols.Http1AndHttp2AndHttp3)] // HTTP/3 not currently supported on macOS
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win81)]
public void DefaultConfigSectionCanSetProtocols_NonWin7(string input, HttpProtocols expected)
=> DefaultConfigSectionCanSetProtocols(input, expected);

[ConditionalTheory]
[InlineData("http1", HttpProtocols.Http1)]
[InlineData("http2", HttpProtocols.Http2)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<Compile Include="$(KestrelSharedSourceRoot)test\TransportTestHelpers\IHostPortExtensions.cs" Link="shared\TransportTestHelpers\IHostPortExtensions.cs" />
<Content Include="$(KestrelSharedSourceRoot)test\TestCertificates\*.pfx" LinkBase="shared\TestCertificates" CopyToOutputDirectory="PreserveNewest" />
<Compile Include="$(KestrelSharedSourceRoot)test\ServerRetryHelper.cs" LinkBase="shared" />
<Compile Include="$(KestrelSharedSourceRoot)test\TransportTestHelpers\TlsAlpnSupportedAttribute.cs" Link="shared\TransportTestHelpers\TlsAlpnSupportedAttribute.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ private static Version GetClientVersion(HttpProtocols protocols)
}

[ConditionalTheory]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing SslStream ALPN support: https://github.com/dotnet/runtime/issues/27727")]
[TlsAlpnSupported]
[InlineData(HttpProtocols.Http1)]
[InlineData(HttpProtocols.Http2)]
public async Task ListenNamedPipeEndpoint_Tls_ClientSuccess(HttpProtocols protocols)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.AspNetCore.Testing;

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
public class TlsAlpnSupportedAttribute : Attribute, ITestCondition
{
public bool IsMet => true; // Replace with https://github.com/dotnet/runtime/issues/79687
public string SkipReason => "TLS ALPN is not supported on the current test machine";
}
25 changes: 2 additions & 23 deletions src/Servers/Kestrel/test/FunctionalTests/Http2/HandshakeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,27 +39,6 @@ public HandshakeTests()
};
}

[ConditionalFact]
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.Windows)]
// Mac SslStream is missing ALPN support: https://github.com/dotnet/runtime/issues/27727
public void TlsAndHttp2NotSupportedOnMac()
{
var ex = Assert.Throws<NotSupportedException>(() => new TestServer(context =>
{
throw new NotImplementedException();
}, new TestServiceContext(LoggerFactory),
kestrelOptions =>
{
kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
listenOptions.UseHttps(_x509Certificate2);
});
}));

Assert.Equal("HTTP/2 over TLS is not supported on macOS due to missing ALPN support.", ex.Message);
}

[ConditionalFact]
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
[MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win7)]
Expand All @@ -83,7 +62,7 @@ public void TlsAndHttp2NotSupportedOnWin7()
}

[ConditionalFact]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing SslStream ALPN support: https://github.com/dotnet/runtime/issues/27727")]
[TlsAlpnSupported]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10)]
public async Task TlsAlpnHandshakeSelectsHttp2From1and2()
{
Expand Down Expand Up @@ -111,7 +90,7 @@ public async Task TlsAlpnHandshakeSelectsHttp2From1and2()
}

[ConditionalFact]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing SslStream ALPN support: https://github.com/dotnet/runtime/issues/27727")]
[TlsAlpnSupported]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10)]
public async Task TlsAlpnHandshakeSelectsHttp2()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Sockets.FunctionalTests.Http2;
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2;
#endif

[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing SslStream ALPN support: https://github.com/dotnet/runtime/issues/27727")]
[TlsAlpnSupported]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10)]
public class ShutdownTests : TestApplicationErrorLoggerLoggedTest
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ await connection.ReceiveEnd("HTTP/1.1 101 Switching Protocols",
}

[ConditionalFact]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing SslStream ALPN support: https://github.com/dotnet/runtime/issues/27727")]
[TlsAlpnSupported]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10)]
public async Task Http2_EmitsStartAndStopEventsWithActivityIds()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class TlsTests : LoggedTest
private static readonly X509Certificate2 _x509Certificate2 = TestResources.GetTestCertificate();

[ConditionalFact]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing SslStream ALPN support: https://github.com/dotnet/runtime/issues/27727")]
[TlsAlpnSupported]
[OSSkipCondition(OperatingSystems.Linux, SkipReason = "TLS 1.1 ciphers are now disabled by default: https://github.com/dotnet/docs/issues/20842")]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10,
SkipReason = "Missing Windows ALPN support: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation#Support or incompatible ciphers on Windows 8.1")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ void ConfigureListenOptions(ListenOptions listenOptions)
[ConditionalTheory]
[InlineData(HttpProtocols.Http1)]
[InlineData(HttpProtocols.Http1AndHttp2)] // Make sure turning on Http/2 doesn't regress HTTP/1
[TlsAlpnSupported]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing platform support.")]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/33566#issuecomment-892031659", Queues = HelixConstants.RedhatAmd64)] // Outdated OpenSSL client
public async Task CanRenegotiateForClientCertificate(HttpProtocols httpProtocols)
Expand Down Expand Up @@ -611,6 +612,7 @@ void ConfigureListenOptions(ListenOptions listenOptions)
}

[ConditionalFact]
[TlsAlpnSupported]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing platform support.")]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/33566#issuecomment-892031659", Queues = HelixConstants.RedhatAmd64)] // Outdated OpenSSL client
public async Task CanRenegotiateForTlsCallbackOptions()
Expand Down Expand Up @@ -658,6 +660,7 @@ void ConfigureListenOptions(ListenOptions listenOptions)
}

[ConditionalFact]
[TlsAlpnSupported]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing platform support.")]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/33566#issuecomment-892031659", Queues = HelixConstants.RedhatAmd64)] // Outdated OpenSSL client
public async Task CanRenegotiateForClientCertificateOnHttp1CanReturnNoCert()
Expand Down Expand Up @@ -782,6 +785,7 @@ void ConfigureListenOptions(ListenOptions listenOptions)
// TLS 1.2 and lower have to renegotiate the whole connection to get a client cert, and if that hits an error
// then the connection is aborted.
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing platform support.")]
[TlsAlpnSupported]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/33566#issuecomment-892031659", Queues = HelixConstants.RedhatAmd64)] // Outdated OpenSSL client
public async Task RenegotiateForClientCertificateOnPostWithoutBufferingThrows()
{
Expand Down Expand Up @@ -824,7 +828,7 @@ void ConfigureListenOptions(ListenOptions listenOptions)

[ConditionalFact]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10)] // HTTP/2 requires Win10
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "ALPN not supported")]
[TlsAlpnSupported]
public async Task ServerOptionsSelectionCallback_SetsALPN()
{
static void ConfigureListenOptions(ListenOptions listenOptions)
Expand Down Expand Up @@ -852,7 +856,7 @@ await stream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions()

[ConditionalFact]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10)] // HTTP/2 requires Win10
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "ALPN not supported")]
[TlsAlpnSupported]
public async Task TlsHandshakeCallbackOptionsOverload_SetsALPN()
{
static void ConfigureListenOptions(ListenOptions listenOptions)
Expand Down Expand Up @@ -884,7 +888,7 @@ await stream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions()
}

[ConditionalFact]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "ALPN not supported")]
[TlsAlpnSupported]
public async Task TlsHandshakeCallbackOptionsOverload_EmptyAlpnList_DisablesAlpn()
{
static void ConfigureListenOptions(ListenOptions listenOptions)
Expand Down Expand Up @@ -917,6 +921,7 @@ await stream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions()
}

[ConditionalFact]
[TlsAlpnSupported]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing platform support.")]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/33566#issuecomment-892031659", Queues = HelixConstants.RedhatAmd64)] // Outdated OpenSSL client
public async Task CanRenegotiateForClientCertificateOnPostIfDrained()
Expand Down Expand Up @@ -964,6 +969,7 @@ void ConfigureListenOptions(ListenOptions listenOptions)

[ConditionalFact]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing platform support.")]
[TlsAlpnSupported]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/33566#issuecomment-892031659", Queues = HelixConstants.RedhatAmd64)] // Outdated OpenSSL client
public async Task RenegotationFailureCausesConnectionClose()
{
Expand Down Expand Up @@ -1265,7 +1271,7 @@ public void ThrowsForCertificatesMissingServerEku(string testCertName)
[InlineData(HttpProtocols.Http1)]
[InlineData(HttpProtocols.Http2)]
[InlineData(HttpProtocols.Http1AndHttp2)]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing SslStream ALPN support: https://github.com/dotnet/runtime/issues/27727")]
[TlsAlpnSupported]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10)]
public async Task ListenOptionsProtolsCanBeSetAfterUseHttps(HttpProtocols httpProtocols)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<Compile Include="$(KestrelSharedSourceRoot)\TransportConnection.cs" Link="Internal\TransportConnection.cs" />
<Compile Include="$(KestrelSharedSourceRoot)\TransportConnection.Generated.cs" Link="Internal\TransportConnection.Generated.cs" />
<Compile Include="$(KestrelSharedSourceRoot)\TransportConnection.FeatureCollection.cs" Link="Internal\TransportConnection.FeatureCollection.cs" />
<Compile Include="$(KestrelSharedSourceRoot)test\TransportTestHelpers\TlsAlpnSupportedAttribute.cs" Link="shared\TransportTestHelpers\TlsAlpnSupportedAttribute.cs" />
<Compile Include="$(SharedSourceRoot)TypeNameHelper\*.cs" />

<Content Include="$(KestrelSharedSourceRoot)test\TestCertificates\*.pfx" LinkBase="shared\TestCertificates" CopyToOutputDirectory="PreserveNewest" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private void InitializeArgs()
}

[ConditionalTheory(Skip = "Disabling while debugging. https://github.com/dotnet/aspnetcore-internal/issues/1363")]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing SslStream ALPN support: https://github.com/dotnet/runtime/issues/27727")]
[TlsAlpnSupported]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win81, SkipReason = "Missing Windows ALPN support: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation#Support")]
[InlineData("", "Interop HTTP/2 GET")]
[InlineData("?TestMethod=POST", "Interop HTTP/2 POST")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
<Compile Include="$(KestrelSharedSourceRoot)test\TestResources.cs" LinkBase="shared" />
<Compile Include="$(SharedSourceRoot)HttpClient\HttpEventSourceListener.cs" LinkBase="shared" />
<Content Include="$(KestrelSharedSourceRoot)test\TestCertificates\*.pfx" LinkBase="shared\TestCertificates" CopyToOutputDirectory="PreserveNewest" />
<Compile Include="$(KestrelSharedSourceRoot)test\TransportTestHelpers\MsQuicSupportedAttribute.cs" LinkBase="shared\TransportTestHelpers\MsQuicSupportedAttribute.cs" />
<Compile Include="$(KestrelSharedSourceRoot)test\TransportTestHelpers\MsQuicSupportedAttribute.cs" Link="shared\TransportTestHelpers\MsQuicSupportedAttribute.cs" />
<Compile Include="$(KestrelSharedSourceRoot)test\TransportTestHelpers\TlsAlpnSupportedAttribute.cs" Link="shared\TransportTestHelpers\TlsAlpnSupportedAttribute.cs" />
<Compile Include="$(KestrelSharedSourceRoot)test\ServerRetryHelper.cs" LinkBase="shared" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ internal static bool CurrentPlatformSupportsHTTP2OverTls()
{
return // "Missing Windows ALPN support: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation#Support" or missing compatible ciphers (Win8.1)
new MinimumOSVersionAttribute(OperatingSystems.Windows, WindowsVersions.Win10).IsMet
// "Missing SslStream ALPN support: https://github.com/dotnet/runtime/issues/27727"
&& new OSSkipConditionAttribute(OperatingSystems.MacOSX).IsMet;
&& new TlsAlpnSupportedAttribute().IsMet;
}
}
Loading

0 comments on commit 8d8e5a8

Please sign in to comment.