-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
certificate_complete_chain: handle duplicate intermediate subjects (#403
) (#405) * Allow multiple intermediate CAs to have same subject. * Add tests. * Fix test name. * Don't use CN for SAN. * Make a bit more compatible. * Include jinja2 compat for CentOS 6. (cherry picked from commit 11a1454) Co-authored-by: Felix Fontein <felix@fontein.de>
- Loading branch information
1 parent
d19faa1
commit 2aa38fe
Showing
8 changed files
with
272 additions
and
141 deletions.
There are no files selected for viewing
2 changes: 2 additions & 0 deletions
2
changelogs/fragments/403-certificate_complete_chain-same-subject.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
bugfixes: | ||
- "certificate_complete_chain - allow multiple potential intermediate certificates to have the same subject (https://github.com/ansible-collections/community.crypto/issues/399, https://github.com/ansible-collections/community.crypto/pull/403)." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
tests/integration/targets/certificate_complete_chain/meta/main.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
dependencies: | ||
- prepare_jinja2_compat | ||
- setup_openssl | ||
- setup_remote_tmp_dir |
20 changes: 20 additions & 0 deletions
20
tests/integration/targets/certificate_complete_chain/tasks/create-single-certificate.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#################################################################### | ||
# WARNING: These are designed specifically for Ansible tests # | ||
# and should not be used as examples of how to write Ansible roles # | ||
#################################################################### | ||
|
||
- name: Generate CSR for {{ certificate.name }} | ||
openssl_csr: | ||
path: '{{ remote_tmp_dir }}/{{ certificate.name }}.csr' | ||
privatekey_path: '{{ remote_tmp_dir }}/{{ certificate.name }}.key' | ||
subject: '{{ certificate.subject }}' | ||
useCommonNameForSAN: false | ||
|
||
- name: Generate certificate for {{ certificate.name }} | ||
x509_certificate: | ||
path: '{{ remote_tmp_dir }}/{{ certificate.name }}.pem' | ||
csr_path: '{{ remote_tmp_dir }}/{{ certificate.name }}.csr' | ||
privatekey_path: '{{ remote_tmp_dir }}/{{ certificate.name }}.key' | ||
provider: '{{ "selfsigned" if certificate.parent is not defined else "ownca" }}' | ||
ownca_path: '{{ (remote_tmp_dir ~ "/" ~ certificate.parent ~ ".pem") if certificate.parent is defined else omit }}' | ||
ownca_privatekey_path: '{{ (remote_tmp_dir ~ "/" ~ certificate.parent ~ ".key") if certificate.parent is defined else omit }}' |
49 changes: 49 additions & 0 deletions
49
tests/integration/targets/certificate_complete_chain/tasks/create.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
#################################################################### | ||
# WARNING: These are designed specifically for Ansible tests # | ||
# and should not be used as examples of how to write Ansible roles # | ||
#################################################################### | ||
|
||
- block: | ||
- name: Create private keys | ||
openssl_privatekey: | ||
path: '{{ remote_tmp_dir }}/{{ item.name }}.key' | ||
size: '{{ default_rsa_key_size_certifiates }}' | ||
loop: '{{ certificates }}' | ||
|
||
- name: Generate certificates | ||
include_tasks: create-single-certificate.yml | ||
loop: '{{ certificates }}' | ||
loop_control: | ||
loop_var: certificate | ||
|
||
- name: Read certificates | ||
slurp: | ||
src: '{{ remote_tmp_dir }}/{{ item.name }}.pem' | ||
loop: '{{ certificates }}' | ||
register: certificates_read | ||
|
||
- name: Store read certificates | ||
set_fact: | ||
read_certificates: >- | ||
{{ certificates_read.results | map(attribute='content') | map('b64decode') | ||
| zip(certificates | map(attribute='name')) | ||
| list | ||
| items2dict(key_name=1, value_name=0) }} | ||
vars: | ||
certificates: | ||
- name: a-root | ||
subject: | ||
commonName: root common name | ||
- name: b-intermediate | ||
subject: | ||
commonName: intermediate common name | ||
parent: a-root | ||
- name: c-intermediate | ||
subject: | ||
commonName: intermediate common name | ||
parent: a-root | ||
- name: d-leaf | ||
subject: | ||
commonName: leaf certificate | ||
parent: b-intermediate |
44 changes: 44 additions & 0 deletions
44
tests/integration/targets/certificate_complete_chain/tasks/created.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#################################################################### | ||
# WARNING: These are designed specifically for Ansible tests # | ||
# and should not be used as examples of how to write Ansible roles # | ||
#################################################################### | ||
|
||
- name: Case A => works | ||
certificate_complete_chain: | ||
input_chain: "{{ read_certificates['d-leaf'] }}" | ||
intermediate_certificates: | ||
- '{{ remote_tmp_dir }}/b-intermediate.pem' | ||
root_certificates: | ||
- '{{ remote_tmp_dir }}/a-root.pem' | ||
|
||
- name: Case B => doesn't work, but this is expected | ||
failed_when: no | ||
register: caseb | ||
certificate_complete_chain: | ||
input_chain: "{{ read_certificates['d-leaf'] }}" | ||
intermediate_certificates: | ||
- '{{ remote_tmp_dir }}/c-intermediate.pem' | ||
root_certificates: | ||
- '{{ remote_tmp_dir }}/a-root.pem' | ||
|
||
- name: Assert that case B failed | ||
assert: | ||
that: "'Cannot complete chain' in caseb.msg" | ||
|
||
- name: Case C => works | ||
certificate_complete_chain: | ||
input_chain: "{{ read_certificates['d-leaf'] }}" | ||
intermediate_certificates: | ||
- '{{ remote_tmp_dir }}/c-intermediate.pem' | ||
- '{{ remote_tmp_dir }}/b-intermediate.pem' | ||
root_certificates: | ||
- '{{ remote_tmp_dir }}/a-root.pem' | ||
|
||
- name: Case D => works as well after PR 403 | ||
certificate_complete_chain: | ||
input_chain: "{{ read_certificates['d-leaf'] }}" | ||
intermediate_certificates: | ||
- '{{ remote_tmp_dir }}/b-intermediate.pem' | ||
- '{{ remote_tmp_dir }}/c-intermediate.pem' | ||
root_certificates: | ||
- '{{ remote_tmp_dir }}/a-root.pem' |
144 changes: 144 additions & 0 deletions
144
tests/integration/targets/certificate_complete_chain/tasks/existing.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
#################################################################### | ||
# WARNING: These are designed specifically for Ansible tests # | ||
# and should not be used as examples of how to write Ansible roles # | ||
#################################################################### | ||
|
||
- block: | ||
- name: Find root for cert 1 using directory | ||
certificate_complete_chain: | ||
input_chain: '{{ fullchain | trim }}' | ||
root_certificates: | ||
- '{{ remote_tmp_dir }}/files/roots/' | ||
register: cert1_root | ||
- name: Verify root for cert 1 | ||
assert: | ||
that: | ||
- cert1_root.complete_chain | join('') == (fullchain ~ root) | ||
- cert1_root.root == root | ||
vars: | ||
fullchain: "{{ lookup('file', 'cert1-fullchain.pem', rstrip=False) }}" | ||
root: "{{ lookup('file', 'cert1-root.pem', rstrip=False) }}" | ||
|
||
- block: | ||
- name: Find rootchain for cert 1 using intermediate and root PEM | ||
certificate_complete_chain: | ||
input_chain: '{{ cert }}' | ||
intermediate_certificates: | ||
- '{{ remote_tmp_dir }}/files/cert1-chain.pem' | ||
root_certificates: | ||
- '{{ remote_tmp_dir }}/files/roots.pem' | ||
register: cert1_rootchain | ||
- name: Verify rootchain for cert 1 | ||
assert: | ||
that: | ||
- cert1_rootchain.complete_chain | join('') == (cert ~ chain ~ root) | ||
- cert1_rootchain.chain[:-1] | join('') == chain | ||
- cert1_rootchain.root == root | ||
vars: | ||
cert: "{{ lookup('file', 'cert1.pem', rstrip=False) }}" | ||
chain: "{{ lookup('file', 'cert1-chain.pem', rstrip=False) }}" | ||
root: "{{ lookup('file', 'cert1-root.pem', rstrip=False) }}" | ||
|
||
- block: | ||
- name: Find root for cert 2 using directory | ||
certificate_complete_chain: | ||
input_chain: "{{ fullchain | trim }}" | ||
root_certificates: | ||
- '{{ remote_tmp_dir }}/files/roots/' | ||
register: cert2_root | ||
- name: Verify root for cert 2 | ||
assert: | ||
that: | ||
- cert2_root.complete_chain | join('') == (fullchain ~ root) | ||
- cert2_root.root == root | ||
vars: | ||
fullchain: "{{ lookup('file', 'cert2-fullchain.pem', rstrip=False) }}" | ||
root: "{{ lookup('file', 'cert2-root.pem', rstrip=False) }}" | ||
|
||
- block: | ||
- name: Find rootchain for cert 2 using intermediate and root PEM | ||
certificate_complete_chain: | ||
input_chain: '{{ cert }}' | ||
intermediate_certificates: | ||
- '{{ remote_tmp_dir }}/files/cert2-chain.pem' | ||
root_certificates: | ||
- '{{ remote_tmp_dir }}/files/roots.pem' | ||
register: cert2_rootchain | ||
- name: Verify rootchain for cert 2 | ||
assert: | ||
that: | ||
- cert2_rootchain.complete_chain | join('') == (cert ~ chain ~ root) | ||
- cert2_rootchain.chain[:-1] | join('') == chain | ||
- cert2_rootchain.root == root | ||
vars: | ||
cert: "{{ lookup('file', 'cert2.pem', rstrip=False) }}" | ||
chain: "{{ lookup('file', 'cert2-chain.pem', rstrip=False) }}" | ||
root: "{{ lookup('file', 'cert2-root.pem', rstrip=False) }}" | ||
|
||
- block: | ||
- name: Find alternate rootchain for cert 2 using intermediate and root PEM | ||
certificate_complete_chain: | ||
input_chain: '{{ cert }}' | ||
intermediate_certificates: | ||
- '{{ remote_tmp_dir }}/files/cert2-altchain.pem' | ||
root_certificates: | ||
- '{{ remote_tmp_dir }}/files/roots.pem' | ||
register: cert2_rootchain_alt | ||
- name: Verify rootchain for cert 2 | ||
assert: | ||
that: | ||
- cert2_rootchain_alt.complete_chain | join('') == (cert ~ chain ~ root) | ||
- cert2_rootchain_alt.chain[:-1] | join('') == chain | ||
- cert2_rootchain_alt.root == root | ||
vars: | ||
cert: "{{ lookup('file', 'cert2.pem', rstrip=False) }}" | ||
chain: "{{ lookup('file', 'cert2-altchain.pem', rstrip=False) }}" | ||
root: "{{ lookup('file', 'cert2-altroot.pem', rstrip=False) }}" | ||
|
||
- block: | ||
- name: Find alternate rootchain for cert 2 when complete chain is already presented to the module | ||
certificate_complete_chain: | ||
input_chain: '{{ cert ~ chain ~ root }}' | ||
root_certificates: | ||
- '{{ remote_tmp_dir }}/files/roots.pem' | ||
register: cert2_complete_chain | ||
- name: Verify rootchain for cert 2 | ||
assert: | ||
that: | ||
- cert2_complete_chain.complete_chain | join('') == (cert ~ chain ~ root) | ||
- cert2_complete_chain.chain == [] | ||
- cert2_complete_chain.root == root | ||
vars: | ||
cert: "{{ lookup('file', 'cert2.pem', rstrip=False) }}" | ||
chain: "{{ lookup('file', 'cert2-altchain.pem', rstrip=False) }}" | ||
root: "{{ lookup('file', 'cert2-altroot.pem', rstrip=False) }}" | ||
|
||
- name: Check failure when no intermediate certificate can be found | ||
certificate_complete_chain: | ||
input_chain: '{{ lookup("file", "cert2.pem", rstrip=True) }}' | ||
intermediate_certificates: | ||
- '{{ remote_tmp_dir }}/files/cert1-chain.pem' | ||
root_certificates: | ||
- '{{ remote_tmp_dir }}/files/roots.pem' | ||
register: cert2_no_intermediate | ||
ignore_errors: true | ||
- name: Verify failure | ||
assert: | ||
that: | ||
- cert2_no_intermediate is failed | ||
- "cert2_no_intermediate.msg.startswith('Cannot complete chain. Stuck at certificate ')" | ||
|
||
- name: Check failure when infinite loop is found | ||
certificate_complete_chain: | ||
input_chain: '{{ lookup("file", "cert2-fullchain.pem", rstrip=True) }}' | ||
intermediate_certificates: | ||
- '{{ remote_tmp_dir }}/files/roots.pem' | ||
root_certificates: | ||
- '{{ remote_tmp_dir }}/files/cert1-chain.pem' | ||
register: cert2_infinite_loop | ||
ignore_errors: true | ||
- name: Verify failure | ||
assert: | ||
that: | ||
- cert2_infinite_loop is failed | ||
- "cert2_infinite_loop.msg == 'Found cycle while building certificate chain'" |
Oops, something went wrong.