如何将 Google App Engine API (ASP.NET Core 3.1) 连接到 SSL 上的 Google Cloud SQL (Postgres)?

Bob*_*ten 1 c# postgresql google-app-engine google-cloud-sql google-cloud-platform

这是我从未问过的最令人沮丧的问题。我如何连接这两个?

我有一个 API (ASP.NET Core 3.1),我将此应用程序部署到 Google 的 App Engine 上。我还在 Cloud SQL(PostgreSQL 引擎 12)上连接了一个数据库。如果没有 SSL,连接也可以正常工作。添加 SSL 是一个巨大的痛苦。我不断收到以下错误:

Error Connecting to database FATAL : no pg_hba.conf entry for host"x.x.x.x", user"jiradbuser", database"jiradb", SSL off
Run Code Online (Sandbox Code Playgroud)

上述错误实际上没有任何意义,因为我在 GCP 上没有任何选项来修改此类文件。

我已经下载了 Google 在将数据库配置为仅接受 SSL 连接时提供的 client-cert.pem、client-key.pem 和 server-ca.pem 文件,当我在 PSQL 中运行以下命令时,我连接通过 SSL 就可以了:

psql "sslmode=verify-ca sslrootcert=server-ca.pem sslcert=client-cert.pem sslkey=client-key.pem hostaddr=<cloud sql public ip> port=5432 user=< my username> dbname=<database inside cloud sql instance>"
Run Code Online (Sandbox Code Playgroud)

我尝试使用 NpgSqlConnectionStringBuilder 从配置文件中获取相关的 ----BEGIN CERTIFICATE--------END CERTIFICATE--- 文件来生成包含该信息的连接字符串,但这不起作用:

    var connectionStringBuilder = new NpgsqlConnectionStringBuilder(connectionString)
    {
        RootCertificate = Configuration["Google:ServerCA"],
        ClientCertificate = Configuration["Google:ClientCert"],
        ClientCertificateKey = Configuration["Google:ClientKey"],
        SslMode = SslMode.Require,
        TrustServerCertificate = true,
        IncludeErrorDetails = true
    };
Run Code Online (Sandbox Code Playgroud)

以下是错误消息:

"exception\":{\"StackTrace\":\"   at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle)\\n   at Internal.Cryptography.Pal.OpenSslX509CertificateReader.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)\\n   at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)\\n   at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName, String password)\\n   at Npgsql.NpgsqlConnector.RawOpen(NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)\\n   at Npgsql.NpgsqlConnector.Open(NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)\\n   at Npgsql.ConnectorPool.OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)\\n   at Npgsql.ConnectorPool.<>c__DisplayClass38_0.<<Rent>g__RentAsync|0>d.MoveNext()\\n--- End of stack trace from previous location where exception was thrown ---
Run Code Online (Sandbox Code Playgroud)

我缺少一个步骤吗?我是否需要将这些文件转换为其他格式(例如 .pfx 或 .crt 或 .key)?我不认为我必须这么做,否则谷歌为什么要给我 .pem 文件呢?此外,我倾向于的一件事是将文件本身存储在 Google Secret Manager 中......这会改变什么吗?

我愿意回答您的问题或酌情添加其他信息。谢谢!

Mas*_*pud 5

工作溶液

过去 24 小时我一直面临同样的问题。我在下面列出了一个可行的解决方案。对第一次尝试表示歉意...那是我大声思考,这在这些论坛中是不合适的。

该解决方案基于在 Windows 10 Professional 上运行的 C# dotnet core SDK 5.0.201 控制台应用程序,并带有 Google SQL Cloud PostgreSQL 13 后端数据库。

PEM 文件

Google SQL Cloud 提供 PEM 文件;其中三个。两张证书和一把钥匙。有一个服务器证书颁发机构证书 (server-ca.pem) 和一个具有相应密钥的客户端证书(分别为 client-cert.pem 和 client-key.pem)。

根据你的例子,psql命令建议连接到 SQL Cloud 需要所有这三个。我正在使用 pgAdmin ,它的作用非常相似。

客户端证书和相应的密钥需要转换为 PFX(个人信息交换)格式。此文件格式本质上将客户端证书和密钥配对到一个文件中。

有两种方法可以执行此转换...使用openssl命令或使用 C# 代码执行此转换。

您可以在这篇 StackOverflow 帖子的“答案”中找到这两种方法...

Npgsql与.net core web api中的ssl证书连接

图片来源:Anish-V

如何正确地将证书和密钥传递给 Npgsql 连接

连接字符串

