diff --git a/source/Halibut.Tests.DotMemory/Halibut.Tests.DotMemory.csproj b/source/Halibut.Tests.DotMemory/Halibut.Tests.DotMemory.csproj index cbd3546a0..84fb9e318 100644 --- a/source/Halibut.Tests.DotMemory/Halibut.Tests.DotMemory.csproj +++ b/source/Halibut.Tests.DotMemory/Halibut.Tests.DotMemory.csproj @@ -39,7 +39,7 @@ - + diff --git a/source/Halibut.Tests/Halibut.Tests.csproj b/source/Halibut.Tests/Halibut.Tests.csproj index a89c6a6e1..4862ad729 100644 --- a/source/Halibut.Tests/Halibut.Tests.csproj +++ b/source/Halibut.Tests/Halibut.Tests.csproj @@ -44,10 +44,11 @@ + - + diff --git a/source/Halibut.Tests/Support/BackwardsCompatibility/LatestClientAndPreviousServiceVersionBuilder.cs b/source/Halibut.Tests/Support/BackwardsCompatibility/LatestClientAndPreviousServiceVersionBuilder.cs index 4d0d041d2..e2a3c904b 100644 --- a/source/Halibut.Tests/Support/BackwardsCompatibility/LatestClientAndPreviousServiceVersionBuilder.cs +++ b/source/Halibut.Tests/Support/BackwardsCompatibility/LatestClientAndPreviousServiceVersionBuilder.cs @@ -1,12 +1,17 @@ using System; +using System.Collections.Concurrent; +using System.IO; using System.Threading; using System.Threading.Tasks; +using Halibut.Diagnostics; using Halibut.Diagnostics.LogCreators; using Halibut.Logging; using Halibut.TestProxy; using Halibut.Tests.Support.Logging; using Halibut.Transport.Proxy; using Octopus.TestPortForwarder; +using Serilog; +using ILog = Halibut.Diagnostics.ILog; namespace Halibut.Tests.Support.BackwardsCompatibility { @@ -21,6 +26,7 @@ public class LatestClientAndPreviousServiceVersionBuilder : IClientAndServiceBui ProxyFactory? proxyFactory; Reference? proxyServiceReference; LogLevel halibutLogLevel = LogLevel.Trace; + StringWriter? loggerStringWriter; readonly OldServiceAvailableServices availableServices = new(false, false); LatestClientAndPreviousServiceVersionBuilder(ServiceConnectionType serviceConnectionType, CertAndThumbprint serviceCertAndThumbprint) @@ -59,6 +65,12 @@ public static LatestClientAndPreviousServiceVersionBuilder ForServiceConnectionT } } + public LatestClientAndPreviousServiceVersionBuilder RecordingLogs(out StringWriter stringWriter) + { + loggerStringWriter = stringWriter = new(); + return this; + } + public LatestClientAndPreviousServiceVersionBuilder WithServiceVersion(Version? version) { this.version = version; @@ -149,7 +161,11 @@ async Task IClientAndServiceBuilder.Build(CancellationToken c public async Task Build(CancellationToken cancellationToken) { - var logger = new SerilogLoggerBuilder().Build().ForContext(); + var logger = loggerStringWriter == null + ? new SerilogLoggerBuilder() + .Build().ForContext() + : new LoggerConfiguration().WriteTo.TextWriter(loggerStringWriter).CreateLogger(); + CancellationTokenSource cancellationTokenSource = new(); if (version == null) { diff --git a/source/Halibut.Tests/TlsFixture.cs b/source/Halibut.Tests/TlsFixture.cs new file mode 100644 index 000000000..f6d9f0577 --- /dev/null +++ b/source/Halibut.Tests/TlsFixture.cs @@ -0,0 +1,88 @@ +// Copyright 2012-2013 Octopus Deploy Pty. Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Authentication; +using System.Threading.Tasks; +using FluentAssertions; +using Halibut.Exceptions; +using Halibut.Tests.Support; +using Halibut.Tests.Support.BackwardsCompatibility; +using Halibut.Tests.Support.TestAttributes; +using Halibut.Tests.Support.TestCases; +using Halibut.Tests.TestServices.Async; +using Halibut.Tests.Util; +using Halibut.TestUtils.Contracts; +using NUnit.Framework; + +namespace Halibut.Tests +{ + public class TlsFixture : BaseTest + { + [Test] + [LatestClientAndLatestServiceTestCases(testNetworkConditions: false)] + public async Task LatestClientAndServiceUseBestAvailableSslProtocol(ClientAndServiceTestCase clientAndServiceTestCase) + { + await using (var clientAndService = await clientAndServiceTestCase.CreateTestCaseBuilder() + .WithStandardServices() + .AsLatestClientAndLatestServiceBuilder() + .RecordingClientLogs(out var clientLogs) + .RecordingServiceLogs(out var serviceLogs) + .Build(CancellationToken)) + { + var echo = clientAndService.CreateAsyncClient(); + await echo.SayHelloAsync("World"); + + var connectionInitiatorLogs = clientAndServiceTestCase.ServiceConnectionType == ServiceConnectionType.Listening + ? clientLogs + : serviceLogs; + + // .NET does not support TLS 1.3 on Mac OS yet. + // https://github.com/dotnet/runtime/issues/1979 + var expectedSslProtocol = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) + ? SslProtocols.Tls12 + : SslProtocols.Tls13; + + connectionInitiatorLogs.Values + .SelectMany(log => log.GetLogs()) + .Should().Contain(logEvent => logEvent.FormattedMessage.Contains($"using protocol {expectedSslProtocol}")); + } + } + + [Test] + // [PreviousClientAndLatestServiceVersionsTestCases(testNetworkConditions: false)] + [LatestClientAndPreviousServiceVersionsTestCases(testNetworkConditions: false)] + public async Task PreviousClientXorServiceUsageMakeSslProtocolFallBackOnTls12(ClientAndServiceTestCase clientAndServiceTestCase) + { + await using (var clientAndService = await ((LatestClientAndPreviousServiceVersionBuilder)(clientAndServiceTestCase.CreateTestCaseBuilder() + .WithStandardServices())) + .RecordingLogs(out var stringWriter) + .Build(CancellationToken)) + { + var echo = clientAndService.CreateAsyncClient(); + await echo.SayHelloAsync("World"); + const SslProtocols expectedProtocol = SslProtocols.Tls12; + var expectedLogFragment = clientAndServiceTestCase.ServiceConnectionType == ServiceConnectionType.Listening + ? $"client connected with {expectedProtocol}" + : $"using protocol {expectedProtocol}"; + var logs = stringWriter.ToString(); + logs.Should().Contain(expectedLogFragment); + } + } + } +} \ No newline at end of file diff --git a/source/Halibut/Transport/DiscoveryClient.cs b/source/Halibut/Transport/DiscoveryClient.cs index d95494d2a..79b343adb 100644 --- a/source/Halibut/Transport/DiscoveryClient.cs +++ b/source/Halibut/Transport/DiscoveryClient.cs @@ -42,7 +42,11 @@ public async Task DiscoverAsync(ServiceEndPoint serviceEndpoint #if NETFRAMEWORK // TODO: ASYNC ME UP! // AuthenticateAsClientAsync in .NET 4.8 does not support cancellation tokens. So `cancellationToken` is not respected here. - await ssl.AuthenticateAsClientAsync(serviceEndpoint.BaseUri.Host, new X509Certificate2Collection(), SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, false); + await ssl.AuthenticateAsClientAsync( + serviceEndpoint.BaseUri.Host, + new X509Certificate2Collection(), + SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13, + false); #else await ssl.AuthenticateAsClientEnforcingTimeout(serviceEndpoint, new X509Certificate2Collection(), cancellationToken); #endif diff --git a/source/Halibut/Transport/SecureListener.cs b/source/Halibut/Transport/SecureListener.cs index 9ee69d548..2367c61d0 100644 --- a/source/Halibut/Transport/SecureListener.cs +++ b/source/Halibut/Transport/SecureListener.cs @@ -298,11 +298,17 @@ async Task ExecuteRequest(TcpClient client) { log.Write(EventType.SecurityNegotiation, "Performing TLS server handshake"); + await ssl + .AuthenticateAsServerAsync( + serverCertificate, + true, #pragma warning disable SYSLIB0039 - // See https://learn.microsoft.com/en-us/dotnet/fundamentals/syslib-diagnostics/syslib0039 - // TLS 1.0 and 1.1 are obsolete from .NET 7 - await ssl.AuthenticateAsServerAsync(serverCertificate, true, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, false).ConfigureAwait(false); + // See https://learn.microsoft.com/en-us/dotnet/fundamentals/syslib-diagnostics/syslib0039 + // TLS 1.0 and 1.1 are obsolete from .NET 7 + SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13, #pragma warning restore SYSLIB0039 + false) + .ConfigureAwait(false); log.Write(EventType.SecurityNegotiation, "Secure connection established, client is not yet authenticated, client connected with {0}", ssl.SslProtocol.ToString()); diff --git a/source/Halibut/Transport/Streams/SslStreamExtensionMethods.cs b/source/Halibut/Transport/Streams/SslStreamExtensionMethods.cs index 07dad0220..e63420b3e 100644 --- a/source/Halibut/Transport/Streams/SslStreamExtensionMethods.cs +++ b/source/Halibut/Transport/Streams/SslStreamExtensionMethods.cs @@ -26,7 +26,7 @@ internal static async Task AuthenticateAsClientEnforcingTimeout( #pragma warning disable SYSLIB0039 // See https://learn.microsoft.com/en-us/dotnet/fundamentals/syslib-diagnostics/syslib0039 // TLS 1.0 and 1.1 are obsolete from .NET 7 - EnabledSslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, + EnabledSslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13, #pragma warning restore SYSLIB0039 CertificateRevocationCheckMode = X509RevocationMode.NoCheck }; diff --git a/source/Halibut/Transport/TcpConnectionFactory.cs b/source/Halibut/Transport/TcpConnectionFactory.cs index 41512bc64..3a13913a9 100644 --- a/source/Halibut/Transport/TcpConnectionFactory.cs +++ b/source/Halibut/Transport/TcpConnectionFactory.cs @@ -47,7 +47,11 @@ public async Task EstablishNewConnectionAsync(ExchangeProtocolBuild #if NETFRAMEWORK // TODO: ASYNC ME UP! // AuthenticateAsClientAsync in .NET 4.8 does not support cancellation tokens. So `cancellationToken` is not respected here. - await ssl.AuthenticateAsClientAsync(serviceEndpoint.BaseUri.Host, new X509Certificate2Collection(clientCertificate), SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, false); + await ssl.AuthenticateAsClientAsync( + serviceEndpoint.BaseUri.Host, + new X509Certificate2Collection(clientCertificate), + SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13, + false); #else await ssl.AuthenticateAsClientEnforcingTimeout(serviceEndpoint, new X509Certificate2Collection(clientCertificate), cancellationToken); #endif diff --git a/source/Octopus.TestPortForwarder/Octopus.TestPortForwarder.csproj b/source/Octopus.TestPortForwarder/Octopus.TestPortForwarder.csproj index b9a5fa5f6..405b6263a 100644 --- a/source/Octopus.TestPortForwarder/Octopus.TestPortForwarder.csproj +++ b/source/Octopus.TestPortForwarder/Octopus.TestPortForwarder.csproj @@ -14,7 +14,7 @@ net8.0 - +