From 8bd0766c7f4e8486155e3287127590500ebfc42e Mon Sep 17 00:00:00 2001 From: Rhys Parry Date: Mon, 8 Sep 2025 10:26:08 +1000 Subject: [PATCH 1/5] Add observer for secure connection info --- .../Transport/SecureClientFixture.cs | 2 +- .../Transport/SecureListenerFixture.cs | 3 ++- source/Halibut/HalibutRuntime.cs | 12 ++++++--- source/Halibut/HalibutRuntimeBuilder.cs | 12 ++++++++- .../Observability/ConnectionDirection.cs | 22 ++++++++++++++++ .../ISecureConnectionObserver.cs | 23 +++++++++++++++++ .../NoOpSecureConnectionObserver.cs | 25 +++++++++++++++++++ .../Observability/SecureConnectionInfo.cs | 24 ++++++++++++++++++ source/Halibut/Transport/SecureListener.cs | 13 +++++++++- .../Halibut/Transport/TcpConnectionFactory.cs | 17 ++++++++++++- 10 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 source/Halibut/Transport/Observability/ConnectionDirection.cs create mode 100644 source/Halibut/Transport/Observability/ISecureConnectionObserver.cs create mode 100644 source/Halibut/Transport/Observability/NoOpSecureConnectionObserver.cs create mode 100644 source/Halibut/Transport/Observability/SecureConnectionInfo.cs diff --git a/source/Halibut.Tests/Transport/SecureClientFixture.cs b/source/Halibut.Tests/Transport/SecureClientFixture.cs index 3a8a83673..80f7e8bed 100644 --- a/source/Halibut.Tests/Transport/SecureClientFixture.cs +++ b/source/Halibut.Tests/Transport/SecureClientFixture.cs @@ -80,7 +80,7 @@ public async Task SecureClientClearsPoolWhenAllConnectionsCorrupt() Params = new object[] { "Fred" } }; - var tcpConnectionFactory = new TcpConnectionFactory(Certificates.Octopus, halibutTimeoutsAndLimits, new StreamFactory()); + var tcpConnectionFactory = new TcpConnectionFactory(Certificates.Octopus, halibutTimeoutsAndLimits, new StreamFactory(), NoOpSecureConnectionObserver.Instance); var secureClient = new SecureListeningClient(GetProtocol, endpoint, Certificates.Octopus, log, connectionManager, tcpConnectionFactory); ResponseMessage response = null!; diff --git a/source/Halibut.Tests/Transport/SecureListenerFixture.cs b/source/Halibut.Tests/Transport/SecureListenerFixture.cs index 0914aad3b..69775b77e 100644 --- a/source/Halibut.Tests/Transport/SecureListenerFixture.cs +++ b/source/Halibut.Tests/Transport/SecureListenerFixture.cs @@ -73,7 +73,8 @@ public async Task SecureListenerDoesNotCreateHundredsOfIoEventsPerSecondOnWindow (_, _) => UnauthorizedClientConnectResponse.BlockConnection, timeoutsAndLimits, new StreamFactory(), - NoOpConnectionsObserver.Instance + NoOpConnectionsObserver.Instance, + NoOpSecureConnectionObserver.Instance ); var idleAverage = CollectCounterValues(opsPerSec) diff --git a/source/Halibut/HalibutRuntime.cs b/source/Halibut/HalibutRuntime.cs index 93d005c90..368899c1b 100644 --- a/source/Halibut/HalibutRuntime.cs +++ b/source/Halibut/HalibutRuntime.cs @@ -43,6 +43,7 @@ public class HalibutRuntime : IHalibutRuntime readonly IRpcObserver rpcObserver; readonly TcpConnectionFactory tcpConnectionFactory; readonly IConnectionsObserver connectionsObserver; + readonly ISecureConnectionObserver secureConnectionObserver; readonly IActiveTcpConnectionsLimiter activeTcpConnectionsLimiter; readonly IControlMessageObserver controlMessageObserver; @@ -59,7 +60,9 @@ internal HalibutRuntime( IStreamFactory streamFactory, IRpcObserver rpcObserver, IConnectionsObserver connectionsObserver, - IControlMessageObserver controlMessageObserver) + IControlMessageObserver controlMessageObserver, + ISecureConnectionObserver secureConnectionObserver + ) { this.serverCertificate = serverCertificate; this.trustProvider = trustProvider; @@ -73,10 +76,11 @@ internal HalibutRuntime( invoker = new ServiceInvoker(serviceFactory); TimeoutsAndLimits = halibutTimeoutsAndLimits; this.connectionsObserver = connectionsObserver; + this.secureConnectionObserver = secureConnectionObserver; this.controlMessageObserver = controlMessageObserver; connectionManager = new ConnectionManagerAsync(); - this.tcpConnectionFactory = new TcpConnectionFactory(serverCertificate, TimeoutsAndLimits, streamFactory); + this.tcpConnectionFactory = new TcpConnectionFactory(serverCertificate, TimeoutsAndLimits, streamFactory, secureConnectionObserver); activeTcpConnectionsLimiter = new ActiveTcpConnectionsLimiter(TimeoutsAndLimits); } @@ -130,7 +134,9 @@ public int Listen(IPEndPoint endpoint) HandleUnauthorizedClientConnect, TimeoutsAndLimits, streamFactory, - connectionsObserver); + connectionsObserver, + secureConnectionObserver + ); listeners.DoWithExclusiveAccess(l => { diff --git a/source/Halibut/HalibutRuntimeBuilder.cs b/source/Halibut/HalibutRuntimeBuilder.cs index 287fcab96..7869d38f2 100644 --- a/source/Halibut/HalibutRuntimeBuilder.cs +++ b/source/Halibut/HalibutRuntimeBuilder.cs @@ -28,6 +28,7 @@ public class HalibutRuntimeBuilder IStreamFactory? streamFactory; IRpcObserver? rpcObserver; IConnectionsObserver? connectionsObserver; + ISecureConnectionObserver? secureConnectionObserver; IControlMessageObserver? controlMessageObserver; MessageStreamWrappers queueMessageStreamWrappers = new(); @@ -43,6 +44,12 @@ public HalibutRuntimeBuilder WithConnectionsObserver(IConnectionsObserver connec return this; } + public HalibutRuntimeBuilder WithSecureConnectionObserver(ISecureConnectionObserver secureConnectionsObserver) + { + this.secureConnectionObserver = secureConnectionsObserver; + return this; + } + internal HalibutRuntimeBuilder WithStreamFactory(IStreamFactory streamFactory) { this.streamFactory = streamFactory; @@ -175,6 +182,7 @@ public HalibutRuntime Build() var streamFactory = this.streamFactory ?? new StreamFactory(); var connectionsObserver = this.connectionsObserver ?? NoOpConnectionsObserver.Instance; + var secureConnectionObserver = this.secureConnectionObserver ?? NoOpSecureConnectionObserver.Instance; var rpcObserver = this.rpcObserver ?? new NoRpcObserver(); var controlMessageObserver = this.controlMessageObserver ?? new NoOpControlMessageObserver(); @@ -191,7 +199,9 @@ public HalibutRuntime Build() streamFactory, rpcObserver, connectionsObserver, - controlMessageObserver); + controlMessageObserver, + secureConnectionObserver + ); if (onUnauthorizedClientConnect is not null) { diff --git a/source/Halibut/Transport/Observability/ConnectionDirection.cs b/source/Halibut/Transport/Observability/ConnectionDirection.cs new file mode 100644 index 000000000..7254e20ee --- /dev/null +++ b/source/Halibut/Transport/Observability/ConnectionDirection.cs @@ -0,0 +1,22 @@ +// 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. + +namespace Halibut.Transport.Observability +{ + public enum ConnectionDirection + { + Incoming, + Outgoing + } +} \ No newline at end of file diff --git a/source/Halibut/Transport/Observability/ISecureConnectionObserver.cs b/source/Halibut/Transport/Observability/ISecureConnectionObserver.cs new file mode 100644 index 000000000..394a3a169 --- /dev/null +++ b/source/Halibut/Transport/Observability/ISecureConnectionObserver.cs @@ -0,0 +1,23 @@ +// 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.Security.Authentication; + +namespace Halibut.Transport.Observability +{ + public interface ISecureConnectionObserver + { + public void SecureConnectionEstablished(SecureConnectionInfo secureConnectionInfo); + } +} \ No newline at end of file diff --git a/source/Halibut/Transport/Observability/NoOpSecureConnectionObserver.cs b/source/Halibut/Transport/Observability/NoOpSecureConnectionObserver.cs new file mode 100644 index 000000000..90fb6a33c --- /dev/null +++ b/source/Halibut/Transport/Observability/NoOpSecureConnectionObserver.cs @@ -0,0 +1,25 @@ +// 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. + +namespace Halibut.Transport.Observability +{ + public class NoOpSecureConnectionObserver : ISecureConnectionObserver + { + static NoOpSecureConnectionObserver? singleInstance; + public static NoOpSecureConnectionObserver Instance => singleInstance ??= new NoOpSecureConnectionObserver(); + public void SecureConnectionEstablished(SecureConnectionInfo secureConnectionInfo) + { + } + } +} \ No newline at end of file diff --git a/source/Halibut/Transport/Observability/SecureConnectionInfo.cs b/source/Halibut/Transport/Observability/SecureConnectionInfo.cs new file mode 100644 index 000000000..17f227419 --- /dev/null +++ b/source/Halibut/Transport/Observability/SecureConnectionInfo.cs @@ -0,0 +1,24 @@ +// 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.Security.Authentication; + +namespace Halibut.Transport.Observability +{ + public struct SecureConnectionInfo + { + public SslProtocols SslProtocols { get; init; } + public ConnectionDirection ConnectionDirection { get; init; } + } +} \ No newline at end of file diff --git a/source/Halibut/Transport/SecureListener.cs b/source/Halibut/Transport/SecureListener.cs index 59f666f84..fb12da3a7 100644 --- a/source/Halibut/Transport/SecureListener.cs +++ b/source/Halibut/Transport/SecureListener.cs @@ -48,6 +48,7 @@ public class SecureListener : IAsyncDisposable readonly HalibutTimeoutsAndLimits halibutTimeoutsAndLimits; readonly IStreamFactory streamFactory; readonly IConnectionsObserver connectionsObserver; + readonly ISecureConnectionObserver secureConnectionObserver; ILog log; TcpListener listener; Thread? backgroundThread; @@ -67,7 +68,9 @@ public SecureListener( Func unauthorizedClientConnect, HalibutTimeoutsAndLimits halibutTimeoutsAndLimits, IStreamFactory streamFactory, - IConnectionsObserver connectionsObserver) + IConnectionsObserver connectionsObserver, + ISecureConnectionObserver secureConnectionObserver + ) { this.endPoint = endPoint; this.serverCertificate = serverCertificate; @@ -81,6 +84,7 @@ public SecureListener( this.halibutTimeoutsAndLimits = halibutTimeoutsAndLimits; this.streamFactory = streamFactory; this.connectionsObserver = connectionsObserver; + this.secureConnectionObserver = secureConnectionObserver; this.cts = new CancellationTokenSource(); this.cancellationToken = cts.Token; @@ -336,6 +340,13 @@ await ssl { connectionAuthorizedAndObserved = true; connectionsObserver.ConnectionAccepted(true); + secureConnectionObserver.SecureConnectionEstablished( + new SecureConnectionInfo + { + ConnectionDirection = ConnectionDirection.Incoming, + SslProtocols = ssl.SslProtocol + } + ); tcpClientManager.AddActiveClient(thumbprint, client); errorEventType = EventType.Error; await ExchangeMessages(ssl).ConfigureAwait(false); diff --git a/source/Halibut/Transport/TcpConnectionFactory.cs b/source/Halibut/Transport/TcpConnectionFactory.cs index 2931297a5..d9d44b199 100644 --- a/source/Halibut/Transport/TcpConnectionFactory.cs +++ b/source/Halibut/Transport/TcpConnectionFactory.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Halibut.Diagnostics; +using Halibut.Transport.Observability; using Halibut.Transport.Protocol; using Halibut.Transport.Proxy; using Halibut.Transport.Streams; @@ -20,12 +21,19 @@ public class TcpConnectionFactory : IConnectionFactory readonly X509Certificate2 clientCertificate; readonly HalibutTimeoutsAndLimits halibutTimeoutsAndLimits; readonly IStreamFactory streamFactory; + readonly ISecureConnectionObserver secureConnectionObserver; - public TcpConnectionFactory(X509Certificate2 clientCertificate, HalibutTimeoutsAndLimits halibutTimeoutsAndLimits, IStreamFactory streamFactory) + public TcpConnectionFactory( + X509Certificate2 clientCertificate, + HalibutTimeoutsAndLimits halibutTimeoutsAndLimits, + IStreamFactory streamFactory, + ISecureConnectionObserver secureConnectionObserver + ) { this.clientCertificate = clientCertificate; this.halibutTimeoutsAndLimits = halibutTimeoutsAndLimits; this.streamFactory = streamFactory; + this.secureConnectionObserver = secureConnectionObserver; } public async Task EstablishNewConnectionAsync(ExchangeProtocolBuilder exchangeProtocolBuilder, ServiceEndPoint serviceEndpoint, ILog log, CancellationToken cancellationToken) @@ -60,6 +68,13 @@ await ssl.AuthenticateAsClientAsync( await ssl.FlushAsync(cancellationToken); log.Write(EventType.Security, "Secure connection established. Server at {0} identified by thumbprint: {1}, using protocol {2}", client.Client.RemoteEndPoint, serviceEndpoint.RemoteThumbprint, ssl.SslProtocol.ToString()); + secureConnectionObserver.SecureConnectionEstablished( + new SecureConnectionInfo + { + ConnectionDirection = ConnectionDirection.Outgoing, + SslProtocols = ssl.SslProtocol + } + ); return new SecureConnection(client, ssl, exchangeProtocolBuilder, halibutTimeoutsAndLimits, log); } From 07cb62196e9da55815c00fed4def79587d930e1c Mon Sep 17 00:00:00 2001 From: Rhys Parry Date: Mon, 8 Sep 2025 10:49:27 +1000 Subject: [PATCH 2/5] Don't use init-only properties --- .../Observability/SecureConnectionInfo.cs | 16 ++++++++++++++-- source/Halibut/Transport/SecureListener.cs | 8 +------- source/Halibut/Transport/TcpConnectionFactory.cs | 8 +------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/source/Halibut/Transport/Observability/SecureConnectionInfo.cs b/source/Halibut/Transport/Observability/SecureConnectionInfo.cs index 17f227419..16ea31cbe 100644 --- a/source/Halibut/Transport/Observability/SecureConnectionInfo.cs +++ b/source/Halibut/Transport/Observability/SecureConnectionInfo.cs @@ -18,7 +18,19 @@ namespace Halibut.Transport.Observability { public struct SecureConnectionInfo { - public SslProtocols SslProtocols { get; init; } - public ConnectionDirection ConnectionDirection { get; init; } + SecureConnectionInfo( + SslProtocols sslProtocols, + ConnectionDirection connectionDirection + ) + { + SslProtocols = sslProtocols; + ConnectionDirection = connectionDirection; + } + + public SslProtocols SslProtocols { get; } + public ConnectionDirection ConnectionDirection { get; } + + public static SecureConnectionInfo CreateIncoming(SslProtocols sslProtocols) => new(sslProtocols, ConnectionDirection.Incoming); + public static SecureConnectionInfo CreateOutgoing(SslProtocols sslProtocols) => new(sslProtocols, ConnectionDirection.Outgoing); } } \ No newline at end of file diff --git a/source/Halibut/Transport/SecureListener.cs b/source/Halibut/Transport/SecureListener.cs index fb12da3a7..4ec522973 100644 --- a/source/Halibut/Transport/SecureListener.cs +++ b/source/Halibut/Transport/SecureListener.cs @@ -340,13 +340,7 @@ await ssl { connectionAuthorizedAndObserved = true; connectionsObserver.ConnectionAccepted(true); - secureConnectionObserver.SecureConnectionEstablished( - new SecureConnectionInfo - { - ConnectionDirection = ConnectionDirection.Incoming, - SslProtocols = ssl.SslProtocol - } - ); + secureConnectionObserver.SecureConnectionEstablished(SecureConnectionInfo.CreateIncoming(ssl.SslProtocol)); tcpClientManager.AddActiveClient(thumbprint, client); errorEventType = EventType.Error; await ExchangeMessages(ssl).ConfigureAwait(false); diff --git a/source/Halibut/Transport/TcpConnectionFactory.cs b/source/Halibut/Transport/TcpConnectionFactory.cs index d9d44b199..7a36a4d65 100644 --- a/source/Halibut/Transport/TcpConnectionFactory.cs +++ b/source/Halibut/Transport/TcpConnectionFactory.cs @@ -68,13 +68,7 @@ await ssl.AuthenticateAsClientAsync( await ssl.FlushAsync(cancellationToken); log.Write(EventType.Security, "Secure connection established. Server at {0} identified by thumbprint: {1}, using protocol {2}", client.Client.RemoteEndPoint, serviceEndpoint.RemoteThumbprint, ssl.SslProtocol.ToString()); - secureConnectionObserver.SecureConnectionEstablished( - new SecureConnectionInfo - { - ConnectionDirection = ConnectionDirection.Outgoing, - SslProtocols = ssl.SslProtocol - } - ); + secureConnectionObserver.SecureConnectionEstablished(SecureConnectionInfo.CreateOutgoing(ssl.SslProtocol)); return new SecureConnection(client, ssl, exchangeProtocolBuilder, halibutTimeoutsAndLimits, log); } From 9373ae1fd8c4b8503f932fc645a7d943e861f9cd Mon Sep 17 00:00:00 2001 From: Rhys Parry Date: Mon, 8 Sep 2025 13:56:32 +1000 Subject: [PATCH 3/5] Include thumbrint in connection info --- .../Observability/SecureConnectionInfo.cs | 22 ++++++++++++++++--- source/Halibut/Transport/SecureListener.cs | 2 +- .../Halibut/Transport/TcpConnectionFactory.cs | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/source/Halibut/Transport/Observability/SecureConnectionInfo.cs b/source/Halibut/Transport/Observability/SecureConnectionInfo.cs index 16ea31cbe..c1b323725 100644 --- a/source/Halibut/Transport/Observability/SecureConnectionInfo.cs +++ b/source/Halibut/Transport/Observability/SecureConnectionInfo.cs @@ -20,17 +20,33 @@ public struct SecureConnectionInfo { SecureConnectionInfo( SslProtocols sslProtocols, - ConnectionDirection connectionDirection + ConnectionDirection connectionDirection, + string thumbprint ) { SslProtocols = sslProtocols; ConnectionDirection = connectionDirection; + Thumbprint = thumbprint; } public SslProtocols SslProtocols { get; } public ConnectionDirection ConnectionDirection { get; } + public string Thumbprint { get; } - public static SecureConnectionInfo CreateIncoming(SslProtocols sslProtocols) => new(sslProtocols, ConnectionDirection.Incoming); - public static SecureConnectionInfo CreateOutgoing(SslProtocols sslProtocols) => new(sslProtocols, ConnectionDirection.Outgoing); + public static SecureConnectionInfo CreateIncoming( + SslProtocols sslProtocols, + string thumbprint + ) + { + return new SecureConnectionInfo(sslProtocols, ConnectionDirection.Incoming, thumbprint); + } + + public static SecureConnectionInfo CreateOutgoing( + SslProtocols sslProtocols, + string thumbprint + ) + { + return new(sslProtocols, ConnectionDirection.Outgoing, thumbprint); + } } } \ No newline at end of file diff --git a/source/Halibut/Transport/SecureListener.cs b/source/Halibut/Transport/SecureListener.cs index 4ec522973..5132e1a4e 100644 --- a/source/Halibut/Transport/SecureListener.cs +++ b/source/Halibut/Transport/SecureListener.cs @@ -340,7 +340,7 @@ await ssl { connectionAuthorizedAndObserved = true; connectionsObserver.ConnectionAccepted(true); - secureConnectionObserver.SecureConnectionEstablished(SecureConnectionInfo.CreateIncoming(ssl.SslProtocol)); + secureConnectionObserver.SecureConnectionEstablished(SecureConnectionInfo.CreateIncoming(ssl.SslProtocol, thumbprint)); tcpClientManager.AddActiveClient(thumbprint, client); errorEventType = EventType.Error; await ExchangeMessages(ssl).ConfigureAwait(false); diff --git a/source/Halibut/Transport/TcpConnectionFactory.cs b/source/Halibut/Transport/TcpConnectionFactory.cs index 7a36a4d65..ac637cd9b 100644 --- a/source/Halibut/Transport/TcpConnectionFactory.cs +++ b/source/Halibut/Transport/TcpConnectionFactory.cs @@ -68,7 +68,7 @@ await ssl.AuthenticateAsClientAsync( await ssl.FlushAsync(cancellationToken); log.Write(EventType.Security, "Secure connection established. Server at {0} identified by thumbprint: {1}, using protocol {2}", client.Client.RemoteEndPoint, serviceEndpoint.RemoteThumbprint, ssl.SslProtocol.ToString()); - secureConnectionObserver.SecureConnectionEstablished(SecureConnectionInfo.CreateOutgoing(ssl.SslProtocol)); + secureConnectionObserver.SecureConnectionEstablished(SecureConnectionInfo.CreateOutgoing(ssl.SslProtocol, serviceEndpoint.RemoteThumbprint ?? "Unknown")); return new SecureConnection(client, ssl, exchangeProtocolBuilder, halibutTimeoutsAndLimits, log); } From 65ab5ac178492b012573ff30690ac9d1bf10c4a1 Mon Sep 17 00:00:00 2001 From: Rhys Parry Date: Mon, 8 Sep 2025 14:36:31 +1000 Subject: [PATCH 4/5] Configure supported SSL via env variable --- source/Halibut/Transport/SslConfiguration.cs | 33 +++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/source/Halibut/Transport/SslConfiguration.cs b/source/Halibut/Transport/SslConfiguration.cs index d4e066df6..5ee333314 100644 --- a/source/Halibut/Transport/SslConfiguration.cs +++ b/source/Halibut/Transport/SslConfiguration.cs @@ -1,16 +1,41 @@ +using System; using System.Security.Authentication; namespace Halibut.Transport { public static class SslConfiguration { - public static SslProtocols SupportedProtocols + static SslProtocols GetSupportedProtocols() { + var supportedProtocolMode = Environment.GetEnvironmentVariable("HALIBUT_SUPPORTED_SSL_PROTOCOLS")?.ToLowerInvariant(); + + if (supportedProtocolMode == "legacy") + { #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 - get => SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13; + // See https://learn.microsoft.com/en-us/dotnet/fundamentals/syslib-diagnostics/syslib0039 + // TLS 1.0 and 1.1 are obsolete from .NET 7 + return SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13; #pragma warning restore SYSLIB0039 + } + + if (supportedProtocolMode == "system") + { + return SslProtocols.None; + } + + if (supportedProtocolMode == "modern") + { + return SslProtocols.Tls12 | SslProtocols.Tls13; + } + + if (supportedProtocolMode == "tls1.3") + { + return SslProtocols.Tls13; + } + + return SslProtocols.None; } + + public static SslProtocols SupportedProtocols { get; } = GetSupportedProtocols(); } } \ No newline at end of file From fabce92bb260276062c1e2f4ff46f65fc1b900e3 Mon Sep 17 00:00:00 2001 From: Rhys Parry Date: Mon, 8 Sep 2025 15:21:46 +1000 Subject: [PATCH 5/5] Always use legacy mode on Net48 --- source/Halibut/Transport/SslConfiguration.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/Halibut/Transport/SslConfiguration.cs b/source/Halibut/Transport/SslConfiguration.cs index 5ee333314..28259c759 100644 --- a/source/Halibut/Transport/SslConfiguration.cs +++ b/source/Halibut/Transport/SslConfiguration.cs @@ -7,7 +7,13 @@ public static class SslConfiguration { static SslProtocols GetSupportedProtocols() { +#if NETFRAMEWORK + // Net48 tests has issues establishing a common algorithm when we allow system default + var supportedProtocolMode = "legacy"; +#else var supportedProtocolMode = Environment.GetEnvironmentVariable("HALIBUT_SUPPORTED_SSL_PROTOCOLS")?.ToLowerInvariant(); +#endif + if (supportedProtocolMode == "legacy") {