首先,设置连接字符串。除了 SSL 证书之外,您还需要提供主机、数据库名称以及数据库用户名和密码等信息。此外,由于我们的目标是与 Google SQL Cloud 进行 SSL 连接,因此您可能会在 Google 控制台中勾选“仅允许 SSL 连接”标志。如果是这样,您还需要将 SslMode 设置为“Require”(或者可能“Prefer”,具体取决于您的要求/配置)。

var _connectionString = new NpgsqlConnectionStringBuilder()
{
    Host = settings.Host,
    Database = settings.Database,
    Username = settings.Username,
    Password = settings.Password,
    SslMode = SslMode.Require
};
var _pgdb = new NpgsqlConnection(_connectionString.ConnectionString);
Run Code Online (Sandbox Code Playgroud)

客户端证书和密钥

查看客户端证书和密钥,上面的 StackOverflow 帖子采用客户端证书 (client-cert.pem) 和密钥 (client-key.pem) 并创建组合的 PFX 证书。如上所述,您可以使用代码openssl或在代码中执行此操作。目前,我正在追求“代码内”方法,以减少将来发布应用程序时的任何手动步骤。不过,我已经测试了该openssl方法并生成了一个 PFX 文件......这也有效。

要将 PFX 证书添加到连接,您可以使用ProvideClientCertificatesCallback该类的方法NpgsqlConnection...

_pgdb.ProvideClientCertificatesCallback += new ProvideClientCertificatesCallback;
_pgdb.Open();
Run Code Online (Sandbox Code Playgroud)

我们现在ProvideClientCertificatesCallback用我们自己的实现重载该方法,如下所示......

public void ProvideClientCertificatesCallback(X509CertificateCollection certificates)
{
    var clientCert = 
        GetCombinedCertificateAndKey(
            $@"{certificatePath}\client-cert.pem",
            $@"{certificatePath}\client-key.pem");
    certificates.Add(clientCert);
    Console.WritLine($"client-cert.pem/client-key.perm: {clientCert.Issuer}");
}
Run Code Online (Sandbox Code Playgroud)

该方法GetCombinedCertificateAndKey采用两个参数...客户端证书和客户端密钥的文件名。这个方法是从上面提到的StackOverflow帖子借用的...我稍微调整了一下...

private X509Certificate2 GetCombinedCertificateAndKey(string clientCertPath, string clientKeyPath)
{
    using var publicKey = new X509Certificate2(clientCertPath); ;

    var privateKeyText = System.IO.File.ReadAllText(clientKeyPath);
    var privateKeyBlocks = privateKeyText.Split("-", StringSplitOptions.RemoveEmptyEntries);
    var privateKeyBytes = Convert.FromBase64String(privateKeyBlocks[1]);
    using var rsa = RSA.Create();

    switch (privateKeyBlocks[0]) {
        case "BEGIN PRIVATE KEY":
            rsa.ImportPkcs8PrivateKey(privateKeyBytes, out _);
            break;
        case "BEGIN RSA PRIVATE KEY":
            rsa.ImportRSAPrivateKey(privateKeyBytes, out _);
            break;
    }

    var keyPair = publicKey.CopyWithPrivateKey(rsa);
    var Certificate = new X509Certificate2(keyPair.Export(X509ContentType.Pfx));
    return Certificate;
}
Run Code Online (Sandbox Code Playgroud)

很高兴通过 Visual Studio 调试器运行它...您可以检查证书的内容。在我的例子中,Google client-key.pem 似乎是一个 RSA 密钥,它是通过 switch 语句中的此方法拾取的。我怀疑所有谷歌密钥都是一样的。

另请注意,之前的行return...它以组合 PFX 格式导出客户端证书/密钥对,并将其作为证书返回给调用方法。

服务器证书颁发机构证书

对于服务器CA证书,我目前的解决方案是绕过该证书的验证。为此,只需将该TrustServerCertificate属性添加到连接字符串中,如下所示。顾名思义,这决定是否信任服务器证书而不验证它。我的想法是,在这种情况下,我相信证书是由信誉良好的来源提供的......谷歌。不过,我将继续寻找更完整的解决方案,以便我可以验证服务器 CA 证书。

var _connectionString = new NpgsqlConnectionStringBuilder()
{
    Host = settings.Host,
    Database = settings.Database,
    Username = settings.Username,
    Password = settings.Password,
    SslMode = SslMode.Require,
    TrustServerCertificate = true,
};
var _pgdb = new NpgsqlConnection(_connectionString.ConnectionString);
Run Code Online (Sandbox Code Playgroud)

结论...

就是这样。有用!:-) 我希望它对您有用并节省您一天的时间。

在回答有关将证书存储在 Google Secret Manager 中的问题时,我建议您这样做,尽管那完全是另一个主题。