diff --git a/changelogs/fragments/71979_ca_path_for_uri.yaml b/changelogs/fragments/71979_ca_path_for_uri.yaml new file mode 100644 index 00000000000..fd8da76ac9f --- /dev/null +++ b/changelogs/fragments/71979_ca_path_for_uri.yaml @@ -0,0 +1,2 @@ +minor_changes: + - uri - add ``ca_path`` argument to allow specification of a CA certificate (https://github.com/ansible/ansible/pull/71979). diff --git a/lib/ansible/module_utils/urls.py b/lib/ansible/module_utils/urls.py index b4cdd47342c..632387f6728 100644 --- a/lib/ansible/module_utils/urls.py +++ b/lib/ansible/module_utils/urls.py @@ -923,9 +923,7 @@ class SSLValidationHandler(urllib_request.BaseHandler): to_native(f.read(), errors='surrogate_or_strict') ) ) - else: - ca_certs.append(f.read()) - return ca_certs, cadata, paths_checked + return self.ca_path, cadata, paths_checked if not HAS_SSLCONTEXT: paths_checked.append('/etc/ssl/certs') diff --git a/lib/ansible/modules/uri.py b/lib/ansible/modules/uri.py index 0e8ad802b22..c21c45f213c 100644 --- a/lib/ansible/modules/uri.py +++ b/lib/ansible/modules/uri.py @@ -141,6 +141,11 @@ options: - If I(client_cert) contains both the certificate and key, this option is not required. type: path version_added: '2.4' + ca_path: + description: + - PEM formatted file that contains a CA certificate to be used for validation + type: path + version_added: '2.11' src: description: - Path to file to be submitted to the remote server. @@ -548,13 +553,12 @@ def form_urlencoded(body): return body -def uri(module, url, dest, body, body_format, method, headers, socket_timeout): +def uri(module, url, dest, body, body_format, method, headers, socket_timeout, ca_path): # is dest is set and is a directory, let's check if we get redirected and # set the filename from that url redirected = False redir_info = {} r = {} - src = module.params['src'] if src: try: @@ -594,6 +598,7 @@ def uri(module, url, dest, body, body_format, method, headers, socket_timeout): resp, info = fetch_url(module, url, data=data, headers=headers, method=method, timeout=socket_timeout, unix_socket=module.params['unix_socket'], + ca_path=ca_path, **kwargs) try: @@ -636,6 +641,7 @@ def main(): headers=dict(type='dict', default={}), unix_socket=dict(type='path'), remote_src=dict(type='bool', default=False), + ca_path=dict(type='path', default=None), ) module = AnsibleModule( @@ -658,7 +664,7 @@ def main(): removes = module.params['removes'] status_code = [int(x) for x in list(module.params['status_code'])] socket_timeout = module.params['timeout'] - + ca_path = module.params['ca_path'] dict_headers = module.params['headers'] if not re.match('^[A-Z]+$', method): @@ -702,7 +708,7 @@ def main(): # Make the request start = datetime.datetime.utcnow() resp, content, dest = uri(module, url, dest, body, body_format, method, - dict_headers, socket_timeout) + dict_headers, socket_timeout, ca_path) resp['elapsed'] = (datetime.datetime.utcnow() - start).seconds resp['status'] = int(resp['status']) resp['changed'] = False diff --git a/test/integration/targets/uri/tasks/main.yml b/test/integration/targets/uri/tasks/main.yml index 2297f6f189d..4cefc6b3095 100644 --- a/test/integration/targets/uri/tasks/main.yml +++ b/test/integration/targets/uri/tasks/main.yml @@ -131,6 +131,48 @@ - "stat_result.stat.exists == true" - "result.changed == true" +- name: "get ca certificate {{ self_signed_host }}" + get_url: + url: "http://{{ httpbin_host }}/ca2cert.pem" + dest: "{{ remote_tmp_dir }}/ca2cert.pem" + +- name: test https fetch to a site with self signed certificate using ca_path + uri: + url: "https://{{ self_signed_host }}:444/" + dest: "{{ output_dir }}/self-signed_using_ca_path.html" + ca_path: "{{ remote_tmp_dir }}/ca2cert.pem" + validate_certs: yes + register: result + +- stat: + path: "{{ output_dir }}/self-signed_using_ca_path.html" + register: stat_result + +- name: Assert that the file was downloaded + assert: + that: + - "stat_result.stat.exists == true" + - "result.changed == true" + +- name: test https fetch to a site with self signed certificate without using ca_path + uri: + url: "https://{{ self_signed_host }}:444/" + dest: "{{ output_dir }}/self-signed-without_using_ca_path.html" + validate_certs: yes + register: result + ignore_errors: true + +- stat: + path: "{{ output_dir }}/self-signed-without_using_ca_path.html" + register: stat_result + +- name: Assure that https access to a host with self-signed certificate without providing ca_path fails + assert: + that: + - "stat_result.stat.exists == false" + - result is failed + - "'certificate verify failed' in result.msg" + - name: test redirect without follow_redirects uri: url: 'https://{{ httpbin_host }}/redirect/2'