I'm getting confused by documentation and examples of sqlalchemy and pg8000 to connect to another postgres database using SSL. A private key isn't required or anything. I'm trying to connect with SSL in require
mode. This is a screenshot of the SSL settings in a connection to the database through a SQL client (Datagrip):
As you can see, the text boxes to type in the key file's location or password are all blank; I've just selected the mode as Require
.
Now in my code, the following used to work but suddenly stopped working.
DATABASE_DB_URL=postgresql+pg8000://user:[email protected]:5432/database?ssl=True
db = records_client.Database(DATABASE_DB_URL)
# Returns error:
TypeError: connect() got an unexpected keyword argument 'ssl'
Here's the full stack trace
Traceback (most recent call last):
File "C:/Users/user/Documents/django-app/test2.py", line 44, in <module>
db = records_client.Database(DATABASE_DB_URL) #, connect_args={'ssl_context':True})
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\records.py", line 245, in __init__
self.db = self._engine.connect()
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\engine\base.py", line 3166, in connect
return self._connection_cls(self, close_with_result=close_with_result)
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\engine\base.py", line 96, in __init__
else engine.raw_connection()
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\engine\base.py", line 3245, in raw_connection
return self._wrap_pool_connect(self.pool.connect, _connection)
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\engine\base.py", line 3212, in _wrap_pool_connect
return fn()
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\pool\base.py", line 301, in connect
return _ConnectionFairy._checkout(self)
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\pool\base.py", line 761, in _checkout
fairy = _ConnectionRecord.checkout(pool)
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\pool\base.py", line 419, in checkout
rec = pool._do_get()
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\pool\impl.py", line 145, in _do_get
self._dec_overflow()
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\util\langhelpers.py", line 72, in __exit__
with_traceback=exc_tb,
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\util\compat.py", line 211, in raise_
raise exception
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\pool\impl.py", line 142, in _do_get
return self._create_connection()
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\pool\base.py", line 247, in _create_connection
return _ConnectionRecord(self)
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\pool\base.py", line 362, in __init__
self.__connect()
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\pool\base.py", line 605, in __connect
pool.logger.debug("Error on connect(): %s", e)
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\util\langhelpers.py", line 72, in __exit__
with_traceback=exc_tb,
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\util\compat.py", line 211, in raise_
raise exception
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\pool\base.py", line 599, in __connect
connection = pool._invoke_creator(self)
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\engine\create.py", line 578, in connect
return dialect.connect(*cargs, **cparams)
File "C:\Users\user\Documents\django-app\venv-dev\lib\site-packages\sqlalchemy\engine\default.py", line 584, in connect
return self.dbapi.connect(*cargs, **cparams)
TypeError: connect() got an unexpected keyword argument 'ssl'
Process finished with exit code 1
So I thought to add in the SSL parameter in the Python call instead of the environment variable like so:
DATABASE_DB_URL=postgresql+pg8000://user:[email protected]:5432/database?
db = records_client.Database(DATABASE_DB_URL, connect_args={'ssl_context':True})
# Returns error: ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: IP address mismatch, certificate is not valid for '98.765.4.321'. (_ssl.c:1091)
I'm pretty sure that IP address in the error is whitelisted to connect to the database.
I've been trying to find an example for proper syntax and usage, but am getting confused. What is the proper argument to pass into the connect call?
The example code below works for me, using a self-signed certificate.
Note that
ssl.CERT_REQUIRED
is the default forssl.create_default_context
, but you would want to set it if the context was created by callingSSLContext()
directly.According to the docs, passing
ssl_context=True
will use the result ofcreate_default_context()
, so there doesn't seem to be any difference between this code and what you are doing already, apart from loading a non-standard certificate path. TheSSLCertVerificationError
implies that your code is correct, but the combination of client machine and certificate is not. You may need to check thepg_hba.conf
file on the server.Output:
For completeness, a psycopg2 secure connection would look like this: