首页 > 解决方案 > SslStream 忽略 Linux 上的 SslProtocols - 始终使用 TLS v1

问题描述

我有一个在弹性beantalk(MongoDB驱动程序2.12.4)上的Linux上运行的asp net core 5.0应用程序。

MongoDb 是 4.0 版本,并且具有 TLS 2.1 及更高版本的最低 TLS 协议版本。

该应用程序在 AMI“在 64 位 Amazon Linux 2 上运行的 .NET Core”v2.1.5 上运行良好,但是,在更新到 v2.2.1 后,应用程序无法连接到 MongoDB,并返回以下错误。

A timeout occurred after 3000ms selecting a server using CompositeServerSelector{ Selectors = MongoDB.Driver.MongoClient+AreSessionsSupportedServerSelector, LatencyLimitingServerSelector{ AllowedLatencyRange = 00:00:00.0150000 } }. Client view of cluster state is { ClusterId : "1", ConnectionMode : "ReplicaSet", Type : "ReplicaSet", State : "Disconnected", Servers : [{ ServerId: "{ ClusterId : 1, EndPoint : "xxxxxx/xxxxxxxx-shard-00-00.xxxxx.mongodb.net:27017" }", EndPoint: "Unspecified/connectionissue-shard-00-00.ybc5n.mongodb.net:27017", ReasonChanged: "Heartbeat", State: "Disconnected", ServerVersion: , TopologyVersion: , Type: "Unknown", HeartbeatException: "MongoDB.Driver.MongoConnectionException: An exception occurred while opening a connection to the server.
 ---> System.IO.IOException:  Received an unexpected EOF or 0 bytes from the transport stream.
   at System.Net.Security.SslStream.<FillHandshakeBufferAsync>g__InternalFillHandshakeBufferAsync|182_0[TIOAdapter](TIOAdapter adap, ValueTask`1 task, Int32 minSize)
   at System.Net.Security.SslStream.ReceiveBlobAsync[TIOAdapter](TIOAdapter adapter)
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
   at MongoDB.Driver.Core.Connections.SslStreamFactory.CreateStreamAsync(EndPoint endPoint, CancellationToken cancellationToken)
   at MongoDB.Driver.Core.Connections.BinaryConnection.OpenHelperAsync(CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at MongoDB.Driver.Core.Connections.BinaryConnection.OpenHelperAsync(CancellationToken cancellationToken)
   at MongoDB.Driver.Core.Servers.ServerMonitor.InitializeConnectionAsync(CancellationToken cancellationToken)
   at MongoDB.Driver.Core.Servers.ServerMonitor.HeartbeatAsync(CancellationToken cancellationToken)", LastHeartbeatTimestamp: "2021-06-29T09:13:06.9903249Z", LastUpdateTimestamp: "2021-06-29T09:13:06.9903259Z" }, { ServerId: "{ ClusterId : 1, EndPoint : "xxxx/xxxx-shard-00-01.xxxx.mongodb.net:27017" }", EndPoint: "Unspecified/connectionissue-shard-00-01.ybc5n.mongodb.net:27017", ReasonChanged: "Heartbeat", State: "Disconnected", ServerVersion: , TopologyVersion: , Type: "Unknown", HeartbeatException: "MongoDB.Driver.MongoConnectionException: An exception occurred while opening a connection to the server.
 ---> System.IO.IOException:  Received an unexpected EOF or 0 bytes from the transport stream.
   at System.Net.Security.SslStream.<FillHandshakeBufferAsync>g__InternalFillHandshakeBufferAsync|182_0[TIOAdapter](TIOAdapter adap, ValueTask`1 task, Int32 minSize)
   at System.Net.Security.SslStream.ReceiveBlobAsync[TIOAdapter](TIOAdapter adapter)
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
   at MongoDB.Driver.Core.Connections.SslStreamFactory.CreateStreamAsync(EndPoint endPoint, CancellationToken cancellationToken)
   at MongoDB.Driver.Core.Connections.BinaryConnection.OpenHelperAsync(CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at MongoDB.Driver.Core.Connections.BinaryConnection.OpenHelperAsync(CancellationToken cancellationToken)
   at MongoDB.Driver.Core.Servers.ServerMonitor.InitializeConnectionAsync(CancellationToken cancellationToken)
   at MongoDB.Driver.Core.Servers.ServerMonitor.HeartbeatAsync(CancellationToken cancellationToken)", LastHeartbeatTimestamp: "2021-06-29T09:13:07.0047845Z", LastUpdateTimestamp: "2021-06-29T09:13:07.0047854Z" }, { ServerId: "{ ClusterId : 1, EndPoint : "xxxx/xxxx-shard-00-02.xxxx.mongodb.net:27017" }", EndPoint: "xxxx/xxxx-shard-00-02.xxxx.mongodb.net:27017", ReasonChanged: "Heartbeat", State: "Disconnected", ServerVersion: , TopologyVersion: , Type: "Unknown", HeartbeatException: "MongoDB.Driver.MongoConnectionException: An exception occurred while opening a connection to the server.
 ---> System.IO.IOException:  Received an unexpected EOF or 0 bytes from the transport stream.
   at System.Net.Security.SslStream.<FillHandshakeBufferAsync>g__InternalFillHandshakeBufferAsync|182_0[TIOAdapter](TIOAdapter adap, ValueTask`1 task, Int32 minSize)
   at System.Net.Security.SslStream.ReceiveBlobAsync[TIOAdapter](TIOAdapter adapter)
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
   at MongoDB.Driver.Core.Connections.SslStreamFactory.CreateStreamAsync(EndPoint endPoint, CancellationToken cancellationToken)
   at MongoDB.Driver.Core.Connections.BinaryConnection.OpenHelperAsync(CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at MongoDB.Driver.Core.Connections.BinaryConnection.OpenHelperAsync(CancellationToken cancellationToken)
   at MongoDB.Driver.Core.Servers.ServerMonitor.InitializeConnectionAsync(CancellationToken cancellationToken)
   at MongoDB.Driver.Core.Servers.ServerMonitor.HeartbeatAsync(CancellationToken cancellationToken)", LastHeartbeatTimestamp: "2021-06-29T09:13:07.0124176Z", LastUpdateTimestamp: "2021-06-29T09:13:07.0124185Z" }] }.

为了进行测试,我创建了一个简单的测试工具并将其部署到一个新的 beanstalk 应用程序中,并使用两个不同的 AMI 创建了 2 个环境。这正如预期的那样复制了这个问题。

旧图像上的 .NET 版本是5.0.55.0.7的。我在5.0.65.0.7的发行说明中没有发现任何暗示任何破坏行为的内容。查看 GitHub,这两个版本之间的 SslStream 没有区别 - 5.0.5 5.0.7

运行数据包捕获显示旧图像使用 TLSv1.2,它按预期工作,旧版本使用 TLSv1,它被数据库拒绝(如您所料)。

在职的

破碎的

两台服务器上的 OpenSSL 版本相同(1.0.2-fips),openssl.cnf 也相同。

两者的输出都没有差异printenv

新的 AMI 映像似乎与Microsoft 的此文档相反

在使用 curl 的环境和使用 HttpClient 的代码中,发出请求正常的 Web 请求都使用 TLS 1.2。

我已经向亚马逊提出了这个问题,他们认为这是 mongo 驱动程序或网络核心的代码问题。

我找不到任何方法来明确强制 mongo 驱动程序使用特定的 TLS 密码。

任何想法将不胜感激。


编辑


在查看了 Mongo 驱动程序中的代码后,我简化了他们的代码并在没有驱动程序的情况下重新创建了问题。

我创建了这个测试方法来重现问题并将 SslProtocols 显式设置为 TLSv1.2。

    private string SslTest(string hostname, int port)
        {
            var ip = Dns.GetHostAddresses(hostname);

            var ipEndPoint = new IPEndPoint(ip.First(), port);
            
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

            socket.Connect(ipEndPoint);

            using var stream = new NetworkStream(socket, true) {ReadTimeout = 3000, WriteTimeout = 3000};
            using var sslStream = new SslStream(stream);
            
            var targetHost = hostname;
            var clientCertificates = new X509CertificateCollection();

            try
            {
                sslStream.AuthenticateAsClient(targetHost, clientCertificates, SslProtocols.Tls12, false);
            }
            catch (Exception e)
            {
                return e.ToString();
            }
            finally
            {
                sslStream.Close();
            }
            
            return $"stream opened and closed successfully to {hostname}:{port}";
        }   

同样,使用旧 AMI 可以正常工作,使用新 AMI,它会引发相同的错误。数据包捕获显示相同的结果。

查看框架版本中的代码差异,System.Net.Security 命名空间在 5.0.5 和 5.0.7 之间没有变化。

奇怪的是,使用 google.com 和端口 443 运行此方法适用于新的 AMI。

我觉得 AMI 中发生了一些非常小的变化,但我不知道如何找出是什么。在 linux 上远程调试核心框架不是我知道该怎么做的事情。

标签: mongodb.net-coreamazon-elastic-beanstalktls1.2mongodb-.net-driver

解决方案


由于包括 C# 在内的 MongoDB 驱动程序是开源的,因此您应该能够在驱动程序中放置断点或打印语句,以查看它是否明确选择了某些 TLS 版本(除非您将其配置为这样)。

假设驱动程序没有这样做,那么问题就出在图像中。尝试openssl s_client从该映像连接到您的 MongoDB 服务器。


推荐阅读