关于postgresql:Postgres-python多个SSL连接

Postgres - python multiple SSL connections

我在使用psycopg2和SSL建立两个并发的Postgres数据库连接(一个到主数据库,一个到从数据库)时遇到了麻烦。分别,两个连接工作,即:

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

有效,

也有效

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

但同时加入两个

1
2
3
4
5
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()第一个连接(如此处所示,但没有使用psycopg2动态地在python中更改ssl更改数据库(postgresql)),并且还尝试了各种psycopg.extensions隔离级别选项,但未成功。

预先感谢!


我相信我已经将问题归结为libq ... PostgreSQL C库。

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

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

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

考虑此代码

1
2
3
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的调试版本并安装了它。这是我得到的调试信息。 (我只发布相关信息)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[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只是在第二个连接上未使用正确的证书。

此外,我还对其他程序进行了一些测试。我针对PostgreSQL(Mac版)测试了Navicat-相同的问题。第一个连接成功,第二个连接未能验证证书。重新启动Navicat时,它再次发生...无论我按什么顺序尝试,第一个连接成功,第二个连接失败。

PgAdmin也发生了同样的事情(当前最新版本为1.20)。第一个连接成功,第二个连接失败。

我怀疑,只要使用libq进行连接,连接到PostgreSQL的任何软件或模块都会遭受相同的问题。实际上,我什至测试了PHP,并得到了相同的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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/

在此之前,我能够提出一个对我有效的解决方案...目前的解决方案是对两个服务器使用相同的证书。如果可以执行此操作,则它适用于两个连接,并且您可以将两个单独的连接连接到两个单独的服务器...(只要两个连接都可以使用相同的客户端证书进行连接)

对我来说,我只是对两个服务器使用了相同的服务器ssl证书,私钥和根证书...我只是使用通配符作为通用名称,然后自己对证书进行了签名(但是您可以使用商业通配符证书然后,我生成了一个客户端证书,并将这单个证书用于两个连接。

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

因此,您的代码现在变为:

1
2
3
4
5
6
7
8
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中有效的实际代码

1
2
3
4
5
6
7
8
9
10
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