community.general icon indicating copy to clipboard operation
community.general copied to clipboard

community.general.tss - SSL:CERTIFICATE_VERIFY_FAILED

Open ifelsefi opened this issue 2 years ago • 13 comments

Summary

I get SSL certificate verification error when trying to connect to my secret server. The CA is present in root bundle.

Issue Type

Bug Report

Component Name

tss

Ansible Version

ansible [core 2.11.5]
  config file = /home/admin/repos/ansible/ansible.cfg
  configured module search path = ['/home/admin/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.6/site-packages/ansible
  ansible collection location = /home/admin/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.6.8 (default, Nov 16 2020, 16:55:22) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
  jinja version = 3.0.2
  libyaml = True

Community.general Version


# /home/admin/.ansible/collections/ansible_collections
Collection        Version
----------------- -------
community.general 3.7.0

# /usr/local/lib/python3.6/site-packages/ansible_collections
Collection        Version
----------------- -------
community.general 3.7.0

Configuration

DEFAULT_FORKS(/home/admin/repos/ansible/ansible.cfg) = 100
DEFAULT_GATHERING(/home/admin/repos/ansible/ansible.cfg) = smart
DEFAULT_GATHER_SUBSET(/home/admin/repos/ansible/ansible.cfg) = ['all']
DEFAULT_GATHER_TIMEOUT(/home/admin/repos/ansible/ansible.cfg) = 5
DEFAULT_HOST_LIST(/home/admin/repos/ansible/ansible.cfg) = ['/home/admin/repos/ansible/hosts']
DEFAULT_LOG_PATH(/home/admin/repos/ansible/ansible.cfg) = /var/log/ansible.log
DEFAULT_NO_TARGET_SYSLOG(/home/admin/repos/ansible/ansible.cfg) = True
DEFAULT_POLL_INTERVAL(/home/admin/repos/ansible/ansible.cfg) = 2
DEFAULT_REMOTE_PORT(/home/admin/repos/ansible/ansible.cfg) = 22
DEFAULT_TIMEOUT(/home/admin/repos/ansible/ansible.cfg) = 5
DEFAULT_TRANSPORT(/home/admin/repos/ansible/ansible.cfg) = smart
HOST_KEY_CHECKING(/home/admin/repos/ansible/ansible.cfg) = False
INJECT_FACTS_AS_VARS(/home/admin/repos/ansible/ansible.cfg) = True
INVENTORY_ENABLED(/home/admin/repos/ansible/ansible.cfg) = ['ini', 'aws_ec2']
PERSISTENT_COMMAND_TIMEOUT(/home/admin/repos/ansible/ansible.cfg) = 30
PERSISTENT_CONNECT_TIMEOUT(/home/admin/repos/ansible/ansible.cfg) = 30

OS / Environment

CentOS 7.9

Steps to Reproduce

tss.yml:


--
- hosts: localhost
  become: no
  vars_prompt:
    - name: secret_server_username
      prompt: type you server server username
      private: yes
      default: ''
    - name: secret_server_password
      prompt: type you server server password
      private: yes
      default: ''
  vars:
   secret: >-
         {{
             lookup(
                 'community.general.tss',
                 3026,
                 base_url='https://secretserver',
                 username='{{ secret_server_username }}',
                 password='{{ secret_server_password }}',
                 validate_certs='False'
             )
         }}
  roles:
    - tss
    
...

roles/tss/main.yml:

---

- ansible.builtin.debug:
          msg: >
            the password is {{
              (secret['items']
                | items2dict(key_name='slug',
                             value_name='itemValue'))['password']
            }}
...

Expected Results

I should be able to authenticate with the server.

Actual Results


TASK [dhcp : ansible.builtin.debug] ****************************************************************************************************************************************************************************************************************************************
task path: /home/admin/repos/ansible/roles/dhcp/tasks/main.yml:3
Loading collection community.general from /home/ansible/collections/ansible_collections/community/general
Secret Server lookup of Secret with ID 3026
fatal: [localhost]: FAILED! => {
    "msg": "An unhandled exception occurred while templating '{{\n    lookup(\n        'community.general.tss',\n        3026,\n        base_url='https://secretserver',\n        username='{{ secret_server_username }}',\n        password='{{ secret_server_password }}',\n        validate_certs='False'\n    )\n}}'. Error was a <class 'ansible.errors.AnsibleError'>, original message: An unhandled exception occurred while running the lookup plugin 'community.general.tss'. Error was a <class 'requests.exceptions.SSLError'>, original message: HTTPSConnectionPool(host='secretserver', port=443): Max retries exceeded with url: /app/oauth2/token (Caused by SSLError(SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:877)'),))"
}

curl works fine:


admin@server[~/repos/ansible]$ curl -vvv https://secretserver
* About to connect() to secretserver port 443 (#0)
*   Trying 10.1.64.182...
* Connected to secretserver (10.1.64.182) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
* Server certificate:
*       subject: CN=THECN
*       start date: Apr 04 20:52:57 2017 GMT
*       expire date: Mar 15 20:09:03 2022 GMT
*       common name: THECN
*       issuer: CN=THECA
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: secretserver
> Accept: */*

As does openssl:


admin@server[~/repos/ansible]$ openssl s_client -connect secretserver:443 | grep ok
depth=1 DC = com, DC = domain, CN = THECN
verify return:1
depth=0 C = US, ST = blah, L = blah, O = blah, OU = IT, CN = THECN
verify return:1
    Verify return code: 0 (ok)

Code of Conduct

  • [X] I agree to follow the Ansible Code of Conduct

ifelsefi avatar Oct 05 '21 15:10 ifelsefi

Files identified in the description:

If these files are incorrect, please update the component name section of the description or use the !component bot command.

click here for bot help

ansibullbot avatar Oct 05 '21 15:10 ansibullbot

cc @amigus @endlesstrax click here for bot help

ansibullbot avatar Oct 05 '21 15:10 ansibullbot

The CA cert is present in what root bundle?

Is there a certain root bundle that ansible is supposed to check?

Are lookup plugins expected to accept 'validate_certs'?

amigus avatar Oct 07 '21 04:10 amigus

Is there a certain root bundle that ansible is supposed to check?

I don't think so. What modules/plugins sometimes provide is an option which allows to specify a root CA certificate. Whether they do so usually depends on which underlying library is used for connecting to the service (and whether that one allows to specify one).

Are lookup plugins expected to accept 'validate_certs'?

The option parser is permissive with options that are not documented, it basically ignores them (instead of reporting an error that the option is not supported). Since the lookup does not claim to support that option, there's no reason why it should react to it.

(Whether it's a good idea to implement such an option is always a good question - such options are unfortunately often abused to work around certificate problems instead of properly solving them, decreasing security by quite a lot. This should only ever be used if the service accessed is running on the same machine, or through a network which doesn't allow interception by third parties.)

felixfontein avatar Oct 07 '21 05:10 felixfontein

In that case, I don't think any changes are needed. Please use the method supported by the underlying requests library to trust the CA cert.

amigus avatar Oct 08 '21 05:10 amigus

Requests allows to provide a CA cert in a call with the verify parameter (https://docs.python-requests.org/en/latest/user/advanced/#ssl-cert-verification), but that's not exposed in this plugin. Having an option for that is probably a good idea in the long run.

felixfontein avatar Oct 08 '21 05:10 felixfontein

Request also supports setting the environment variable REQUESTS_CA_BUNDLE. That will negate the need to use the verify argument in the code, which seems like a more sensible solution, as this is an environment issue.

This list of trusted CAs can also be specified through the REQUESTS_CA_BUNDLE environment variable. If REQUESTS_CA_BUNDLE is not set, CURL_CA_BUNDLE will be used as fallback. (https://docs.python-requests.org/en/latest/user/advanced/#ssl-cert-verification)

EndlessTrax avatar Oct 08 '21 12:10 EndlessTrax

If Ansible were to expose verify so that we can piggyback, that's fine but, I don't see why a plugin should expose its underlying library so that users can avoid setting an environment variable to solve (per @EndlessTrax) an environment problem.

amigus avatar Oct 09 '21 04:10 amigus

@amigus Ansible does not use requests. Also this isn't about exposing the underlying library, but to provide an API that uses the underlying library to achieve common tasks, like proving a CA certificate to use for validation. If the plugin does not provide such an option, you are basically forcing users to try to find out what the underlying library does to try to achieve the correct behavior.

In this case, python-tss-sdk would need to provide such an option (and pass it on to the library it uses, requests), so that the plugin can tell python-tss-sdk what the user wants to do.

felixfontein avatar Oct 09 '21 08:10 felixfontein

Thank you everyone.

I would expect Ansible and the libraries it leverages to use the system default CA store which would be /etc/pki/tls/certs/ca-bundle.crt. This would make it unnecessary to specify extra environmental variables. If I don't need it with curl or openssl then why should Ansible force users to add that step? If the file exists already it should be tried automatically.

ifelsefi avatar Oct 09 '21 17:10 ifelsefi

the system default CA store which would be /etc/pki/tls/certs/ca-bundle.crt.

I'm not sure what kind of system you're using, but neither on Arch Linux nor Debian /etc/pki/tls/ even exists. So this is not the "system default CA store" on Linux.

You can run python -c "import ssl; print(ssl.get_default_verify_paths())" to find out where Python by default find its CA certs. That will also be what Ansible uses. Requests (a third-party library that is used by the TSS SDK to do HTTPS requests) might ignore that store though, and rely only on certifi (https://pypi.org/project/certifi/), yet another root certificate store.

There's not much Ansible can do when plugins / modues decide to use requests (or are forced to use it since third-party libraries they use use it), except trying to offer a way to provide the CA cert as a module/plugin option.

felixfontein avatar Oct 10 '21 13:10 felixfontein

Likewise, I don't see why an individual Ansible plugin should couple itself to a particular location for a CA certificate bundle nor do I see why it should couple itself to a particular HTTP library by exposing that library's features.

Also, as @EndlessTrax said, this is an environmental issue and that's what environment variables are for.

amigus avatar Oct 10 '21 21:10 amigus

cc @delineaKrehl @tylerezimmerman click here for bot help

ansibullbot avatar Jun 23 '22 23:06 ansibullbot