ansys / openapi-common

Common authentication components for OpenAPI client libraries
https://openapi.docs.pyansys.com/
MIT License
2 stars 0 forks source link

OIDC authentication fails if Granta MI uses a private cert and the IdP uses a public cert #637

Closed Andy-Grigg closed 2 weeks ago

Andy-Grigg commented 3 months ago

πŸ” Before submitting the issue

🐞 Description of the bug

Using PyGranta RecordLists as an example implementation.

If I try to connect to Granta MI configured in OIDC mode with a private cert, but with a publicly-hosted IdP with a public cert, authentication fails.

I get the following stack trace:

Traceback (most recent call last):
  File "C:\code\oidc-investigation\venv\Lib\site-packages\urllib3\connectionpool.py", line 466, in _make_request
    self._validate_conn(conn)
  File "C:\code\oidc-investigation\venv\Lib\site-packages\urllib3\connectionpool.py", line 1095, in _validate_conn
    conn.connect()
  File "C:\code\oidc-investigation\venv\Lib\site-packages\urllib3\connection.py", line 652, in connect
    sock_and_verified = _ssl_wrap_socket_and_match_hostname(
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\urllib3\connection.py", line 805, in _ssl_wrap_socket_and_match_hostname
    ssl_sock = ssl_wrap_socket(
               ^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\urllib3\util\ssl_.py", line 465, in ssl_wrap_socket
    ssl_sock = _ssl_wrap_socket_impl(sock, context, tls_in_tls, server_hostname)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\urllib3\util\ssl_.py", line 509, in _ssl_wrap_socket_impl
    return ssl_context.wrap_socket(sock, server_hostname=server_hostname)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python312\Lib\ssl.py", line 455, in wrap_socket
    return self.sslsocket_class._create(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python312\Lib\ssl.py", line 1042, in _create
    self.do_handshake()
  File "C:\Program Files\Python312\Lib\ssl.py", line 1320, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1000)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\code\oidc-investigation\venv\Lib\site-packages\urllib3\connectionpool.py", line 789, in urlopen
    response = self._make_request(
               ^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\urllib3\connectionpool.py", line 490, in _make_request
    raise new_e
urllib3.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1000)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests\adapters.py", line 667, in send
    resp = conn.urlopen(
           ^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\urllib3\connectionpool.py", line 873, in urlopen
    return self.urlopen(
           ^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\urllib3\connectionpool.py", line 873, in urlopen
    return self.urlopen(
           ^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\urllib3\connectionpool.py", line 873, in urlopen
    return self.urlopen(
           ^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\urllib3\connectionpool.py", line 843, in urlopen
    retries = retries.increment(
              ^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\urllib3\util\retry.py", line 519, in increment
    raise MaxRetryError(_pool, url, reason) from reason  # type: ignore[arg-type]
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='idp.host.com', port=443): Max retries exceeded with url: /oauth/token (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1000)')))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\code\oidc-investigation\test.py", line 8, in <module>
    client = Connection("https://my_mi_server/mi_servicelayer", config).with_oidc().authorize().connect()
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\ansys\openapi\common\_session.py", line 484, in authorize
    self._session_factory.get_session_with_interactive_authorization(login_timeout)
  File "C:\code\oidc-investigation\venv\Lib\site-packages\ansys\openapi\common\_oidc.py", line 195, in get_session_with_interactive_authorization
    self._authorized_session.get(self._api_url)
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests\sessions.py", line 602, in get
    return self.request("GET", url, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests\sessions.py", line 575, in request
    prep = self.prepare_request(req)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests\sessions.py", line 484, in prepare_request
    p.prepare(
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests\models.py", line 371, in prepare
    self.prepare_auth(auth, url)
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests\models.py", line 602, in prepare_auth
    r = auth(self)
        ^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests_auth\authentication.py", line 634, in __call__
    token = OAuth2.token_cache.get_token(
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests_auth\oauth2_tokens.py", line 165, in get_token
    new_token = on_missing_token(**on_missing_token_kwargs)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests_auth\authentication.py", line 652, in request_new_token
    token, expires_in, refresh_token = request_new_grant_with_post(
                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests_auth\authentication.py", line 68, in request_new_grant_with_post
    response = session.post(url, data=data, timeout=timeout)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests\sessions.py", line 637, in post
    return self.request("POST", url, data=data, json=json, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests\sessions.py", line 589, in request
    resp = self.send(prep, **send_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests\sessions.py", line 703, in send
    r = adapter.send(request, **kwargs)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\ansys\openapi\common\_session.py", line 537, in send
    return super().send(request, stream, timeout or self.timeout, verify, cert, proxies)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\code\oidc-investigation\venv\Lib\site-packages\requests\adapters.py", line 698, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='idp.host.com', port=443): Max retries exceeded with url: /oauth/token (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1000)')))

It seems to be that the OIDC request to the IdP is being made with the cert required to authenticate to MI. Since when you provide a cert to requests it uses that instead of the certifi scripts, any requests to servers with public certs (or signed by any other CA) will fail.

It is possible to work around this issue either by specifying verify_ssl=False, or install pip-system-certs.

πŸ“ Steps to reproduce

from ansys.grantami.recordlists import Connection
from ansys.openapi.common import SessionConfiguration

config = SessionConfiguration(
    cert_store_path=r"C:\path\to\private\CA\cert.crt"
)

client = Connection("https://my_mi_server/mi_servicelayer", config).with_oidc().authorize().connect()

πŸ’» Which operating system are you using?

Windows

πŸ“€ Which ANSYS version are you using?

2024 R2

🐍 Which Python version are you using?

3.12

πŸ“¦ Installed packages

ansys-grantami-recordlists==1.2.1
ansys-grantami-serverapi-openapi==3.0.0
ansys-openapi-common==2.0.2
certifi==2024.7.4
cffi==1.17.0
charset-normalizer==3.3.2
cryptography==43.0.0
idna==3.7
jaraco.classes==3.4.0
keyring==24.3.1
more-itertools==10.4.0
pip-system-certs==4.0  <- Fixes the issue
pycparser==2.22
pyparsing==3.1.2
pypiwin32==223
pyspnego==0.11.1
python-dateutil==2.9.0.post0
pywin32==306
pywin32-ctypes==0.2.3
requests==2.32.3
requests-auth==7.0.0
requests-negotiate-sspi==0.5.2
requests_ntlm==1.3.0
six==1.16.0
sspilib==0.1.0
urllib3==2.2.2
wrapt==1.16.0
Andy-Grigg commented 2 weeks ago

We now recommend the use of pip-system-certs. Closing this ticket unless this approach is shown to be unsuitable.