为什么 C# Google.Cloud.Firestore 库无法连接到企业 MITM 防火墙?

Why does a C# Google.Cloud.Firestore library fail to connect with an enterprise MITM firewall?

图书馆的具体错误信息

CompletedListGrpc.Core.RpcException: Status(StatusCode=Unavailable, Detail="Connect Failed")

当为 firestore.googleapis.com 禁用防火墙 MITM 功能时,库工作正常。启用中间人攻击功能后,它不起作用。

子题:

1) 库代码是否有硬编码证书检查? (我找不到)

参见 https://github.com/googleapis/google-cloud-dotnet/blob/master/apis/Google.Cloud.Firestore.V1/Google.Cloud.Firestore.V1/FirestoreClient.cs#L550 行 ~550

public static gaxgrpc::ServiceEndpoint DefaultEndpoint { get; } = new gaxgrpc::ServiceEndpoint("firestore.googleapis.com", 443);

2a) .Net Framework 是否自动信任 windows 信任库中的证书?是否需要任何代码才能完成这项工作?

.Net Framework 似乎可以与 Windows 证书存储一起使用 - 请参阅 https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/working-with-certificates

2b) 也许证书只受信任但交互用户而不是整个机器,因此服务帐户看不到该证书 - 我会检查一下...

3) 如果这是导致 "Connect Failed" 的原因,图书馆是否会有关于证书的特定错误?

这可能是 GRPC 和 Windows 证书存储之间的问题。无论这是 GRPC(而不是 .Net)的根本问题还是由于 运行 它在不同的帐户下,将防火墙的正确根 CA 硬编码到应用程序中肯定会给您完全控制权。

您可以覆盖 DefaultEndpoint/Channel 以使用您自己的 ServicePoint,该服务点还包含硬编码证书

参见

  var cacert = File.ReadAllText(@"../ca.crt");
  var clientcert = File.ReadAllText(@"../client.crt");
  var clientkey = File.ReadAllText(@"../client.key");
  var ssl = new SslCredentials(cacert, new KeyCertificatePair(clientcert, clientkey));
  var channel = new Channel("firestore.googleapis.com", 443, ssl);
  //etc..

创建您自己的端点,然后显式提供它以覆盖 DefaultEndpoint。这是您将通过频道调用的函数。 (参见 https://github.com/googleapis/google-cloud-dotnet/blob/master/apis/Google.Cloud.Firestore.V1/Google.Cloud.Firestore.V1/FirestoreClient.cs#L550

public static FirestoreClient Create(grpccore::Channel channel, FirestoreSettings settings = null)
{
    gax::GaxPreconditions.CheckNotNull(channel, nameof(channel));
    return Create(new grpccore::DefaultCallInvoker(channel), settings);
}

此外,请仔细查看 SslCredentialsTest 代码 - https://github.com/grpc/grpc/blob/master/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs

注意:通常会使用 s_channelPool 创建频道。如果您手动创建它,它可能不会使用池 - 可能没有办法做到这一点。这对您来说可能是问题,也可能不是问题。


您可以扩展它以在运行时按名称手动从 Windows 证书存储中读取 public 证书信息,选择最新的。这意味着您可以使用 app.config 分配从 Windows 证书存储中获取的证书的 CN,这样您就无需手动发送软件的更新版本。


这 link 似乎表明默认情况下 "Publically Trusted Roots" 是可信的:https://forum.predix.io/questions/30875/event-hub-c-client-how-to-deal-with-the-tls-certif.html

var channel = new Channel("greeter.googleapis.com", new SslCredentials());  // Use publicly trusted roots.

这意味着虽然 firestore 没有 "hardcoded" 特定证书,但它通常不会与 Windows 证书存储集成,因此任何自定义安装的根 CA 都不可信。

仔细查看库源代码[https://github.com/googleapis/gax-dotnet/blob/master/Google.Api.Gax.Grpc/ChannelPool.cs]:

public Channel GetChannel(ServiceEndpoint endpoint, IEnumerable<ChannelOption> channelOptions)
{
    GaxPreconditions.CheckNotNull(endpoint, nameof(endpoint));
    var credentials = _lazyScopedDefaultChannelCredentials.Value.ResultWithUnwrappedExceptions();
    return GetChannel(endpoint, channelOptions, credentials);
}

credentials 参数是自动填充的,在 _lazyScopedDefaultChannelCredentialsGoogleCredential.GetApplicationDefaultAsync() 之后,我们最终到达 CreateDefaultCredentialAsync,它指的是您可能下载的 Google 服务文件来自 firebase 某处。

https://github.com/googleapis/google-api-dotnet-client/blob/master/Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs

注:

这个答案很可能只会导致自定义客户端凭据,而不是为自定义服务器端根 CA 提供硬编码凭据的能力。

这可能只是 Windows 证书存储配置错误。也许您 运行 软件所在的帐户无权访问正确的根 CA。不管是什么原因,有一种常见的 .Net 方法可以手动批准证书。

1) 首先,尝试简单地绕过检查的测试。参见

ServicePointManager
    .ServerCertificateValidationCallback += 
    (sender, cert, chain, sslPolicyErrors) => true;

不要让这段代码留在生产环境中,只用它看看这条路是否可行。

2) 接下来自定义此功能以手动检查根 CA 的特定证书和相关证书。强烈建议使用一个库来帮助你,但你可以让它与核心 .net 库一起工作。

结论

如果这个答案无效,我会删除它。