C# 身份验证失败,因为远程方已关闭传输流。托管在 Azure Function 中的应用

C# Authentication failed because the remote party has closed the transport stream. App hosted in Azure Function

我在生产中遇到了这个奇怪的问题。向 APIM 发出远程 https 请求时,抛出以下错误:

The SSL connection could not be established,  see inner exception.
Inner exception: Authentication failed because the remote party has closed the transport stream.

{"assembly":"System.Net.Security, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a","method":"System.Net.Security.SslStream.StartReadFrame","level":0,"line":0}
{"assembly":"System.Net.Security, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a","method":"System.Net.Security.SslStream.PartialFrameCallback","level":1,"line":0}
{"assembly":"System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e","method":"System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw","level":2,"line":0}
{"assembly":"System.Net.Security, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a","method":"System.Net.Security.SslStream.ThrowIfExceptional","level":3,"line":0}
{"assembly":"System.Net.Security, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a","method":"System.Net.Security.SslStream.InternalEndProcessAuthentication","level":4,"line":0}
{"assembly":"System.Net.Security, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a","method":"System.Net.Security.SslStream.EndProcessAuthentication","level":5,"line":0}
{"assembly":"System.Net.Security, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a","method":"System.Net.Security.SslStream.EndAuthenticateAsClient","level":6,"line":0}
{"assembly":"System.Net.Security, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a","method":"System.Net.Security.SslStream+<>c.<AuthenticateAsClientAsync>b__65_1","level":7,"line":0}
{"assembly":"System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e","method":"System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic","level":8,"line":0}
{"assembly":"System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e","method":"System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw","level":9,"line":0}
{"assembly":"System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e","method":"System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess","level":10,"line":0}
{"assembly":"System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e","method":"System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification","level":11,"line":0}
{"assembly":"System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e","method":"System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult","level":12,"line":0}
{"assembly":"System.Net.Http, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a","method":"System.Net.Http.ConnectHelper+<EstablishSslConnectionAsyncCore>d__4.MoveNext","level":13,"line":0}

我在代码中将安全协议设置为tls1.2:

ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;

当我从生产中点击 https://www.howsmyssl.com/a/check 时,我得到以下响应:

{
  "given_cipher_suites": [
    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
    "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
    "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
    "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
    "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
    "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
    "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
    "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
    "TLS_RSA_WITH_AES_256_GCM_SHA384",
    "TLS_RSA_WITH_AES_128_GCM_SHA256",
    "TLS_RSA_WITH_AES_256_CBC_SHA256",
    "TLS_RSA_WITH_AES_128_CBC_SHA256",
    "TLS_RSA_WITH_AES_256_CBC_SHA",
    "TLS_RSA_WITH_AES_128_CBC_SHA",
    "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
    "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
    "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
    "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
    "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
    "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
    "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
    "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"
  ],
  "ephemeral_keys_supported": true,
  "session_ticket_supported": true,
  "tls_compression_supported": false,
  "unknown_cipher_suite_supported": false,
  "beast_vuln": false,
  "able_to_detect_n_minus_one_splitting": false,
  "insecure_cipher_suites": {
    "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA": [ "uses 3DES which is vulnerable to the Sweet32 attack but was not configured as a fallback in the ciphersuite order" ],
    "TLS_RSA_WITH_3DES_EDE_CBC_SHA": [ "uses 3DES which is vulnerable to the Sweet32 attack but was not configured as a fallback in the ciphersuite order" ]
  },
  "tls_version": "TLS 1.2",
  "rating": "Bad"
}

相同的功能在 DEV 和 QA 环境中正常工作。所有环境都是使用相同的管道创建的(arm 模板和代码)

问题的根本原因是在私有 DNS 区域中配置了错误的 IP 地址。我们有两个私有 DNS 区域来解析 APIM 的自定义域名,一个指向 QA 的 APIM,另一个指向 PROD 的 APIM。我们一开始就不应该有两个私有 DNS 区域,错误的 IP 地址是一个可怕的错误:(。经验教训