diff --git a/changelogs/fragments/openssl_certificate_fix_has_expired.yml b/changelogs/fragments/openssl_certificate_fix_has_expired.yml new file mode 100644 index 0000000000..b33b2da9d4 --- /dev/null +++ b/changelogs/fragments/openssl_certificate_fix_has_expired.yml @@ -0,0 +1,2 @@ +bugfixes: + - openssl_certificate - ``has_expired`` correctly checks if the certificate is expired or not diff --git a/lib/ansible/modules/crypto/openssl_certificate.py b/lib/ansible/modules/crypto/openssl_certificate.py index cae9dc517d..fa06112cdc 100644 --- a/lib/ansible/modules/crypto/openssl_certificate.py +++ b/lib/ansible/modules/crypto/openssl_certificate.py @@ -229,7 +229,8 @@ options: has_expired: description: - - Checks if the certificate is expired/not expired at the time the module is executed. + - Checks if the certificate is expired/not expired at the time the module is executed. This only applies to + the C(assertonly) provider. type: bool default: no @@ -830,11 +831,18 @@ class AssertOnlyCertificate(Certificate): ) def _validate_has_expired(): - if self.has_expired: - if self.has_expired != self.cert.has_expired(): - self.message.append( - 'Certificate expiration check failed (certificate expiration is %s, expected %s)' % (self.cert.has_expired(), self.has_expired) - ) + # The following 3 lines are the same as the current PyOpenSSL code for cert.has_expired(). + # Older version of PyOpenSSL have a buggy implementation, + # to avoid issues with those we added the code from a more recent release here. + + time_string = to_native(self.cert.get_notAfter()) + not_after = datetime.datetime.strptime(time_string, "%Y%m%d%H%M%SZ") + cert_expired = not_after < datetime.datetime.utcnow() + + if self.has_expired != cert_expired: + self.message.append( + 'Certificate expiration check failed (certificate expiration is %s, expected %s)' % (cert_expired, self.has_expired) + ) def _validate_version(): if self.version: diff --git a/test/integration/targets/openssl_certificate/tasks/expired.yml b/test/integration/targets/openssl_certificate/tasks/expired.yml new file mode 100644 index 0000000000..d1a862812c --- /dev/null +++ b/test/integration/targets/openssl_certificate/tasks/expired.yml @@ -0,0 +1,39 @@ +--- +- name: Generate privatekey + openssl_privatekey: + path: '{{ output_dir }}/has_expired_privatekey.pem' + +- name: Generate CSR + openssl_csr: + path: '{{ output_dir }}/has_expired_csr.csr' + privatekey_path: '{{ output_dir }}/has_expired_privatekey.pem' + subject: + commonName: www.example.com + +- name: Generate expired selfsigned certificate + openssl_certificate: + path: '{{ output_dir }}/has_expired_cert.pem' + csr_path: '{{ output_dir }}/has_expired_csr.csr' + privatekey_path: '{{ output_dir }}/has_expired_privatekey.pem' + provider: selfsigned + selfsigned_digest: sha256 + selfsigned_not_after: "-1s" + +- name: "Check task fails because cert is expired (has_expired: false)" + openssl_certificate: + provider: assertonly + path: "{{ output_dir }}/has_expired_cert.pem" + has_expired: false + ignore_errors: true + register: expired_cert_check + +- name: Ensure previous task failed + assert: + that: expired_cert_check is failed + +- name: "Check expired cert check is ignored (has_expired: true)" + openssl_certificate: + provider: assertonly + path: "{{ output_dir }}/has_expired_cert.pem" + has_expired: true + register: expired_cert_skip diff --git a/test/integration/targets/openssl_certificate/tasks/main.yml b/test/integration/targets/openssl_certificate/tasks/main.yml index 8f846746c9..5cbb5ee4b9 100644 --- a/test/integration/targets/openssl_certificate/tasks/main.yml +++ b/test/integration/targets/openssl_certificate/tasks/main.yml @@ -1,6 +1,8 @@ --- - block: + - import_tasks: expired.yml + - import_tasks: selfsigned.yml - import_tasks: ownca.yml