【问题标题】:Postgres - python multiple SSL connectionsPostgres - python 多个 SSL 连接
【发布时间】:2014-11-28 15:08:38
【问题描述】:

我在使用 psycopg2 和 SSL 建立两个并发 Postgres 数据库连接(一个到主数据库,一个到从属数据库)时遇到了麻烦。另外,两个连接都有效,即:

import psycopg2
dsnMaster='dbname=... sslcert=path/to/master/cert'
psycopg2.connect(dsnMaster, connection_factory=None, async=False)

有效,所以也有效

import psycopg2
dsnSlave='dbname=... sslcert=path/to/slave/cert'
psycopg2.connect(dsnSlave, connection_factory=None, async=False

但同时加入

import psycopg2
dsnMaster='dbname=... sslcert=path/to/master/cert'
psycopg2.connect(dsnMaster, connection_factory=None, async=False)
dsnSlave='dbname=... sslcert=path/to/slave/cert'
psycopg2.connect(dsnSlave, connection_factory=None, async=False)

第二次连接总是失败,SSL error: block type is not 01 .似乎 psycopg 使用了之前连接的证书。

我尝试了 .close() 第一个连接(如图所示但没有 ssl change database (postgresql) in python using psycopg2 dynamically),还尝试了各种 psycopg.extensions 隔离级别选项,但没有成功。

提前致谢!

【问题讨论】:

    标签: python postgresql ssl psycopg2


    【解决方案1】:

    我相信我已经将问题追溯到 libq...PostgreSQL C 库。

    我也注意到我不能为 2 个不同的连接使用不同的 ssl 客户端证书。第一个连接总是成功,而第二个连接总是失败,SSL error: certificate verify failed

    在服务器日志中我得到could not accept SSL connection: tlsv1 alert unknown ca

    这告诉我第二个连接可能正在尝试使用第一个连接中的 ssl 证书,而不是使用它被告知要使用的 ssl 证书。

    考虑这段代码

    import psycopg2
    conn1 = psycopg2.connect('host=server1... sslcert=path/to/cert1')
    conn2 = psycopg2.connect('host=server2... sslcert=path/to/cert2')
    

    连接 2 似乎使用的是 cert1 而不是 cert2

    我认为 psycopg2 有问题......也许它正在缓存客户端 ssl 证书......

    我继续构建了 psycopg2 的调试版本并安装了它。我再次尝试了我的代码并获得了大量的调试信息。这是我得到的调试信息。 (我只是发布相关信息)

    [98940] psyco_connect: dsn = 'dbname=testdb user=testdb host=server1 sslrootcert=root1.crt sslkey=cert1.key sslcert=cert1.crt sslmode=verify-full', async = 0
    [98940] connection_setup: init connection object at 0x103093048, async 0, refcnt = 1
    [98940] con_connect: connecting in SYNC mode
    [98940] conn_connect: new postgresql connection at 0x10047ff90
    [98940] conn_connect: server standard_conforming_strings parameter: on
    [98940] conn_connect: server requires E'' quotes: NO
    [98940] conn_connect: using protocol 3
    [98940] conn_connect: client encoding: UTF8
    [98940] clear_encoding_name: UTF8 -> UTF8
    [98940] conn_connect: DateStyle ISO, MDY
    [98940] connection_setup: good connection object at 0x103093048, refcnt = 1
    # ... Got a good 1st connection here
    # ... (Tons more lines of output before the 2nd connection)
    [98940] psyco_connect: dsn = 'dbname=testdb user=testdb host=server2 sslrootcert=root2.crt sslkey=cert2.key sslcert=cert2.crt sslmode=verify-full', async = 0
    [98940] connection_setup: init connection object at 0x103093170, async 0, refcnt = 1
    [98940] con_connect: connecting in SYNC mode
    [98940] conn_connect: new postgresql connection at 0x100682d30
    [98940] conn_connect: PQconnectdb(dbname=testdb user=testdb host=server2 sslrootcert=root2.crt sslkey=cert2.key sslcert=cert2.crt sslmode=verify-full) returned BAD
    [98940] connection_init: FAILED
    [98940] conn_close: PQfinish called
    [98940] connection_dealloc: deleted connection object at 0x103093170, refcnt = 0
    

    如果我切换2个连接,结果是一样的……第一个连接成功,但第二个连接失败。因此,第二个连接的 dsn 是正确的,因为如果先执行,则连接成功。

    检查 psycopg2 的源代码,它只是从 libq C 库中调用 PQconnectdb ......并且它使用正确的参数调用它。 您可以在PQconnectdb 上查看文档 http://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-PQCONNECTDB

    这告诉我 psycopg2 正在使用正确的参数正确调用 PQconnectdb,而 PQconnectdb 只是没有在第二次连接上使用正确的证书。

    更重要的是,我还对其他程序进行了一些测试。我测试了 Navicat for PostgreSQL(Mac 版)——同样的问题。第一次连接成功,第二次连接验证证书失败。当我重新启动 Navicat 时,它再次发生......无论我尝试什么顺序,第一次连接成功,第二次连接失败。

    PgAdmin 也会发生同样的情况(最新版本目前是 1.20)。第一次连接成功,第二次连接失败。

    我怀疑任何连接到 PostgreSQL 的软件或模块只要使用 libq 连接就会遇到同样的问题。事实上,我什至测试了 PHP,得到了相同的结果

    root@test:~# php -a
    Interactive mode enabled
    
    php > // Test with server 1 first
    php > $conn = pg_connect('host=server1 user=testdb dbname=testdb sslcert=cert1.crt sslmode=verify-full sslkey=cert1.key sslrootcert=root1.crt');
    php > $conn2 = pg_connect('host=server2 user=testdb dbname=testdb sslcert=cert2.crt sslmode=verify-full sslkey=cert2.key sslrootcert=root2.crt');
    PHP Warning:  pg_connect(): Unable to connect to PostgreSQL server: SSL error: certificate verify failed in php shell code on line 1
    php > quit
    root@test:~# php -a
    Interactive mode enabled
    
    php > // Test with server 2 first
    php > $conn2 = pg_connect('host=server2 user=testdb dbname=testdb sslcert=cert2.crt sslmode=verify-full sslkey=cert2.key sslrootcert=root2.crt');
    php > $conn = pg_connect('host=server1 user=testdb dbname=testdb sslcert=cert1.crt sslmode=verify-full sslkey=cert1.key sslrootcert=root1.crt');
    PHP Warning:  pg_connect(): Unable to connect to PostgreSQL server: SSL error: certificate verify failed in php shell code on line 1
    php > quit
    root@test:~# php -a
    Interactive mode enabled
    
    php > // Test using the same certificate
    php > $conn = pg_connect('host=server1 user=testdb dbname=testdb sslcert=cert1.crt sslmode=verify-full sslkey=cert1.key sslrootcert=root1.crt');
    php > $conn2 = pg_connect('host=server2 user=testdb dbname=testdb sslcert=cert1.crt sslmode=verify-full sslkey=cert1.key sslrootcert=root1.crt');
    php > // No problems. Both connect just fine now
    

    我的建议是使用 PostgreSQL 提交错误报告。不确定这是否是提交此类错误报告的正确位置 http://www.postgresql.org/support/submitbug/

    在此之前,我能够想出一个适合我的解决方案......现在的解决方案是简单地为两台服务器使用相同的证书。如果你能做到这一点,它适用于两个连接,你可以有 2 个独立的连接到 2 个独立的服务器......(只要两个连接都可以使用相同的客户端证书进行连接)

    对我来说,我只是为两台服务器使用了相同的服务器 ssl 证书、私钥和根证书...我只是使用通配符作为公用名并自己签署了证书(但您可以使用商业通配符证书如果你愿意的话)然后,我生成了一个客户端证书并将该证书用于两个连接。

    这可能不是您要寻找的答案,但这似乎是您可以使用客户端证书身份验证通过 SSL 与 2 个不同服务器建立 2 个连接的唯一方法。无论您使用什么编程语言或软件,这都是事实。

    所以,你的代码现在变成这样:

    import psycopg2
    dsnMaster='dbname=... sslcert=path/to/master/cert'
    psycopg2.connect(dsnMaster, connection_factory=None, async=False)
    
    # Here, the dsnSlave simply uses the same cert as the master
    # Other connection details like the host and dbname can be different
    dsnSlave='dbname=... sslcert=path/to/master/cert'
    psycopg2.connect(dsnSlave, connection_factory=None, async=False)
    

    这是我在 python 中的实际代码

    root@test:~# python3
    Python 3.4.0 (default, Jun 19 2015, 14:20:21) 
    [GCC 4.8.2] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import psycopg2
    >>> dsn1 = 'host=server1 user=testdb dbname=testdb sslcert=cert1.crt sslmode=verify-full sslkey=cert1.key sslrootcert=root1.crt'
    >>> conn1 = psycopg2.connect(dsn1)
    >>> dsn2 = 'host=server2 user=testdb dbname=testdb sslcert=cert1.crt sslmode=verify-full sslkey=cert1.key sslrootcert=root1.crt'
    >>> conn2 = psycopg2.connect(dsn2)
    >>> # YAY, no issues and both connections work
    

    【讨论】:

    • 谢谢雷,非常有趣...我最终得到了相同的解决方案:在两台服务器上使用相同的证书。
    猜你喜欢
    • 2018-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-28
    • 2011-03-11
    • 1970-01-01
    相关资源
    最近更新 更多