GCP部署应用中psycopg2传入SSL证书字符串连接数据库失败
问题:Psycopg2使用字符串形式的SSL证书/密钥连接GCP数据库失败
在GCP部署应用时,使用psycopg2连接数据库,当传入证书文件路径时连接正常:
psycopg2.connect(host='hostname',port=1234, connect_timeout=100, database='db', user='user', password='pwd', sslrootcert="server-cert.pem", sslcert="client-cert.pem", sslkey="key.pem")
但直接传入证书/密钥字符串时连接失败,报错:
FATAL: connection requires a valid client certificate
connection to server at "hostname", port 1234 failed
使用的代码如下:
SERVER_CERT = """-----BEGIN CERTIFICATE----- xxxxxx -----END CERTIFICATE-----""" CLIENT_CERT = """-----BEGIN CERTIFICATE----- xxxxxx -----END CERTIFICATE-----""" KEY = """-----BEGIN RSA PRIVATE KEY----- xxxxn-----END RSA PRIVATE KEY-----""" psycopg2.connect(host='hostname',port=1234, connect_timeout=100, database='db', user='user', password='pwd', sslrootcert=SERVER_CERT, sslcert=CLIENT_CERT, sslkey=KEY)
尝试过ssl.DER_cert_to_PEM_cert()和RSA.importKey()处理字符串,但仍然失败。请问如何直接传入字符串而非文件路径建立连接?
解决方案
核心原因
psycopg2的sslrootcert、sslcert、sslkey参数仅接受文件路径,而非证书/密钥的字符串内容。直接传入字符串会被当作文件路径解析,导致找不到对应文件,最终触发证书验证失败。
方法一:使用Python SSLContext对象(推荐)
通过创建ssl.SSLContext对象,直接加载字符串形式的证书和密钥,再将该对象传入psycopg2的sslcontext参数,无需创建临时文件。
import ssl import psycopg2 SERVER_CERT = """-----BEGIN CERTIFICATE----- xxxxxx -----END CERTIFICATE-----""" CLIENT_CERT = """-----BEGIN CERTIFICATE----- xxxxxx -----END CERTIFICATE-----""" KEY = """-----BEGIN RSA PRIVATE KEY----- xxxxn-----END RSA PRIVATE KEY-----""" # 创建SSL上下文,指定用于服务器认证的用途 ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) # 加载CA证书(服务器根证书),使用cadata参数传入字符串内容 ctx.load_verify_locations(cadata=SERVER_CERT) # 加载客户端证书和密钥,分别通过cert_data和key_data传入字符串内容 ctx.load_cert_chain(certfile=None, cert_data=CLIENT_CERT, keyfile=None, key_data=KEY) # 建立数据库连接,传入sslcontext参数 conn = psycopg2.connect( host='hostname', port=1234, connect_timeout=100, database='db', user='user', password='pwd', sslcontext=ctx )
方法二:使用临时文件
如果不想处理SSLContext,可以将字符串写入临时文件,再传入文件路径,使用后清理临时文件。
import tempfile import psycopg2 import os SERVER_CERT = """-----BEGIN CERTIFICATE----- xxxxxx -----END CERTIFICATE-----""" CLIENT_CERT = """-----BEGIN CERTIFICATE----- xxxxxx -----END CERTIFICATE-----""" KEY = """-----BEGIN RSA PRIVATE KEY----- xxxxn-----END RSA PRIVATE KEY-----""" # 创建临时文件存储证书和密钥,delete=False避免文件被自动删除 with tempfile.NamedTemporaryFile(mode='w', suffix='.pem', delete=False) as root_cert_file: root_cert_file.write(SERVER_CERT) root_cert_path = root_cert_file.name with tempfile.NamedTemporaryFile(mode='w', suffix='.pem', delete=False) as client_cert_file: client_cert_file.write(CLIENT_CERT) client_cert_path = client_cert_file.name with tempfile.NamedTemporaryFile(mode='w', suffix='.pem', delete=False) as key_file: key_file.write(KEY) key_path = key_file.name try: # 传入临时文件路径建立连接 conn = psycopg2.connect( host='hostname', port=1234, connect_timeout=100, database='db', user='user', password='pwd', sslrootcert=root_cert_path, sslcert=client_cert_path, sslkey=key_path ) finally: # 强制删除临时文件,避免遗留 os.unlink(root_cert_path) os.unlink(client_cert_path) os.unlink(key_path)
内容的提问来源于stack exchange,提问作者ds_Abc
相关产品推荐
相关产品推荐

