ansible-community/ara

api_ca does not default to system cert bundle

nlvw opened this issue · 9 comments

nlvw commented

What is the issue ?

When using ARA 1.6.0 with a HTTPS API Server the callback plugin will fail due to a certificate error. It seems like it is not checking the default cert bundle on they system (Fedora 37 in my case) as the following fixes the error

[ara]
api_client = http
api_server = https://ara.example.com
api_ca = /etc/pki/tls/cert.pem

Without api_ca the connection fails with

[WARNING]: Failure using method (v2_playbook_on_start) in callback plugin
(<ansible.plugins.callback.ara_default.CallbackModule object at 0x7f4b47a812d0>):
HTTPSConnectionPool(host='ara.nmsu.edu', port=443): Max retries exceeded with url:
/api/v1/playbooks (Caused by SSLError(SSLCertVerificationError(1, '[SSL:
CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer
certificate (_ssl.c:992)')))

PLAY [Test Ansible Connection] ************************************************************
[WARNING]: Failure using method (v2_playbook_on_play_start) in callback plugin
(<ansible.plugins.callback.ara_default.CallbackModule object at 0x7f4b47a812d0>):
'NoneType' object is not subscriptable

TASK [connection test] ********************************************************************
[WARNING]: Failure using method (v2_playbook_on_task_start) in callback plugin
(<ansible.plugins.callback.ara_default.CallbackModule object at 0x7f4b47a812d0>):
'NoneType' object is not subscriptable
ok: [hpc-wiki-p]

TASK [stop ssh client persistant connection] **********************************************

PLAY RECAP ********************************************************************************
hpc-wiki-p                 : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[WARNING]: Failure using method (v2_playbook_on_stats) in callback plugin
(<ansible.plugins.callback.ara_default.CallbackModule object at 0x7f4b47a812d0>):
'NoneType' object is not subscriptable

What should be happening ?

The default ca bundle trusted by the system should be used by default when api_ca is not specified

Software Versions

Ansible = 7.1.0 (Core = 2.14)
OS = Fedora 37
Python = 3.11
ARA = 1.6.0
Install Source = pip

Hi @nlvw and thanks for the issue.

Unless mistaken, the client doesn't specify a path to a CA bundle at all (even a default one) when doing requests unless there is one provided via the configuration so I would expect that to come from python or the requests library.

Out of curiosity, could you use pure python requests to see if it also gets a SSL error?

Something like this (replacing the demo URL with your own) should be sufficient:

> python3 -c 'import requests; print(requests.get("https://demo.recordsansible.org/api/v1/").json())'
{'labels': 'https://demo.recordsansible.org/api/v1/labels', 'playbooks': 'https://demo.recordsansible.org/api/v1/playbooks', 'plays': 'https://demo.recordsansible.org/api/v1/plays', 'tasks': 'https://demo.recordsansible.org/api/v1/tasks', 'hosts': 'https://demo.recordsansible.org/api/v1/hosts', 'latesthosts': 'https://demo.recordsansible.org/api/v1/latesthosts', 'results': 'https://demo.recordsansible.org/api/v1/results', 'files': 'https://demo.recordsansible.org/api/v1/files', 'records': 'https://demo.recordsansible.org/api/v1/records'}

For what it's worth, I am also running on Fedora and do not need to specify ``api_ca` when using the demo and it works with a letsencrypt cert.

I know there's an env variable to set different bundle paths but that shouldn't be necessary.

@dmsimard I guess in your case it's not a self signed certificate. As soon as you are using a self signed certificate you have to give the path to the system ca bundle to the Python requests library, e.g. by simple setting REQUESTS_CA_BUNDLE.
Have to do the same for my Ara installations.

And the path differ between e.g. Ubuntu and Fedora. Thus hard to set a default there.

@hille721 I don't have bandwidth to try and reproduce right now but we have a working integration test to validate that it works for self signed certificates here: https://github.com/ansible-community/ara-collection/pull/57/files and it looks sufficient to specify api_ca.

It hasn't run in a while but I will trigger it to get some fresh results.

I can try and investigate later but in the meantime the job returned successfully: https://ansible.softwarefactory-project.io/zuul/build/50e09cec38da4fafbc5dc57136d8f1fe

Don't get me wrong. Don't see this as a bug or problem. We need to set REQUEST_CA_BUNDLE not only for Ara but for all our Python tools in which the requests library is used and there we have a lot. Thus not a problem for me and that's why I won't spend time for further investigation.

And just to clarify, we don't set any certificate / ca-bundle in the Ara config, but ONLY the REQUEST_CA_BUNDLE environment variable.

So either you have to set api_ca in Ara config which will pass this to requests as verify argument OR you can directly specify the REQUEST_CA_BUNDLE env variable without special ara config. Which makes sense if you are having more tools which are using the requests library

Using requests with not self signed url:

(.venv) $  python3 -c 'import requests; print(requests.get("https://demo.recordsansible.org/api/v1/").json())'
{'labels': 'https://demo.recordsansible.org/api/v1/labels', 'playbooks': 'https://demo.recordsansible.org/api/v1/playbooks', 'plays': 'https://demo.recordsansible.org/api/v1/plays', 'tasks': 'https://demo.recordsansible.org/api/v1/tasks', 'hosts': 'https://demo.recordsansible.org/api/v1/hosts', 'latesthosts': 'https://demo.recordsansible.org/api/v1/latesthosts', 'results': 'https://demo.recordsansible.org/api/v1/results', 'files': 'https://demo.recordsansible.org/api/v1/files', 'records': 'https://demo.recordsansible.org/api/v1/records'}

Using requests with self signed certificate, wont't work

(.venv) $  python3 -c 'import requests; print(requests.get("https://raspi4.fritz.box:9090/"))'
Traceback (most recent call last):
  File "/home/hille/.venv/lib64/python3.10/site-packages/urllib3/connectionpool.py", line 703, in urlopen
    httplib_response = self._make_request(
  File "/home/hille/.venv/lib64/python3.10/site-packages/urllib3/connectionpool.py", line 386, in _make_request
    self._validate_conn(conn)
  File "/home/hille/.venv/lib64/python3.10/site-packages/urllib3/connectionpool.py", line 1042, in _validate_conn
    conn.connect()
  File "/home/hille/.venv/lib64/python3.10/site-packages/urllib3/connection.py", line 414, in connect
    self.sock = ssl_wrap_socket(
  File "/home/hille/.venv/lib64/python3.10/site-packages/urllib3/util/ssl_.py", line 449, in ssl_wrap_socket
    ssl_sock = _ssl_wrap_socket_impl(
  File "/home/hille/.venv/lib64/python3.10/site-packages/urllib3/util/ssl_.py", line 493, in _ssl_wrap_socket_impl
    return ssl_context.wrap_socket(sock, server_hostname=server_hostname)
  File "/usr/lib64/python3.10/ssl.py", line 513, in wrap_socket
    return self.sslsocket_class._create(
  File "/usr/lib64/python3.10/ssl.py", line 1071, in _create
    self.do_handshake()
  File "/usr/lib64/python3.10/ssl.py", line 1342, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:997)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/hille/.venv/lib64/python3.10/site-packages/requests/adapters.py", line 489, in send
    resp = conn.urlopen(
  File "/home/hille/.venv/lib64/python3.10/site-packages/urllib3/connectionpool.py", line 787, in urlopen
    retries = retries.increment(
  File "/home/hille/.venv/lib64/python3.10/site-packages/urllib3/util/retry.py", line 592, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='raspi4.fritz.box', port=9090): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:997)')))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/hille/.venv/lib64/python3.10/site-packages/requests/api.py", line 73, in get
    return request("get", url, params=params, **kwargs)
  File "/home/hille/.venv/lib64/python3.10/site-packages/requests/api.py", line 59, in request
    return session.request(method=method, url=url, **kwargs)
  File "/home/hille/.venv/lib64/python3.10/site-packages/requests/sessions.py", line 587, in request
    resp = self.send(prep, **send_kwargs)
  File "/home/hille/.venv/lib64/python3.10/site-packages/requests/sessions.py", line 701, in send
    r = adapter.send(request, **kwargs)
  File "/home/hille/.venv/lib64/python3.10/site-packages/requests/adapters.py", line 563, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='raspi4.fritz.box', port=9090): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:997)')))

Simple set REQUESTS_CA_BUNDLE and it will work:

(.venv) $  REQUESTS_CA_BUNDLE=/etc/pki/tls/certs/ca-bundle.crt python3 -c 'import requests; print(requests.get("https://raspi4.fritz.box:9090/"))'
<Response [200]>

tested with Fedora 36 and a Virtual Environment.

Fun fact: If I'm using not a virtual environment, but the system Python it will work without setting REQUESTS_CA_BUNDLE.

$ echo $REQUESTS_CA_BUNDLE

$ python3 -c 'import requests; print(requests.get("https://raspi4.fritz.box:9090/"))'
<Response [200]>
$ which python3
/usr/bin/python3

Yes, it's a common enough issue that I have seen with python things before but I had not noticed it with ara (yet?).

I suppose since this is a known issue we should add a little something to the troubleshooting docs: https://github.com/ansible-community/ara/blob/master/doc/source/troubleshooting.rst

I'll leave this issue opened in the meantime.