oracle / python-oracledb

Python driver for Oracle Database conforming to the Python DB API 2.0 specification. This is the renamed, new major release of cx_Oracle
https://oracle.github.io/python-oracledb
Other
339 stars 67 forks source link

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

Closed FrancoisNoyez closed 2 years ago

FrancoisNoyez commented 2 years ago

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.

anthony-tuininga commented 2 years ago

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!

FrancoisNoyez commented 2 years ago

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.

anthony-tuininga commented 2 years ago

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!

FrancoisNoyez commented 2 years ago

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?

anthony-tuininga commented 2 years ago

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

anthony-tuininga commented 2 years ago

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!

FrancoisNoyez commented 2 years ago

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?

anthony-tuininga commented 2 years ago

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!

FrancoisNoyez commented 2 years ago

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?

anthony-tuininga commented 2 years ago

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!

FrancoisNoyez commented 2 years ago

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

anthony-tuininga commented 2 years ago

This is part of oracledb 1.2.0 which was just released.

FrancoisNoyez commented 2 years ago

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

rudolfnoe commented 1 year ago

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

FrancoisNoyez commented 1 year ago

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.

rudolfnoe commented 1 year ago

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.

FrancoisNoyez commented 1 year ago

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?

rudolfnoe commented 1 year ago

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

FrancoisNoyez commented 1 year ago

Hi @rudolfnoe Got it, thank you!

harshshah1618 commented 9 months ago

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