OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.84k stars 6.59k forks source link

SSL Exception when connecting to an HTTPS service #12355

Open davesargrad opened 2 years ago

davesargrad commented 2 years ago

We use version 5.4.0 of the openapi generator. We have a nodejs service, and python and javascript clients.

We autogenerate our javascript and python client libraries to allow our javascript and python client applications to connect to our NodeJs OpenApi service.

I'm able to connect my python clients just fine to an HTTP instance of the service. In fact our javascript applications can connect both to HTTP and HTTPS service instances

However our python clients complain when we connect them to an HTTPS service instance.

I've updated the python code to pass in an ssl_ca_cert concatenated pem file. In the following I instantiate the Configuration class that is created by the OpenAPI generator.

self.configuration = Configuration(host, ssl_ca_cert=ssl_cert)

However I see the following error when I then try to hit a protected route on the HTTPS service. <class 'urllib3.exceptions.MaxRetryError'>, MaxRetryError("HTTPSConnectionPool(host='changeme.com', port=8086): Max retries exceeded with url: /video (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1125)')))"), <traceback object at 0x7fbbbe122f80>

Though I've used OpenAPI successfully before, it has always been with HTTP. Its possible that I am simply missing something that I must do.

In the following I am showing the constructed RESTClientObject within the debugger. Here we see the ca_certs is set to my "test.pem" file.

image

And this next picture shows the point at which the request is about to go out: image

The request seems to go out to the server, and I believe that the server is rejecting the request. However I'm not sure how to further resolve this issue. We tested this by shutting down the service, and verifying that the python client fails in a different way. Then we restarted the service and the client then reports the SSL verification error shown above.

We also have javascript openapi clients, and they are able to talk to the HTTPS service.. just fine. So this problem seems to be strictly with the generated python clients

I've further isolated the problem to the SSL handshake with the server: image

we use openssl verion 1.1.1n image

and python 3.8.8 image

and urllib 1.26.4 image

Any guidance would be appreciated.

davesargrad commented 2 years ago

I've discovered that I am able to replicate the problem as follows:

from urllib import request
resp = request.urlopen("https://sdbie-sargrad.chgme.com:8081/api-docs") 
html = resp.read()

and fix it as follows:

from urllib import request
import ssl
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.verify_mode = ssl.CERT_REQUIRED 
context.load_verify_locations("/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem")
resp = request.urlopen("https://sdbie-sargrad.chgme.com:8081/api-docs", context=context) 
html = resp.read()

Currently our python client code does the following:

from api.vlc_api_1_5_0.openapi_client.model.video import Video
from api.vlc_api_1_5_0.openapi_client.exceptions import ApiException
from api.vlc_api_1_5_0.openapi_client.exceptions import ApiTypeError
from api.vlc_api_1_5_0.openapi_client.configuration import Configuration
from api.vlc_api_1_5_0.openapi_client.api import video_api
from api.vlc_api_1_5_0.openapi_client.api_client import ApiClient

self.configuration = Configuration(host)
with ApiClient(self.configuration) as api_client:
    vid_api = video_api.VideoApi(api_client)
    try:
        return vid_api.get_video_inventory()
    except ApiException as e:
        self.logger.error("Exception when calling VideoApi->get_video_inventory: %s\n" % e)

So how do I pass the comparable SSL context into the api.VideoApi constructor?

ralphflat commented 2 years ago

I have seen similar things with the openapi generated code. Simplifying the code as @davesargrad did above, seems to indicate that we might be able to set some environment parameters to allow getting the correct SSL context.

weetster commented 1 year ago

I ran into the same problem. I found that the generated Python code does use certifi for certificates: https://github.com/OpenAPITools/openapi-generator/blob/v6.4.0/modules/openapi-generator/src/main/resources/python/rest.handlebars#L40

So you could try making sure that certifi is updated to the latest version in your Python environment. Additionally, the certificate was actually self-signed in my case, even though I thought it wasn't. A corporate firewall that does certificate proxying was interfering by presenting its own self-signed certificate instead of the actual one from the HTTPS service.