Skip to content
This repository has been archived by the owner on Aug 30, 2024. It is now read-only.

Error 403 when trying to access couchDB server that uses SSL certificate. #499

Closed
othmanechentouf opened this issue May 27, 2021 · 5 comments

Comments

@othmanechentouf
Copy link

othmanechentouf commented May 27, 2021

Hello,
I'm trying to connect to a remote couchDB server, this server uses a SSL certificate which I installed correctly on my mac and I can access the server via my browser.
When I try to connect to the server via python-cloudant, I get an error, here's the code :

from cloudant.client import Cloudant
from cloudant.client import CouchDB

#client = CouchDB('myuser', 'mypassword', url='https://xxx.xx.xxx.xx', connect=True)

session = client.session()
print('Username: {0}'.format(session['userCtx']['name']))
print('Databases: {0}'.format(client.all_dbs()))

# Disconnect from the server
client.disconnect()

and here's the error I get :

HTTPError                                 Traceback (most recent call last)
<ipython-input-57-e11f4f61f4aa> in <module>
      5 
      6 
----> 7 client = CouchDB('myuser', 'mypassword', url='https://xxx.xx.xxx.x', connect = True)
      8 
      9 

~/miniconda3/lib/python3.8/site-packages/cloudant/client.py in __init__(self, user, auth_token, admin_party, **kwargs)
    120         connect_to_couch = kwargs.get('connect', False)
    121         if connect_to_couch and self._DATABASE_CLASS == CouchDatabase:
--> 122             self.connect()
    123 
    124     @property

~/miniconda3/lib/python3.8/site-packages/cloudant/client.py in connect(self)
    188             self.r_session.headers.update(self._client_user_header)
    189 
--> 190         self.session_login()
    191 
    192         # Utilize an event hook to append to the response message

~/miniconda3/lib/python3.8/site-packages/cloudant/client.py in session_login(self, user, passwd)
    229         :param str auth_token: Authentication token used to connect to server.
    230         """
--> 231         self.change_credentials(user=user, auth_token=passwd)
    232 
    233     def change_credentials(self, user=None, auth_token=None):

~/miniconda3/lib/python3.8/site-packages/cloudant/client.py in change_credentials(self, user, auth_token)
    239         """
    240         self.r_session.set_credentials(user, auth_token)
--> 241         self.r_session.login()
    242 
    243     def session_logout(self):

~/miniconda3/lib/python3.8/site-packages/cloudant/_client_session.py in login(self)
    153             data={'name': self._username, 'password': self._password},
    154         )
--> 155         resp.raise_for_status()
    156 
    157     def logout(self):

~/miniconda3/lib/python3.8/site-packages/requests/models.py in raise_for_status(self)
    891 
    892         if http_error_msg:
--> 893             raise HTTPError(http_error_msg, response=self)
    894 
    895     def close(self):

HTTPError: 403 Client Error: Forbidden for url:

I can't figure out a way around it, another thing is when I point requests directly to the certificate using the code below, I get a response 200, which means that the connection is established. Here's the code :

r = requests.get('https://xxx.xx.xxx.x', verify='catrust/',cert=('client01.crt', 'client01.key'))

I'm using macOS Big Sur, Python 3.9.

Thank you in advance.

@emlaver
Copy link
Member

emlaver commented May 27, 2021

Hello @othmanechentouf, there's some helpful info mentioned in another issue:

Since all the HTTP requests python-cloudant makes go through Requests you should also be able to use the REQUESTS_CA_BUNDLE environment variable to point to a CA certificate file that will allow Requests to validate your certificate without needing to pass the verify argument to each request.

I believe you'll want to set the REQUESTS_CA_BUNDLE env variable and set the verify attribute on the client's r_session:

client = CouchDB('myuser', 'mypassword', url='https://xxx.xx.xxx.xx')
client.r_session.verify = '/path/to/certfile'
client.connect()
...

@othmanechentouf
Copy link
Author

Hello @emlaver, thank you for your response.
the code you pointed out is giving me this error :

AttributeError: 'NoneType' object has no attribute 'verify'

This is cause this line of code :

CouchDB('myuser', 'mypassword', url='https://xxx.xx.xxx.xx')

is returning an empty object.
I've set the REQUESTS_CA_BUNDLE variable to point here /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/certifi/cacert.pem, I'm not sure where the problem is coming from.

@othmanechentouf
Copy link
Author

I have to point out that with this code :

import requests

r = requests.get('https://xxx.xx.xxx.xx', verify='catrust/ca.crt',cert=('client01.crt', 'client01.key'))

data = r.content  # Content of response

print(r.status_code)  # Status code of response
print(data)

I get no error and a connection is established.
My guess is that I have to find a way to point couchDB to these same files to get the connection through, do you have any suggestions ?

@emlaver
Copy link
Member

emlaver commented May 28, 2021

@othmanechentouf I did a bit more digging and the connect call initializes the underlying session. I think what you'll want to do is:

from cloudant import CouchDB
from cloudant._client_session import ClientSession

client = CouchDB('myuser', 'mypassword', url='https://xxx.xx.xxx.xx')
client.r_session = ClientSession()
client.r_session.verify = ''
client.connect()

@othmanechentouf
Copy link
Author

othmanechentouf commented May 29, 2021

@emlaver It didn't work, but I found another solution.

You have to install a package called requests_pkcs12, which allows you to create an adapter to use within the Cloudant/CouchDB function. This adapter takes the certificate file and its password as arguments, and then you pass the adapter as an argument when calling Cloudant/CouchDB. Here's the code :

from requests import Session 
from requests_pkcs12 import Pkcs12Adapter 
from requests_pkcs12 import get
from cloudant.client import Cloudant
from cloudant.client import CouchDB

 # Loading the server's certificate file and its password 
my_adapter=Pkcs12Adapter(pkcs12_filename='client01.p12', pkcs12_password='certificate_password')

# calling CouchDB with our new adapter as one of its arguments 
client=CouchDB('myusername', 'mypassword', url='https://xxx.xx.xxx.xx',connect=True,adapter=my_adapter)
session = client.session()

# Accessing the Server and printing all the databases withtin
print('Username: {0}'.format(session['userCtx']['name']))
print('Databases: {0}'.format(client.all_dbs()))
client.disconnect()

In case the server you are trying to access is using other kind of certificates (.pem/.cer/.crt..), You just have to point an adapter to that and use your adapter variable as argument for your Cloudant/CouchDB function call.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants