python-oracledb icon indicating copy to clipboard operation
python-oracledb copied to clipboard

Improve TLS connection implementation to support use of one way TLS certificates without needing OS recognition

Open FrancoisNoyez opened this issue 2 years ago • 11 comments

Talking about version 1.1.0.

The current version does not allow to use unencrypted .pem file for mTLS connection, for which the following code, found at line 134 of the 'src/oracledb/impl/thin/crypto.pyx' module, fails in that case:

ssl_context.load_cert_chain(pem_file_name,
                            password=params._get_wallet_password())

Things work if we make this code line conditional, for instance on whether a password to decrypt the certificate is actually provided:

password = params._get_wallet_password()
if password is not None:
	ssl_context.load_cert_chain(pem_file_name,
                               password=password)

Cf this discussion on the forum: something like that is necessary when one is using the Oracle Cloud functionality of the Amazon Cloud service, and notably when one is not admin of the server actually hosting the Oracle database.

FrancoisNoyez avatar Sep 20 '22 15:09 FrancoisNoyez

To be clear: unencrypted .pem for mTLS connections work just fine. The issue is that if the .pem file does NOT contain a private key, the code fails -- this occurs with one-way TLS, when only the certificates are included. The usual solution is to get the OS to recognize the certificates, but if this is not an option, some approach is needed to get Python to recognize them for this particular TLS connection.

As such, simply checking for the wallet_password value being None is insufficient. I'll give it some more thought as to how this can be accomplished!

anthony-tuininga avatar Sep 20 '22 15:09 anthony-tuininga

Hi Anthony,

Ok, I see. Sorry for me using the wrong terminology here, I don't have speific knowledge about encryption protocols in general, and TLS connection in particular. I have updated the title of the issue, but not my first post.

Hm, ok. I agree that, ideally, the change that I suggested is not enough, insofar as the behavior of the function / of the library needs to be consistent overall, and that this change would need to be properly advertised / we would need to let people know how it works. But maybe you think that this is insufficient for other reasons. In that case, I'm not sure to understand why. Would it be possible for you to explain this, please?

Anyway, thank you for considering it.

FrancoisNoyez avatar Sep 21 '22 08:09 FrancoisNoyez

Looking further into this, I suspect it may be possible to just do this:

try:
    ssl_context.load_cert_chain(pem_file_name,
                                password=params._get_wallet_password())
except ssl.SSLError:
    pass

In other words, try to load the certificate chain from the supplied PEM file and simply ignore the error if it is unsuccessful. The error (as noted) isn't really all that helpful anyway! If one-way TLS is set up, it will work, and if 2-way TLS is required, a different error will be raised. I'm just checking internally to see what that looks like with an older database. Stay tuned!

anthony-tuininga avatar Sep 21 '22 23:09 anthony-tuininga

Hi Anthony, So, was it possible for you to assess whether the change that you suggested to implement would be possible without breaking compatibility with older databases?

FrancoisNoyez avatar Sep 29 '22 11:09 FrancoisNoyez

Not yet, unfortunately. I'm still waiting but will prod internally again. :-)

anthony-tuininga avatar Sep 29 '22 13:09 anthony-tuininga

I was finally able to test with an older database. When supplying an invalid password for an encrypted PEM file, instead of this error

oracledb.exceptions.OperationalError: DPY-6005: cannot connect to database. Connection failed with "[SSL] PEM lib (_ssl.c:3895)"

you will now get

oracledb.exceptions.OperationalError: DPY-6005: cannot connect to database. Connection failed with "[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:997)"

And for your case, you can now supply a PEM file containing just certificates and this will be used to validate the server without requiring the OS to recognize the server certificate.

This patch will go into the next release of python-oracledb. Thanks for your patience!

anthony-tuininga avatar Oct 18 '22 22:10 anthony-tuininga

Hi Anthony,

Awesome, thank you! I have tested out the code currently associated to the top of the 'main' branch (commit bb694028), and it works for me / for my use case. Thank you for having taken this in consideration. If I understand properly, this will part of the 1.1.2 version? Or the 1.2.0 version? Is there a way for me to be warned when a new version of this library will be released, or should I just regularly check the state of the repository for this project?

FrancoisNoyez avatar Oct 19 '22 15:10 FrancoisNoyez

You're welcome. This will likely become part of 1.1.2, but it depends on a number of factors. You can "watch" this repository -- just for new releases if that is all you're interested in!

anthony-tuininga avatar Oct 19 '22 16:10 anthony-tuininga

Nice, did not know about this functionality of github, thank you! Then it's all set, all that's left to do is to wait for the release : )

Should I let you close this issue, once the release has occurred? Or should I do it myself?

FrancoisNoyez avatar Oct 19 '22 16:10 FrancoisNoyez

I usually add one more comment when the release is made and then close it, but if you prefer to close it now, that's fine, too!

anthony-tuininga avatar Oct 19 '22 16:10 anthony-tuininga

Ok, then no, I would prefer that you do as you usually do : )

FrancoisNoyez avatar Oct 20 '22 07:10 FrancoisNoyez

This is part of oracledb 1.2.0 which was just released.

anthony-tuininga avatar Nov 14 '22 18:11 anthony-tuininga

Got it, I saw that, now things work great on my end, thank you : ) !

FrancoisNoyez avatar Nov 15 '22 14:11 FrancoisNoyez

Hi Francois, I'try to connect to an AWS RDS Oracle Instance but always get get the error mentioned above. I copied the contents of https://truststore.pki.rds.amazonaws.com/eu-central-1/eu-central-1-bundle.pem into a file called ewallet.pem and set the appropriate wallet_location in the connect method. But I still get the SSL-Handshake error. This is my code: connection = oracledb.connect(user='<usernam>', password=userpwd, host='...', port='..', protocol='tcps', sid='...', ssl_server_dn_match=False, wallet_location='path_to_ewallet_pem') I'am using oracledb version 1.3.1. Can you provide a running example please? Thanks a lot

rudolfnoe avatar Jun 08 '23 16:06 rudolfnoe

Hi @rudolfnoe ,

I'm sorry, but I'm not a maintainer of this library, and now that the need that I had opened this issue for has been met, I have forgotten much about it. To solve your issue, I would suggest to perform some experiments and review the documentation, and if that's not enough, then I would suggest opening an issue in this repository, by giving full context and info regarding the experiments that you would have performed.

FrancoisNoyez avatar Jun 16 '23 17:06 FrancoisNoyez

Hi @FrancoisNoyez, I solved the problem myself. The cause was a mismatch in the available cipher suites. AWS RDS applies the cipher SSL_RSA_WITH_AES_256_CBC_SHA by default which is not available in the default security context of the python SSL library as it assessed as insecure. When changing the RDS Cipher to a more secure one like TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 it works.

rudolfnoe avatar Jun 20 '23 06:06 rudolfnoe

Hi @rudolfnoe I see, glad for you! Out of curiosity, how did you do, in order to perform the change of cipher? Is it something that you did within the python code? Or something done in the environment in which the python program runs?

FrancoisNoyez avatar Jun 20 '23 07:06 FrancoisNoyez

Hi @FrancoisNoyez, see my answer in https://github.com/oracle/python-oracledb/discussions/138

rudolfnoe avatar Jul 17 '23 16:07 rudolfnoe

Hi @rudolfnoe Got it, thank you!

FrancoisNoyez avatar Jul 18 '23 07:07 FrancoisNoyez

Hello @rudolfnoe i have the same error as you can i know what is the final oracledb.connect() string you passed? i did not understand how to use the ssl_context param in the new version

harshshah1618 avatar Feb 22 '24 08:02 harshshah1618