我在使用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 isolation_level选项,但没有成功。
提前致谢!
答案 0 :(得分:4)
我相信我已将问题追溯到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)。第一次连接成功,第二次连接失败。
我怀疑只要使用libq进行连接,任何连接到PostgreSQL的软件或模块都会遇到同样的问题。事实上,我甚至测试了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