pythonpython-requestsopenapiclient-certificates

how to connect to a server using mutual authentication (mTLS) using openapi3 python


I have a server which implements https://osia.readthedocs.io/en/stable/abis.yaml < This server is configured to use mutual TLS (as a client, I need to have a truststore, and a keypair to connect to the server). The abis.yaml does not indicate this security method.

I do not understand how to provide my keypair to openapi3. I think I need to use a persistent requests.session, but I do not understand how to provide this session.

Here is a sample code, which fails, but may explain what I am trying to do.

from requests import Session
import yaml
from openapi3 import OpenAPI

# load the spec file and read the yaml
with open('abis.yaml') as f:
    spec = yaml.safe_load(f.read())

# parse the spec into python - this will raise if the spec is invalid
req: Session = Session()
req.cert = ( "otsbms.pem", "otsbms.key.pem" )
req.verify = 'ca.pem'
req.request(method= 'DELETE', url='https://192.168.101.41/brs/v1/persons/P1?transactionId=guid',)
# the line above returns '500' meaning the https connection was successful, and the abis server did not understand my request, which is another problem outside the scope of this question.

api = OpenAPI(raw_document=spec, ssl_verify='ca.pem', use_session=True, session_factory=Session)
api.servers[0].url = 'https://192.168.101.41/'

# api.authenticate( "mutualTLS", ( "otsbms.pem", "otsbms.key.pem" ) )

# call operations and receive result models
result = api.call_deleteAll(parameters={"personId": "a123", "transactionId": "a456", },session=req)

The result is: I am almost sur my ca.pem is good but the server does fails when challenging the keypair. requests.exceptions.SSLError: HTTPSConnectionPool(host='192.168.101.41', port=443): Max retries exceeded with url: //v1/persons/a123?transactionId=a456 (Caused by SSLError(SSLError(1, '[SSL: SSLV3_ALERT_BAD_CERTIFICATE] sslv3 alert bad certificate (_ssl.c:997)')))


Solution

  • Turns out that openapi3 is a mostly inactive project. Using aiopenapi3 is a better solution.

    Here is the answer to the ticket:

    Hi,

    to me your approach is basically valid. requests.Request class does not have a .cert property, it get's added by openapi3 when mutualTLS is requested. The (client cert/key tuple) has to be passed to requests.Session.request() or requests.Session.send - as you do. If mutualTLS is not set, it fails as the property does not exist Possible workaround:

    result = session.send(self._request.prepare(), 
        verify=verify, 
        cert=getattr(self._request, "cert", None)) Unlikely this has been working properly.
    

    I shared the issue in aiopenapi3, literally the same issue even though it's requests vs. httpx. here is my take on it.

    As the required asgi tls extensions is not implemented in hypercorn/uvloop and FastAPI does not know mutualTLS - the unit tests been quite interesting.