Compare commits

...

134 Commits

Author SHA1 Message Date
Felix Fontein
9e367e1d42 Release 1.9.23. 2023-10-29 15:32:31 +01:00
Felix Fontein
cb747236d9 [stable-1] openssl_pkcs12: handle pyOpenSSL 23.3.0, which removed PKCS#12 support (#668)
* Handle pyOpenSSL 23.3.0, which removed PKCS#12 support (at least partially). (#666)

(cherry picked from commit d1299c11d6)

* Try to fix FreeBSD 13.1 failures in CI.
2023-10-28 22:13:20 +02:00
patchback[bot]
b73bd91783 Fix Galaxy URLs. (#658) (#659)
(cherry picked from commit 5f4fc95c50)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-09-30 22:48:50 +02:00
Felix Fontein
21632bf044 Bump FreeBSD versions on stable-2.13. (#650) 2023-08-15 07:14:35 +02:00
patchback[bot]
a836169da4 Bump AZP container. (#629) (#630)
(cherry picked from commit b40a1c54f7)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-06-24 16:29:51 +02:00
Felix Fontein
ce93a9a2db Next expected release is 1.9.23. 2023-06-15 13:25:53 +02:00
Felix Fontein
52407bd8d8 Release 1.9.22. 2023-06-15 13:01:33 +02:00
patchback[bot]
9ef079efab Fix example. (#620) (#621)
(cherry picked from commit a7e9bb7618)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-06-09 12:58:39 +02:00
Felix Fontein
afd2bd3bad Move ansible-core 2.12 to EOL CI (#609) (#610)
* https://github.com/ansible/ansible/pull/79734 has been merged and backported for all branches but stable-2.10 and stable-2.11.

* Move ansible-core 2.12 to EOL CI.

(cherry picked from commit 0d30a3793a)
2023-05-29 18:33:26 +02:00
Felix Fontein
022b011a90 Switch to Ansible Galaxy compatible requirements files for tests. (#607) (#608)
(cherry picked from commit e3bc22f7d5)
2023-05-21 14:38:13 +02:00
patchback[bot]
7a1494cbe4 Always generate a new key pair if the private key doesn't exist (#598) (#599)
* Always generate a new key pair if the private key doesn't exist (#597)

This commit updates `KeypairBackend._should_generate()` to first check
if the original private key named by the `path` argument exists, and
return True if it does not. This brings the code in line with
the documentation, which says that a new key will always be generated if
the key file doesn't already exist.

As an alternative to the approach implemented here, I also considered
only modifying the condition in the `fail` branch of the if statement,
but I thought that would not map as cleanly to the behavior specified in
the documentation, so doing it the way I did should make it easier to
check that the code is doing the right thing just by looking at it.
I also considered doing something to make the logic more similar to
`PrivateKeyBackend.needs_regeneration()` (the openssl version of this
functionality), because the two are supposed to be acting the same way,
but I thought that'd be going beyond the scope of just fixing this bug.
If it'd be useful to make both methods work the same way, someone can
refactor the code in a future commit.

* Test different regenerate values with nonexistent keys

This commit changes the test task that generates new keys to use each of
the different values for the `regenerate` argument, which will ensure
that the module is capable of generating a key when no previous key
exists regardless of the value of `regenerate`. Previously, the task
would always run with the `partial_idempotence` value, and that obscured
a bug (#597) that would occur when it was set to `fail`. The bug was
fixed in the previous commit.

(cherry picked from commit ce3299f106)

Co-authored-by: David Zaslavsky <diazona@ellipsix.net>
2023-05-01 21:34:10 +02:00
Felix Fontein
2b98c0b250 The next release will be 1.9.22. 2023-04-16 20:07:23 +02:00
Felix Fontein
f10504e95f Release 1.9.21. 2023-04-16 19:47:19 +02:00
patchback[bot]
7fbe649dc6 Do extra docs validation; explicitly disallow semantic markup in docs (#593) (#594)
* Do extra docs validation. Explicitly disallow semantic markup in docs.

* Forgot to add new requirement.

* Improve test.

* TEMP - make CI fail.

* Revert "TEMP - make CI fail."

This reverts commit a71b8901c1.

* Remove unnecessary import.

* Make sure ANSIBLE_COLLECTIONS_PATH is set.

* Make sure sanity tests from older Ansible versions don't complain.

(cherry picked from commit ceabef7e58)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-16 18:55:26 +02:00
Felix Fontein
11e7232bd6 For some reason some 2.9 tests were still running in AZP. 2023-04-09 14:27:39 +02:00
patchback[bot]
9fc27e74f8 Use curl instead of get_url on Python 2.6. (#585) (#586)
(cherry picked from commit 0829bc641e)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-03-22 21:30:53 +01:00
patchback[bot]
1572c10384 fix(doc): privatekey_content docs were the same as privatekey_path (#583) (#584)
(cherry picked from commit b997773139)

Co-authored-by: Thomas Anderson <tnyeanderson@users.noreply.github.com>
2023-03-21 18:02:40 +01:00
Felix Fontein
553f3c2ee0 Cancel concurrent workflow runs in PRs.
(cherry picked from commit 5a3e21788d)
2023-02-23 09:57:25 +01:00
patchback[bot]
837733b4c2 Fix deprecation handling. (#572) (#573)
(cherry picked from commit 70c4585b88)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-02-09 16:25:26 +01:00
patchback[bot]
3468628f1f Fix acme_inspect tests. (#565) (#566)
(cherry picked from commit c6429eae4f)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-01-23 06:41:21 +01:00
patchback[bot]
eee646b636 openssl_csr: fix bad tests, avoid accepting invalid crl_distribution_points records (#560) (#561)
* Improve error handling.

* Remove invalid tests.

* Add changelog fragment.

* Fix tests.

* Improve exception catching.

Co-authored-by: Kristian Heljas <11139388+kristianheljas@users.noreply.github.com>

* Prevent empty full_name.

* Fix condition. Make sure errors are caught.

* Add more checks.

Co-authored-by: Kristian Heljas <11139388+kristianheljas@users.noreply.github.com>
(cherry picked from commit ddfb18b609)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-01-02 17:28:28 +01:00
Felix Fontein
e78318c4cb Next release will be 1.9.21. 2023-01-01 08:52:43 +01:00
Felix Fontein
8ccab3ab80 Release 1.9.20. 2023-01-01 08:14:32 +01:00
patchback[bot]
e2ecd14d86 Fix crash when public key cannot be parsed. (#551) (#552)
(cherry picked from commit 5d24d04adf)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-12-28 20:53:02 +01:00
patchback[bot]
6f8131a628 Make sure that iteration_count=1000 is not used with algorithm=argon* (which is SLOW and takes around 10 minutes). (#546) (#547)
(cherry picked from commit 242c15bf4c)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-12-20 20:16:20 +01:00
Felix Fontein
5761ca12b6 Disable broken CI platforms. (#469)
(cherry picked from commit f7bc3aa77c)
2022-12-20 07:24:19 +01:00
Felix Fontein
bfe181ac1f Move tests with EOL versions of Ansible from AZP to GHA. (#543) 2022-12-20 06:58:28 +01:00
Felix Fontein
f5632c27f4 [TEMP] Create temp remote directory in ~. (#504)
(cherry picked from commit d0d99c31b0)
2022-12-11 18:12:53 +01:00
Felix Fontein
6bec0b402c Be more precise about which private keys are supported in openssl_publickey. (#532)
(cherry picked from commit 1097371cf4)
2022-11-27 18:19:42 +01:00
Felix Fontein
b0262bf8f1 Prepare 1.9.20 release. 2022-11-01 21:14:17 +01:00
Felix Fontein
c57822b3b3 Release 1.9.19. 2022-11-01 20:53:52 +01:00
patchback[bot]
c313bbd83d Action plugin support code: ensure compatibility with newer versions of ansible-core (#515) (#516)
* Only access C.STRING_CONVERSION_ACTION for old ansible-base / Ansible versions.

* Always use self.__xxx instead of xxx directly.

(cherry picked from commit b3f589df62)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-09-23 07:33:48 +02:00
patchback[bot]
b9e8bb70eb Fix docs (#497) (#498)
* Fix docs.

* Fix YAML.

* Prevent crashes with older pyyaml versions.

(cherry picked from commit 2dafef1fab)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-08-04 20:17:55 +02:00
patchback[bot]
62da550c6c Fix ssh-agent tests (#493) (#494)
* Work around stupid ssh-agent output format.

* Workaround for Ansible 2.9.

* Old jinja2...

* Jinja2 on CentOS 6 is really annoying.

(cherry picked from commit e4ebca0945)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-07-22 14:00:53 +02:00
Felix Fontein
3d9fb17d17 Fix typo. 2022-07-09 13:54:52 +02:00
Felix Fontein
2fd7aa6de7 Prepare 1.9.19. 2022-07-09 13:51:12 +02:00
Felix Fontein
b78dd8d542 Release 1.9.18. 2022-07-09 13:17:40 +02:00
Felix Fontein
1df51621fa openssl_pkcs12: fix crash when trying to get non-existing other certificates (#487) (#488)
* Fix crash when trying to get non-existing other certificates.

* Add test.

(cherry picked from commit 9ed4526fee)
2022-07-07 22:53:36 +02:00
Felix Fontein
33703d15e2 Prepare 1.9.18 release. 2022-06-17 09:26:09 +02:00
Felix Fontein
b682c7a281 Release 1.9.17. 2022-06-17 08:23:20 +02:00
Felix Fontein
d393ea233e Add Apache 2.0 license for Apache 2.0 licensed parts. (#479) 2022-06-17 08:21:08 +02:00
patchback[bot]
563e3a2791 Fix ValueError: excluded_subtrees must be a non-empty list or None (#481) (#482)
(cherry picked from commit b29f238083)

Co-authored-by: Songmin Li <lisongmin@protonmail.com>
2022-06-17 08:02:49 +02:00
Felix Fontein
651f2b8f5d x509_crl: do not crash when signing with Ed25519 or Ed448 (#475) (#480)
* Do not crash when signing with Ed25519 or Ed448.

* Forgot replace.

(cherry picked from commit 297b44f24b)
2022-06-15 22:29:34 +02:00
patchback[bot]
077bcba377 Skip Ansible 2.9 coverage reporting with new AZP container. (#476) (#477)
ci_coverage

(cherry picked from commit 429ed5faa5)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-06-14 20:32:32 +00:00
patchback[bot]
9084df1e5c Bump AZP container version. (#472) (#474)
(cherry picked from commit b3029f75cd)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-06-13 21:58:14 +02:00
patchback[bot]
2bb6d7f49d Disable broken CI platforms. (#469) (#470)
(cherry picked from commit f7bc3aa77c)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-06-06 09:36:44 +02:00
Felix Fontein
83cf13a483 Next expected release is 1.9.17. 2022-06-02 12:46:50 +02:00
Felix Fontein
1b22a88b80 Release 1.9.16. 2022-06-02 12:27:44 +02:00
patchback[bot]
252c1a7236 Add simplified_bsd.txt license file (#467) (#468)
* Add simplified_bsd.txt and adjust references.

* Add changelog.

(cherry picked from commit ccd66419f4)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-06-02 08:14:26 +02:00
Felix Fontein
7f371f6915 Update release summary for 1.9.16. 2022-06-02 07:48:31 +02:00
patchback[bot]
1ccd48efba Remove FreeBSD 12.1 from CI. The remote seems to be no longer working. (#459) (#461)
(cherry picked from commit fd0048827d)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-05-20 15:33:08 +00:00
patchback[bot]
a4c077c388 certificate_complete_chain: do not stop execution on unsupported algorithm (#457) (#458)
* Do not stop execution on unsupported algorithm.

* Fix typo.

(cherry picked from commit c49102d688)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-05-20 12:47:59 +02:00
Felix Fontein
2b1e85c86c Next expected release is 1.9.16. 2022-05-16 12:52:21 +02:00
Felix Fontein
a68f119afe Release 1.9.15. 2022-05-16 12:26:27 +02:00
Felix Fontein
9ebf7d668f [stable-1] Add PSF-license.txt file (#455)
* Add PSF-license.txt file. (#453)

(cherry picked from commit 5664bfe4b6)

* Update with actual CPython 3.9.5 license. (#454)

(cherry picked from commit 7183596586)
2022-05-16 09:02:58 +02:00
Felix Fontein
4570c481ef Prepare 1.9.15 release. 2022-05-16 07:14:00 +02:00
Felix Fontein
5d56629d10 Prepare 1.9.15 release. 2022-05-09 20:52:41 +02:00
Felix Fontein
37c7100c8b Release 1.9.14. 2022-05-09 20:29:24 +02:00
Felix Fontein
35266bda0e Prepare 1.9.14 release. 2022-05-03 19:25:24 +02:00
Felix Fontein
6a90a43995 Fix stable-1 for new cryptography 37.0.0 release (#446)
* Fix empty check for openssl_pkcs12 tests.

* Prevent crash if PyOpenSSL cannot be imported because of an AttributeError.

* Add changelog fragment.

* Fix constraints file.

* Use Python 2.7 instead of 3.5 for 2.9 cloud tests (pip module is broken).

* Prevent upgrading cryptography on ansible-core 2.12's default container with Python 3.9.
2022-04-26 22:33:13 +02:00
Felix Fontein
096262b6f1 Fix crash in x509_crl when certificate issuer is specified (#441) (#442)
* Fix x509_crl certificate issuer issue.

* Add tests.

* Add changelog fragment.

(cherry picked from commit 9d03178b00)
2022-04-18 10:19:27 +02:00
patchback[bot]
03df636e5e Switch from antsibull to antsibull-docs. (#438) (#439)
(cherry picked from commit c7f581daad)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-04-10 10:58:54 +02:00
Felix Fontein
02bc9de560 On the 'default' image with Python 3.8, cryptography 2.8 is installed from the
system packages, but cryptography 36.0.1 is already present and shadows the
one from the system packages. Since PyOpenSSL is also installed from the system
packages, there is a version mismatch between the two. Temporarily disable the
Python 3.8 tests with the 'default' image on ansible-core 2.13 until this is
resolved, or the stable-1 branch is EOL (whatever comes first).
2022-04-03 15:08:55 +02:00
patchback[bot]
8fe0a2450e Replace antsibull-lint collection-docs with antsibull-docs lint-collection-docs. (#432) (#433)
(cherry picked from commit bc00c30faf)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-03-30 08:13:40 +02:00
Felix Fontein
d8bb99d547 Replace devel with stable-2.13 in stable-1 CI. (#424) 2022-03-29 06:19:54 +02:00
patchback[bot]
79f9ce437a openssh_* - catch and report top-level exceptions via fail_json (#417) (#418)
* ensure exceptions are properly reported

* adding changelog fragment

* applying review suggestions

* typo

* adding back exception msg

(cherry picked from commit 033bab7db1)

Co-authored-by: Andrew Pantuso <ajpantuso@gmail.com>
2022-03-08 20:25:38 +01:00
Felix Fontein
6cb02818ef Next expected release is 1.9.14. 2022-03-04 08:06:24 +01:00
Felix Fontein
e94fb2dff2 Release 1.9.13. 2022-03-04 07:38:05 +01:00
Felix Fontein
14c39b1f99 Prepare 1.9.13 release. 2022-03-03 21:17:36 +01:00
patchback[bot]
6dafa5954e fixing public key return value docs (#412) (#414)
(cherry picked from commit 010f1a4d2d)

Co-authored-by: Andrew Pantuso <ajpantuso@gmail.com>
2022-03-02 14:09:20 +01:00
patchback[bot]
da1dd21a9e Fix parsing of lsblk output. (#410) (#413)
(cherry picked from commit 0d4b3ed991)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-03-02 14:05:07 +01:00
Felix Fontein
011036b87f Next expected release is 1.9.13. 2022-02-21 22:22:36 +01:00
Felix Fontein
35ef2edb3f Release 1.9.12. 2022-02-21 21:48:14 +01:00
Felix Fontein
ebcf866891 Prepare 1.9.12 release. 2022-02-19 18:53:26 +01:00
Felix Fontein
60c6d87b05 [stable-1] x509_certificate: regenerate certificate on CA's subject change (#406)
* Regenerate certificate on CA's subject change. (#402)

(cherry picked from commit 3ebc132c03)

* Add fix for PyOpenSSL backend.

* x509_certificate: check existing certificate's signature for selfsigned and ownca provider (#407)

* Verify whether signature matches.

* Add changelog fragment.

* Forgot imports.

* Fix wrong name.

* Check whether the CA private key fits to the CA certificate. Use correct key in tests.

* Refactor code.

(cherry picked from commit 28729657ac)

* There doesn't seem a way to do this with pyOpenSSL.
2022-02-19 17:51:28 +00:00
patchback[bot]
2aa38fe247 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 11a14543c8)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-02-14 18:04:54 +01:00
Felix Fontein
d19faa1627 Next expected release is 1.9.12. 2022-02-05 21:45:27 +01:00
Felix Fontein
e910f299b9 Release 1.9.11. 2022-02-05 21:28:22 +01:00
Felix Fontein
2ebf26854e Prepare 1.9.11 release. 2022-02-05 20:19:18 +01:00
Andrew Pantuso
7ff067937a openssh_cert - fix full_idempotence for host certificates (#396) (#397)
* fixing host cert idempotence

* adding changelog fragment

(cherry picked from commit a307618872)
2022-02-05 10:00:07 +01:00
Felix Fontein
2727b74cc7 Next expected release is 1.9.11. 2022-02-01 06:18:36 +01:00
Felix Fontein
3bb9c5f9a7 Release 1.9.10. 2022-02-01 05:49:00 +01:00
patchback[bot]
5623590c77 Drop CentOS 8 from CI. (#393) (#394)
(cherry picked from commit 23226dce8f)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-02-01 05:24:21 +01:00
Felix Fontein
29050913b3 Prepare 1.9.10 release. 2022-01-31 06:03:15 +01:00
patchback[bot]
aead2bf783 Set LANG and similar env variables to prevent translated cryptsetup output. (#388) (#390)
(cherry picked from commit ea2e45d63f)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-30 21:57:31 +01:00
patchback[bot]
2aaae70372 [PR #387/5abfe8fc backport][stable-1] PyOpenSSL 22.0.0 no longer supports Python 2.7 (#389)
* PyOpenSSL 22.0.0 no longer supports Python 2.7. (#387)

(cherry picked from commit 5abfe8fca9)

* Do not install PyOpenSSL from PyPi if cryptography cannot be updated - at least on FreeBSD 13.0, latest PyOpenSSL requires a cryptography upgrade, which breaks CI.

* Revert "Do not install PyOpenSSL from PyPi if cryptography cannot be updated - at least on FreeBSD 13.0, latest PyOpenSSL requires a cryptography upgrade, which breaks CI."

This reverts commit 16f9145653.

* Try another approach.

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-30 15:23:29 +01:00
patchback[bot]
de29a258c6 Fix indentation of when in example. (#382) (#383)
(cherry picked from commit a467f036b1)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-19 04:54:17 +00:00
patchback[bot]
8ca5b480e4 Update CI matrix for Remote Devel (#377) (#378)
* Update CI matrix for Remote Devel.

* Add Python info entries.

(cherry picked from commit cd5ed011a5)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-13 09:52:59 +01:00
Felix Fontein
f6ec785b0d Next expected release is 1.9.10. 2022-01-11 07:28:54 +01:00
Felix Fontein
4834c0cb4b Release 1.9.9. 2022-01-11 07:04:43 +01:00
patchback[bot]
1f0016c616 Fix comment. (#372) (#373)
(cherry picked from commit 1b0fcde862)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-06 15:10:40 +01:00
patchback[bot]
74e4be139f Use vendored copy of distutils.version. (#369) (#370)
(cherry picked from commit 46f39efc43)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-05 22:22:25 +01:00
patchback[bot]
b584336b62 Fix typo. (#367) (#368)
(cherry picked from commit 3e307fe062)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-05 20:36:01 +01:00
patchback[bot]
20b0d7a298 certificate_complete_chain: avoid infinite loops, and double roots when root certificate was already part of chain (#360) (#366)
* Avoid infinite loops, and double roots when root certificate was already part of chain.

* Refactor tests for readability.

(cherry picked from commit 6ee238d961)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-04 07:27:15 +01:00
patchback[bot]
5741f24aa9 Fix indentation in docs. (#364) (#365)
(cherry picked from commit f3e431912d)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-03 21:47:20 +01:00
patchback[bot]
b5dfc9fc75 Improve changed / nonchanged validations by using new modules from community.internal_test_tools (#183) (#361)
* Use modules from internal_test_tools instead of stat workaround to check whether file actually changed.

* Properly add testing dependency.

(cherry picked from commit 471506c5d4)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-03 19:30:21 +01:00
patchback[bot]
4318e95618 Feature/rename test cases (#356) (#358)
* Name test tasks in a more explicite manner

* Space test + verification blocks apart

* Apply suggestions from code review

Co-authored-by: Jens Heinrich <github.com/JensHeinrich>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 2c05221d89)

Co-authored-by: Jens Heinrich <59469646+JensHeinrich@users.noreply.github.com>
2021-12-30 10:31:04 +01:00
Felix Fontein
113cbb6eb8 Prepare for distutils.version being removed in Python 3.12 (#353) (#354)
* Prepare for distutils.version being removed in Python 2.12.

* Fix copy'n'paste error.

* Re-add Loose prefix.

* Fix Python version typo.

* Improve formulation.

* Move message into own line.

* Fix casing, now that the object is no longer called Version.

(cherry picked from commit a539cd6939)
2021-12-24 12:15:45 +01:00
Felix Fontein
3ebed060b7 Next expected release is 1.9.9. 2021-12-13 21:11:58 +01:00
Felix Fontein
270ef0db47 Release 1.9.8. 2021-12-13 20:20:11 +01:00
patchback[bot]
0b306b436a Fix module reference in example (#351) (#352)
openssl_privatekey -> openssl_publickey

(cherry picked from commit 45b7aa797e)

Co-authored-by: Jasmine Hegman <jasmine@jhegman.com>
2021-12-13 06:03:13 +00:00
Felix Fontein
c5519bc557 Prepare 1.9.8 release. 2021-12-13 07:01:04 +01:00
patchback[bot]
23d6f2ae75 Fix CSR copy/paste error (#349) (#350)
The first case about ca_csr has been copy/pasted.
But in the following cases, the CSR must be the certificate csr.

(cherry picked from commit 32dab841d7)

Co-authored-by: Bruno Vernay <brunovern.a@gmail.com>
2021-12-09 21:09:52 +01:00
Felix Fontein
9da7c54ae9 Next release will be 1.9.8. 2021-11-22 12:18:34 +01:00
Felix Fontein
928cb3aa9b Release 1.9.7. 2021-11-22 11:41:02 +01:00
patchback[bot]
3e6815d73f [PR #331/3f40795a backport][stable-1] Extension parsing: add new fallback code which uses the new cryptography API (#345)
* Extension parsing: add new fallback code which uses the new cryptography API (#331)

* Add new code as fallback which re-serializes de-serialized extensions using the new cryptography API.

* Forgot Base64 encoding.

* Add extension by OID tests.

* There's one value which is different with the new code.

* Differences in CI.

* Working around older Jinjas.

* Value depends on which SAN was included.

* Force complete CI run now since cryptography 36.0.0 is out.

ci_complete

(cherry picked from commit 3f40795a98)

* Adjust tests.

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-22 08:54:08 +01:00
patchback[bot]
cb08f56066 Use new PKCS#12 deserialization code from cryptography 36.0.0 if available (#302) (#344)
* Use new PKCS#12 deserialization code from cryptography 36.0.0 if available.

* Refactor into smaller functions.

* Force complete CI run now since cryptography 36.0.0 is out.

ci_complete

(cherry picked from commit 73bc0f5de7)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-22 08:27:33 +01:00
Felix Fontein
e3f486a063 Prepare 1.9.7 releaese. 2021-11-22 07:41:21 +01:00
patchback[bot]
0a1e25e16a Fix collection dependency installation in CI. (#341) (#342)
(cherry picked from commit f1a6baadc7)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-18 21:21:57 +01:00
patchback[bot]
ff4966ad3f Fix compatibility to fetch_url change in ansible-core devel (#339) (#340)
* Fix compatibility to fetch_url change in ansible-core devel.

* Adjust tests.

(cherry picked from commit 5de50b9f91)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-17 21:46:13 +01:00
patchback[bot]
0cb10be2d5 Replace RHEL 8.4 by RHEL 8.5 for devel. (#337) (#338)
(cherry picked from commit cf0d2679aa)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-17 07:07:31 +00:00
patchback[bot]
901863989b This is no longer a problem with the dev version of cryptography. (#335) (#336)
(cherry picked from commit 2d388bf8d0)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-13 23:31:52 +01:00
Felix Fontein
99377764c1 Replace Bash codecov uploader by new Python codecov uploader. (#333) (#334)
ci_coverage

(cherry picked from commit 056a86fcae)
2021-11-13 13:22:01 +01:00
patchback[bot]
426d70fbcf luks_device: add built-in signature wiper to work around older wipefs versions with LUKS2 containers (#327) (#330)
* Use 'cryptsetup erase' to kill LUKS signature.

* Adjust unit test.

* Use own wiper for LUKS headers.

* Add comments.

* Fix tests.

* Update changelog.

* Remove 'cryptsetup erase'.

* Improve error messages.

(cherry picked from commit ebbfd7c56f)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-11 07:17:45 +01:00
patchback[bot]
f315722b31 Replace Fedora 33 with Fedora 35 for devel tests. (#328) (#329)
(cherry picked from commit 91d98c4413)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-09 05:52:42 +01:00
patchback[bot]
73afe8e742 acme_certificate: fix crash when using fullchain_dest (#324) (#325)
* Fix crash when using fullchain_dest.

* Adjust changelog.

* Update plugins/module_utils/acme/backend_cryptography.py

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
(cherry picked from commit 51b6bb210d)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-05 09:35:10 +01:00
Felix Fontein
db67b8a857 Next expected release is 1.9.7. 2021-10-30 18:21:40 +02:00
Felix Fontein
e05475d58a Release 1.9.6. 2021-10-30 17:48:48 +02:00
Felix Fontein
dceee8f50e Prepare 1.9.6 release. 2021-10-30 17:07:49 +02:00
Felix Fontein
b893252ad1 [stable-1] cryptography support: improve Python 2 Unicode handling (#314)
* Improve Python 2 Unicode handling. (#313)

(cherry picked from commit eb8dabce84)

* Remove test since it doesn't work with pyOpenSSL.

* Completely remove test.

* Update plugins/module_utils/crypto/cryptography_support.py
2021-10-29 21:10:57 +02:00
patchback[bot]
0755a2b657 Remove centos8 for devel from CI. (#307) (#308)
(cherry picked from commit 78b27ffedb)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-16 09:27:45 +02:00
Felix Fontein
90bf8b0b2e Adjust to latest devel changes.
(cherry picked from commit e735bdab60)
2021-10-12 19:32:56 +02:00
patchback[bot]
5ff28c751d Fix shellcheck error. (#303) (#304)
(cherry picked from commit c68bfedbaa)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-08 15:10:41 +02:00
Felix Fontein
7b08edb5a4 Next expected release is 1.9.6. 2021-10-06 13:35:44 +02:00
Felix Fontein
fbadcbeb29 Release 1.9.5. 2021-10-06 13:08:43 +02:00
Felix Fontein
e991375f55 Prepare 1.9.5 release. 2021-10-05 22:28:03 +02:00
Felix Fontein
33c99014ae [stable-1] Fix PyOpenSSL backends with cryptography 35.0.0 (#300)
* Try to make compatible with cryptography 35.0.0.

* Forgot import.

ci_complete

* Add changelog fragment.
2021-10-05 22:19:11 +02:00
patchback[bot]
fbd6ff6ead x509_certificate: document that *notBefore/*notAfter are not used for idempotency (#298) (#301)
* Document that *notBefore/*notAfter are not used for idempotency.

* Change formulation.

(cherry picked from commit ed03841fd1)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-03 22:20:34 +02:00
patchback[bot]
c4ab2eb3b5 Fix PKCS#12 friendly name extraction for cryptography 35.0.0. (#296) (#299)
(cherry picked from commit d6c0d53442)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-03 21:37:37 +02:00
patchback[bot]
44b6df0ce5 Support cryptography 35.0.0 for all modules except openssl_pkcs12 (#294) (#297)
* Add some workarounds for cryptography 35.0.0.

* Make fix work with very old cryptography versions as well (which supported multiple backends).

* [TEMP] Disable openssl_pkcs12 tests to see whether everything else works.

* Revert "[TEMP] Disable openssl_pkcs12 tests to see whether everything else works."

This reverts commit 3f905bc795.

* Add changelog fragment.

* Remove unnecessary assignment.

* Simplify code change.

* [TEMP] Disable openssl_pkcs12 tests to see whether everything else works.

* Revert "[TEMP] Disable openssl_pkcs12 tests to see whether everything else works."

This reverts commit fdb210528e.

(cherry picked from commit a2a7d94055)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-03 17:23:35 +02:00
Felix Fontein
14a42505a9 Ansible-core devel dropped support for Python 2.6.
(cherry picked from commit 2a7e452cf8)
2021-10-01 13:46:08 +02:00
Felix Fontein
44cbd33cb7 Run CI on stable branches only once per week.
(cherry picked from commit 24e7d07973)
2021-10-01 13:44:44 +02:00
Felix Fontein
4411a71d06 Temporarily fix CI for cryptography 35.0.0 release. (#292)
(cherry picked from commit 57c364fe87)
2021-09-30 13:45:38 +02:00
Felix Fontein
bfe37bc668 Next expected 1.x.y release is 1.9.5. 2021-09-28 17:31:41 +02:00
113 changed files with 2909 additions and 667 deletions

View File

@@ -19,6 +19,11 @@ schedules:
branches:
include:
- main
- cron: 0 12 * * 0
displayName: Weekly (old stable branches)
always: true
branches:
include:
- stable-*
variables:
@@ -36,88 +41,40 @@ variables:
resources:
containers:
- container: default
image: quay.io/ansible/azure-pipelines-test-container:1.9.0
image: quay.io/ansible/azure-pipelines-test-container:4.0.1
pool: Standard
stages:
### Sanity & units
- stage: Ansible_devel
displayName: Sanity & Units devel
- stage: Ansible_2_13
displayName: Sanity & Units 2.13
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
targets:
- name: Sanity
test: 'devel/sanity/1'
test: '2.13/sanity/1'
- name: Sanity Extra # Only on devel
test: 'devel/sanity/extra'
test: '2.13/sanity/extra'
- name: Units
test: 'devel/units/1'
- stage: Ansible_2_12
displayName: Sanity & Units 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
targets:
- name: Sanity
test: '2.12/sanity/1'
- name: Units
test: '2.12/units/1'
- stage: Ansible_2_11
displayName: Sanity & Units 2.11
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
targets:
- name: Sanity
test: '2.11/sanity/1'
- name: Units
test: '2.11/units/1'
- stage: Ansible_2_10
displayName: Sanity & Units 2.10
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
targets:
- name: Sanity
test: '2.10/sanity/1'
- name: Units
test: '2.10/units/1'
- stage: Ansible_2_9
displayName: Sanity & Units 2.9
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
targets:
- name: Sanity
test: '2.9/sanity/1'
- name: Units
test: '2.9/units/1'
test: '2.13/units/1'
### Docker
- stage: Docker_devel
displayName: Docker devel
- stage: Docker_2_13
displayName: Docker 2.13
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: devel/linux/{0}/1
testFormat: 2.13/linux/{0}/1
targets:
- name: CentOS 6
test: centos6
- name: CentOS 7
test: centos7
- name: CentOS 8
test: centos8
- name: Fedora 33
test: fedora33
- name: Fedora 34
test: fedora34
- name: Fedora 35
test: fedora35
- name: openSUSE 15 py2
test: opensuse15py2
- name: openSUSE 15 py3
@@ -126,227 +83,52 @@ stages:
test: ubuntu1804
- name: Ubuntu 20.04
test: ubuntu2004
- stage: Docker_2_12
displayName: Docker 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.12/linux/{0}/1
targets:
- name: CentOS 8
test: centos8
- name: Fedora 33
test: fedora33
- name: openSUSE 15 py3
test: opensuse15
- name: Ubuntu 20.04
test: ubuntu2004
- stage: Docker_2_11
displayName: Docker 2.11
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.11/linux/{0}/1
targets:
- name: CentOS 7
test: centos7
- name: CentOS 8
test: centos8
- name: Fedora 32
test: fedora32
- name: openSUSE 15 py2
test: opensuse15py2
- name: Ubuntu 18.04
test: ubuntu1804
- stage: Docker_2_10
displayName: Docker 2.10
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.10/linux/{0}/1
targets:
- name: CentOS 6
test: centos6
- name: Fedora 31
test: fedora31
- name: Ubuntu 16.04
test: ubuntu1604
- stage: Docker_2_9
displayName: Docker 2.9
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.9/linux/{0}/1
targets:
- name: CentOS 6
test: centos6
- name: CentOS 7
test: centos7
- name: Fedora 31
test: fedora31
- name: Ubuntu 16.04
test: ubuntu1604
- name: Ubuntu 18.04
test: ubuntu1804
### Remote
- stage: Remote_devel
displayName: Remote devel
- stage: Remote_2_13
displayName: Remote 2.13
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: devel/{0}/1
testFormat: 2.13/{0}/1
targets:
- name: macOS 11.1
test: macos/11.1
- name: macOS 12.0
test: macos/12.0
- name: RHEL 7.9
test: rhel/7.9
- name: RHEL 8.4
test: rhel/8.4
- name: FreeBSD 12.2
test: freebsd/12.2
- name: FreeBSD 13.0
test: freebsd/13.0
- stage: Remote_2_12
displayName: Remote 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.12/{0}/1
targets:
- name: macOS 11.1
test: macos/11.1
- name: RHEL 8.4
test: rhel/8.4
- name: FreeBSD 13.0
test: freebsd/13.0
- stage: Remote_2_11
displayName: Remote 2.11
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.11/{0}/1
targets:
- name: RHEL 7.9
test: rhel/7.9
- name: RHEL 8.3
test: rhel/8.3
- name: FreeBSD 12.2
test: freebsd/12.2
- stage: Remote_2_10
displayName: Remote 2.10
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.10/{0}/1
targets:
- name: OS X 10.11
test: osx/10.11
- name: macOS 10.15
test: macos/10.15
- name: FreeBSD 12.1
test: freebsd/12.1
- stage: Remote_2_9
displayName: Remote 2.9
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.9/{0}/1
targets:
- name: 'RHEL 7.8'
test: 'rhel/7.8'
- name: RHEL 8.5
test: rhel/8.5
- name: FreeBSD 12.4
test: freebsd/12.4
- name: FreeBSD 13.1
test: freebsd/13.1
### cloud
- stage: Cloud_devel
displayName: Cloud devel
- stage: Cloud_2_13
displayName: Cloud 2.13
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: devel/cloud/{0}/1
testFormat: 2.13/cloud/{0}/1
targets:
- test: 2.6
- test: 2.7
- test: 3.5
- test: 3.6
- test: 3.7
- test: 3.8
# - test: 3.8
- test: 3.9
- test: "3.10"
- stage: Cloud_2_12
displayName: Cloud 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.12/cloud/{0}/1
targets:
- test: 3.9
- stage: Cloud_2_11
displayName: Cloud 2.11
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.11/cloud/{0}/1
targets:
- test: 3.8
- stage: Cloud_2_10
displayName: Cloud 2.10
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.10/cloud/{0}/1
targets:
- test: 3.6
- stage: Cloud_2_9
displayName: Cloud 2.9
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.9/cloud/{0}/1
targets:
- test: 3.5
## Finally
- stage: Summary
condition: succeededOrFailed()
dependsOn:
- Ansible_devel
- Ansible_2_12
- Ansible_2_11
- Ansible_2_10
- Ansible_2_9
- Remote_devel
- Remote_2_12
- Remote_2_11
- Remote_2_10
- Remote_2_9
- Docker_devel
- Docker_2_12
- Docker_2_11
- Docker_2_10
- Docker_2_9
- Cloud_devel
- Cloud_2_12
- Cloud_2_11
- Cloud_2_10
- Cloud_2_9
- Ansible_2_13
- Remote_2_13
- Docker_2_13
- Cloud_2_13
jobs:
- template: templates/coverage.yml

View File

@@ -9,9 +9,13 @@ PATH="${PWD}/bin:${PATH}"
mkdir "${agent_temp_directory}/coverage/"
if [[ "$(ansible --version)" =~ \ 2\.9\. ]]; then
exit
fi
options=(--venv --venv-system-site-packages --color -v)
ansible-test coverage combine --export "${agent_temp_directory}/coverage/" "${options[@]}"
ansible-test coverage combine --group-by command --export "${agent_temp_directory}/coverage/" "${options[@]}"
if ansible-test coverage analyze targets generate --help >/dev/null 2>&1; then
# Only analyze coverage if the installed version of ansible-test supports it.

View File

@@ -0,0 +1,101 @@
#!/usr/bin/env python
"""
Upload code coverage reports to codecov.io.
Multiple coverage files from multiple languages are accepted and aggregated after upload.
Python coverage, as well as PowerShell and Python stubs can all be uploaded.
"""
import argparse
import dataclasses
import pathlib
import shutil
import subprocess
import tempfile
import typing as t
import urllib.request
@dataclasses.dataclass(frozen=True)
class CoverageFile:
name: str
path: pathlib.Path
flags: t.List[str]
@dataclasses.dataclass(frozen=True)
class Args:
dry_run: bool
path: pathlib.Path
def parse_args() -> Args:
parser = argparse.ArgumentParser()
parser.add_argument('-n', '--dry-run', action='store_true')
parser.add_argument('path', type=pathlib.Path)
args = parser.parse_args()
# Store arguments in a typed dataclass
fields = dataclasses.fields(Args)
kwargs = {field.name: getattr(args, field.name) for field in fields}
return Args(**kwargs)
def process_files(directory: pathlib.Path) -> t.Tuple[CoverageFile, ...]:
processed = []
for file in directory.joinpath('reports').glob('coverage*.xml'):
name = file.stem.replace('coverage=', '')
# Get flags from name
flags = name.replace('-powershell', '').split('=') # Drop '-powershell' suffix
flags = [flag if not flag.startswith('stub') else flag.split('-')[0] for flag in flags] # Remove "-01" from stub files
processed.append(CoverageFile(name, file, flags))
return tuple(processed)
def upload_files(codecov_bin: pathlib.Path, files: t.Tuple[CoverageFile, ...], dry_run: bool = False) -> None:
for file in files:
cmd = [
str(codecov_bin),
'--name', file.name,
'--file', str(file.path),
]
for flag in file.flags:
cmd.extend(['--flags', flag])
if dry_run:
print(f'DRY-RUN: Would run command: {cmd}')
continue
subprocess.run(cmd, check=True)
def download_file(url: str, dest: pathlib.Path, flags: int, dry_run: bool = False) -> None:
if dry_run:
print(f'DRY-RUN: Would download {url} to {dest} and set mode to {flags:o}')
return
with urllib.request.urlopen(url) as resp:
with dest.open('w+b') as f:
# Read data in chunks rather than all at once
shutil.copyfileobj(resp, f, 64 * 1024)
dest.chmod(flags)
def main():
args = parse_args()
url = 'https://ansible-ci-files.s3.amazonaws.com/codecov/linux/codecov'
with tempfile.TemporaryDirectory(prefix='codecov-') as tmpdir:
codecov_bin = pathlib.Path(tmpdir) / 'codecov'
download_file(url, codecov_bin, 0o755, args.dry_run)
files = process_files(args.path)
upload_files(codecov_bin, files, args.dry_run)
if __name__ == '__main__':
main()

View File

@@ -1,27 +0,0 @@
#!/usr/bin/env bash
# Upload code coverage reports to codecov.io.
# Multiple coverage files from multiple languages are accepted and aggregated after upload.
# Python coverage, as well as PowerShell and Python stubs can all be uploaded.
set -o pipefail -eu
output_path="$1"
curl --silent --show-error https://ansible-ci-files.s3.us-east-1.amazonaws.com/codecov/codecov.sh > codecov.sh
for file in "${output_path}"/reports/coverage*.xml; do
name="${file}"
name="${name##*/}" # remove path
name="${name##coverage=}" # remove 'coverage=' prefix if present
name="${name%.xml}" # remove '.xml' suffix
bash codecov.sh \
-f "${file}" \
-n "${name}" \
-X coveragepy \
-X gcov \
-X fix \
-X search \
-X xcode \
|| echo "Failed to upload code coverage report to codecov.io: ${file}"
done

View File

@@ -5,6 +5,10 @@ set -o pipefail -eu
PATH="${PWD}/bin:${PATH}"
if [[ "$(ansible --version)" =~ \ 2\.9\. ]]; then
exit
fi
if ! ansible-test --help >/dev/null 2>&1; then
# Install the devel version of ansible-test for generating code coverage reports.
# This is only used by Ansible Collections, which are typically tested against multiple Ansible versions (in separate jobs).
@@ -12,4 +16,4 @@ if ! ansible-test --help >/dev/null 2>&1; then
pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check
fi
ansible-test coverage xml --stub --venv --venv-system-site-packages --color -v
ansible-test coverage xml --group-by command --stub --venv --venv-system-site-packages --color -v

View File

@@ -33,7 +33,7 @@ jobs:
summaryFileLocation: "$(outputPath)/reports/$(pipelinesCoverage).xml"
displayName: Publish to Azure Pipelines
condition: gt(variables.coverageFileCount, 0)
- bash: .azure-pipelines/scripts/publish-codecov.sh "$(outputPath)"
- bash: .azure-pipelines/scripts/publish-codecov.py "$(outputPath)"
displayName: Publish to codecov.io
condition: gt(variables.coverageFileCount, 0)
continueOnError: true

215
.github/workflows/ansible-test.yml vendored Normal file
View File

@@ -0,0 +1,215 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
name: EOL CI
on:
# Run EOL CI against all pushes (direct commits, also merged PRs), Pull Requests
push:
branches:
- stable-1
pull_request:
concurrency:
# Make sure there is at most one active run per PR, but do not cancel any non-PR runs
group: ${{ github.workflow }}-${{ (github.head_ref && github.event.number) || github.run_id }}
cancel-in-progress: true
jobs:
sanity:
name: EOL Sanity (Ⓐ${{ matrix.ansible }})
strategy:
matrix:
ansible:
- '2.9'
- '2.10'
- '2.11'
- '2.12'
# Ansible-test on various stable branches does not yet work well with cgroups v2.
# Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
# image for these stable branches. The list of branches where this is necessary will
# shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28
# for the latest list.
runs-on: >-
${{ contains(fromJson(
'["2.9", "2.10", "2.11"]'
), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }}
steps:
- name: Perform sanity testing
uses: felixfontein/ansible-test-gh-action@main
with:
ansible-core-github-repository-slug: ${{ contains(fromJson('["2.10", "2.11"]'), matrix.ansible) && 'felixfontein/ansible' || 'ansible/ansible' }}
ansible-core-version: stable-${{ matrix.ansible }}
coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }}
pull-request-change-detection: 'true'
testing-type: sanity
units:
# Ansible-test on various stable branches does not yet work well with cgroups v2.
# Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
# image for these stable branches. The list of branches where this is necessary will
# shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28
# for the latest list.
runs-on: >-
${{ contains(fromJson(
'["2.9", "2.10", "2.11"]'
), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }}
name: EOL Units (Ⓐ${{ matrix.ansible }})
strategy:
# As soon as the first unit test fails, cancel the others to free up the CI queue
fail-fast: true
matrix:
ansible:
- '2.9'
- '2.10'
- '2.11'
- '2.12'
steps:
- name: >-
Perform unit testing against
Ansible version ${{ matrix.ansible }}
uses: felixfontein/ansible-test-gh-action@main
with:
ansible-core-github-repository-slug: ${{ contains(fromJson('["2.10", "2.11"]'), matrix.ansible) && 'felixfontein/ansible' || 'ansible/ansible' }}
ansible-core-version: stable-${{ matrix.ansible }}
coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }}
pull-request-change-detection: 'true'
testing-type: units
integration:
# Ansible-test on various stable branches does not yet work well with cgroups v2.
# Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
# image for these stable branches. The list of branches where this is necessary will
# shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28
# for the latest list.
runs-on: >-
${{ contains(fromJson(
'["2.9", "2.10", "2.11"]'
), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }}
name: EOL I (Ⓐ${{ matrix.ansible }}+${{ matrix.docker }}+py${{ matrix.python }}:${{ matrix.target }})
strategy:
fail-fast: false
matrix:
ansible:
- ''
docker:
- ''
python:
- ''
target:
- ''
exclude:
- ansible: ''
include:
# 2.9
- ansible: '2.9'
docker: centos6
python: ''
target: shippable/posix/group1/
- ansible: '2.9'
docker: centos7
python: ''
target: shippable/posix/group1/
- ansible: '2.9'
docker: fedora31
python: ''
target: shippable/posix/group1/
- ansible: '2.9'
docker: ubuntu1604
python: ''
target: shippable/posix/group1/
- ansible: '2.9'
docker: ubuntu1804
python: ''
target: shippable/posix/group1/
- ansible: '2.9'
docker: default
python: '2.7'
target: shippable/cloud/group1/
# 2.10
- ansible: '2.10'
docker: centos6
python: ''
target: shippable/posix/group1/
- ansible: '2.10'
docker: fedora31
python: ''
target: shippable/posix/group1/
- ansible: '2.10'
docker: ubuntu1604
python: ''
target: shippable/posix/group1/
- ansible: '2.10'
docker: default
python: '3.6'
target: shippable/cloud/group1/
# 2.11
- ansible: '2.11'
docker: centos7
python: ''
target: shippable/posix/group1/
- ansible: '2.11'
docker: fedora32
python: ''
target: shippable/posix/group1/
- ansible: '2.11'
docker: opensuse15py2
python: ''
target: shippable/posix/group1/
- ansible: '2.11'
docker: ubuntu1804
python: ''
target: shippable/posix/group1/
- ansible: '2.11'
docker: default
python: '3.8'
target: shippable/cloud/group1/
# 2.12
- ansible: '2.12'
docker: centos6
python: ''
target: shippable/posix/group1/
- ansible: '2.12'
docker: fedora33
python: ''
target: shippable/posix/group1/
- ansible: '2.12'
docker: opensuse15
python: ''
target: shippable/posix/group1/
- ansible: '2.12'
docker: ubuntu2004
python: ''
target: shippable/posix/group1/
- ansible: '2.12'
docker: default
python: '2.6'
target: shippable/cloud/group1/
- ansible: '2.12'
docker: default
python: '3.9'
target: shippable/cloud/group1/
steps:
- name: >-
Perform integration testing against
Ansible version ${{ matrix.ansible }}
under Python ${{ matrix.python }}
uses: felixfontein/ansible-test-gh-action@main
with:
ansible-core-github-repository-slug: ${{ contains(fromJson('["2.10", "2.11"]'), matrix.ansible) && 'felixfontein/ansible' || 'ansible/ansible' }}
ansible-core-version: stable-${{ matrix.ansible }}
coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }}
docker-image: ${{ matrix.docker }}
integration-continue-on-error: 'false'
integration-diff: 'false'
integration-retry-on-error: 'true'
pre-test-cmd: >-
git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git ../../community/internal_test_tools
;
git clone --depth=1 --single-branch https://github.com/ansible-collections/community.general.git ../../community/general
pull-request-change-detection: 'true'
target: ${{ matrix.target }}
target-python-version: ${{ matrix.python }}
testing-type: integration

202
Apache-2.0.txt Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -5,6 +5,275 @@ Community Crypto Release Notes
.. contents:: Topics
v1.9.23
=======
Release Summary
---------------
Bugfix release.
Bugfixes
--------
- openssl_pkcs12 - modify autodetect to not detect pyOpenSSL >= 23.3.0, which removed PKCS#12 support (https://github.com/ansible-collections/community.crypto/pull/666).
v1.9.22
=======
Release Summary
---------------
Bugfix release.
Bugfixes
--------
- openssh_keypair - always generate a new key pair if the private key does not exist. Previously, the module would fail when ``regenerate=fail`` without an existing key, contradicting the documentation (https://github.com/ansible-collections/community.crypto/pull/598).
v1.9.21
=======
Release Summary
---------------
Bugfix release.
Bugfixes
--------
- action plugin helper - fix handling of deprecations for ansible-core 2.14.2 (https://github.com/ansible-collections/community.crypto/pull/572).
- openssl_csr, openssl_csr_pipe - prevent invalid values for ``crl_distribution_points`` that do not have one of ``full_name``, ``relative_name``, and ``crl_issuer`` (https://github.com/ansible-collections/community.crypto/pull/560).
v1.9.20
=======
Release Summary
---------------
Bugfix release.
Bugfixes
--------
- openssl_publickey_info - do not crash with internal error when public key cannot be parsed (https://github.com/ansible-collections/community.crypto/pull/551).
v1.9.19
=======
Release Summary
---------------
Bugfix release.
Bugfixes
--------
- openssl_privatekey_pipe - ensure compatibility with newer versions of ansible-core (https://github.com/ansible-collections/community.crypto/pull/515).
v1.9.18
=======
Release Summary
---------------
Bugfix release.
Bugfixes
--------
- openssl_pkcs12 - when using the pyOpenSSL backend, do not crash when trying to read non-existing other certificates (https://github.com/ansible-collections/community.crypto/issues/486, https://github.com/ansible-collections/community.crypto/pull/487).
v1.9.17
=======
Release Summary
---------------
Bugfix release.
Bugfixes
--------
- Include ``Apache-2.0.txt`` file for ``plugins/module_utils/crypto/_obj2txt.py`` and ``plugins/module_utils/crypto/_objects_data.py``.
- openssl_csr - the module no longer crashes with 'permitted_subtrees/excluded_subtrees must be a non-empty list or None' if only one of ``name_constraints_permitted`` and ``name_constraints_excluded`` is provided (https://github.com/ansible-collections/community.crypto/issues/481).
- x509_crl - do not crash when signing CRL with Ed25519 or Ed448 keys (https://github.com/ansible-collections/community.crypto/issues/473, https://github.com/ansible-collections/community.crypto/pull/474).
v1.9.16
=======
Release Summary
---------------
Maintenance and bugfix release.
Bugfixes
--------
- Include ``simplified_bsd.txt`` license file for the ECS module utils.
- certificate_complete_chain - do not stop execution if an unsupported signature algorithm is encountered; warn instead (https://github.com/ansible-collections/community.crypto/pull/457).
v1.9.15
=======
Release Summary
---------------
Maintenance release.
Bugfixes
--------
- Include ``PSF-license.txt`` file for ``plugins/module_utils/_version.py``.
v1.9.14
=======
Release Summary
---------------
Regular bugfix release.
Bugfixes
--------
- Make collection more robust when PyOpenSSL is used with an incompatible cryptography version (https://github.com/ansible-collections/community.crypto/pull/446).
- openssh_* modules - fix exception handling to report traceback to users for enhanced traceability (https://github.com/ansible-collections/community.crypto/pull/417).
- x509_crl - fix crash when ``issuer`` for a revoked certificate is specified (https://github.com/ansible-collections/community.crypto/pull/441).
v1.9.13
=======
Release Summary
---------------
Regular bugfix release.
Bugfixes
--------
- luks_device - fix parsing of ``lsblk`` output when device name ends with ``crypt`` (https://github.com/ansible-collections/community.crypto/issues/409, https://github.com/ansible-collections/community.crypto/pull/410).
v1.9.12
=======
Release Summary
---------------
Regular bugfix release.
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).
- x509_certificate - for the ``ownca`` provider, check whether the CA private key actually belongs to the CA certificate. This fix only covers the ``cryptography`` backend, not the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/407).
- x509_certificate - regenerate certificate when the CA's public key changes for ``provider=ownca``. This fix only covers the ``cryptography`` backend, not the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/407).
- x509_certificate - regenerate certificate when the CA's subject changes for ``provider=ownca`` (https://github.com/ansible-collections/community.crypto/issues/400, https://github.com/ansible-collections/community.crypto/pull/402).
- x509_certificate - regenerate certificate when the private key changes for ``provider=selfsigned``. This fix only covers the ``cryptography`` backend, not the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/407).
Known Issues
------------
- x509_certificate - when using the ``ownca`` provider with the ``pyopenssl`` backend, changing the CA's public key does not cause regeneration of the certificate (https://github.com/ansible-collections/community.crypto/pull/407).
- x509_certificate - when using the ``ownca`` provider with the ``pyopenssl`` backend, it is possible to specify a CA private key which is not related to the CA certificate (https://github.com/ansible-collections/community.crypto/pull/407).
- x509_certificate - when using the ``selfsigned`` provider with the ``pyopenssl`` backend, changing the private key does not cause regeneration of the certificate (https://github.com/ansible-collections/community.crypto/pull/407).
v1.9.11
=======
Release Summary
---------------
Bugfix release.
Bugfixes
--------
- openssh_cert - fixed false ``changed`` status for ``host`` certificates when using ``full_idempotence`` (https://github.com/ansible-collections/community.crypto/issues/395, https://github.com/ansible-collections/community.crypto/pull/396).
v1.9.10
=======
Release Summary
---------------
Regular bugfix release.
Bugfixes
--------
- luks_devices - set ``LANG`` and similar environment variables to avoid translated output, which can break some of the module's functionality like key management (https://github.com/ansible-collections/community.crypto/pull/388, https://github.com/ansible-collections/community.crypto/issues/385).
v1.9.9
======
Bugfixes
--------
- Various modules and plugins - use vendored version of ``distutils.version`` instead of the deprecated Python standard library ``distutils`` (https://github.com/ansible-collections/community.crypto/pull/353).
- certificate_complete_chain - do not append root twice if the chain already ends with a root certificate (https://github.com/ansible-collections/community.crypto/pull/360).
- certificate_complete_chain - do not hang when infinite loop is found (https://github.com/ansible-collections/community.crypto/issues/355, https://github.com/ansible-collections/community.crypto/pull/360).
v1.9.8
======
Release Summary
---------------
Documentation fix release. No actual code changes.
v1.9.7
======
Release Summary
---------------
Bugfix release with extra forward compatibility for newer versions of cryptography.
Minor Changes
-------------
- acme_* modules - fix usage of ``fetch_url`` with changes in latest ansible-core ``devel`` branch (https://github.com/ansible-collections/community.crypto/pull/339).
Bugfixes
--------
- acme_certificate - avoid passing multiple certificates to ``cryptography``'s X.509 certificate loader when ``fullchain_dest`` is used (https://github.com/ansible-collections/community.crypto/pull/324).
- get_certificate, openssl_csr_info, x509_certificate_info - add fallback code for extension parsing that works with cryptography 36.0.0 and newer. This code re-serializes de-serialized extensions and thus can return slightly different values if the extension in the original CSR resp. certificate was not canonicalized correctly. This code is currently used as a fallback if the existing code stops working, but we will switch it to be the main code in a future release (https://github.com/ansible-collections/community.crypto/pull/331).
- luks_device - now also runs a built-in LUKS signature cleaner on ``state=absent`` to make sure that also the secondary LUKS2 header is wiped when older versions of wipefs are used (https://github.com/ansible-collections/community.crypto/issues/326, https://github.com/ansible-collections/community.crypto/pull/327).
- openssl_pkcs12 - use new PKCS#12 deserialization infrastructure from cryptography 36.0.0 if available (https://github.com/ansible-collections/community.crypto/pull/302).
v1.9.6
======
Release Summary
---------------
Regular bugfix release.
Bugfixes
--------
- cryptography backend - improve Unicode handling for Python 2 (https://github.com/ansible-collections/community.crypto/pull/313).
v1.9.5
======
Release Summary
---------------
Bugfix release to fully support cryptography 35.0.0.
Bugfixes
--------
- get_certificate - fix compatibility with the cryptography 35.0.0 release (https://github.com/ansible-collections/community.crypto/pull/294).
- openssl_csr_info - fix compatibility with the cryptography 35.0.0 release (https://github.com/ansible-collections/community.crypto/pull/294).
- openssl_csr_info - fix compatibility with the cryptography 35.0.0 release in PyOpenSSL backend (https://github.com/ansible-collections/community.crypto/pull/300).
- openssl_pkcs12 - fix compatibility with the cryptography 35.0.0 release (https://github.com/ansible-collections/community.crypto/pull/296).
- x509_certificate_info - fix compatibility with the cryptography 35.0.0 release (https://github.com/ansible-collections/community.crypto/pull/294).
- x509_certificate_info - fix compatibility with the cryptography 35.0.0 release in PyOpenSSL backend (https://github.com/ansible-collections/community.crypto/pull/300).
v1.9.4
======

48
PSF-license.txt Normal file
View File

@@ -0,0 +1,48 @@
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
--------------------------------------------
1. This LICENSE AGREEMENT is between the Python Software Foundation
("PSF"), and the Individual or Organization ("Licensee") accessing and
otherwise using this software ("Python") in source or binary form and
its associated documentation.
2. Subject to the terms and conditions of this License Agreement, PSF hereby
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
analyze, test, perform and/or display publicly, prepare derivative works,
distribute, and otherwise use Python alone or in any derivative version,
provided, however, that PSF's License Agreement and PSF's notice of copyright,
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Python Software Foundation;
All Rights Reserved" are retained in Python alone or in any derivative version
prepared by Licensee.
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python.
4. PSF is making Python available to Licensee on an "AS IS"
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between PSF and
Licensee. This License Agreement does not grant permission to use PSF
trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.
8. By copying, installing or otherwise using Python, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.

View File

@@ -11,7 +11,7 @@ Please note that this collection does **not** support Windows targets.
## Tested with Ansible
Tested with the current Ansible 2.9, ansible-base 2.10, ansible-core 2.11 and ansible-core 2.12 releases and the current development version of ansible-core. Ansible versions before 2.9.10 are not supported.
Tested with the current Ansible 2.9, ansible-base 2.10, ansible-core 2.11, ansible-core 2.12 and ansible-core 2.13 releases. Ansible versions before 2.9.10 are not supported.
## External requirements

View File

@@ -528,6 +528,148 @@ releases:
changes:
release_summary: Accidental 1.9.1 release. Identical to 1.9.0.
release_date: '2021-08-30'
1.9.10:
changes:
bugfixes:
- luks_devices - set ``LANG`` and similar environment variables to avoid translated
output, which can break some of the module's functionality like key management
(https://github.com/ansible-collections/community.crypto/pull/388, https://github.com/ansible-collections/community.crypto/issues/385).
release_summary: Regular bugfix release.
fragments:
- 1.9.10.yml
- 388-luks_device-i18n.yml
release_date: '2022-02-01'
1.9.11:
changes:
bugfixes:
- openssh_cert - fixed false ``changed`` status for ``host`` certificates when
using ``full_idempotence`` (https://github.com/ansible-collections/community.crypto/issues/395,
https://github.com/ansible-collections/community.crypto/pull/396).
release_summary: Bugfix release.
fragments:
- 1.9.11.yml
- 396-openssh_cert-host-cert-idempotence-fix.yml
release_date: '2022-02-05'
1.9.12:
changes:
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).
- x509_certificate - for the ``ownca`` provider, check whether the CA private
key actually belongs to the CA certificate. This fix only covers the ``cryptography``
backend, not the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/407).
- x509_certificate - regenerate certificate when the CA's public key changes
for ``provider=ownca``. This fix only covers the ``cryptography`` backend,
not the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/407).
- x509_certificate - regenerate certificate when the CA's subject changes for
``provider=ownca`` (https://github.com/ansible-collections/community.crypto/issues/400,
https://github.com/ansible-collections/community.crypto/pull/402).
- x509_certificate - regenerate certificate when the private key changes for
``provider=selfsigned``. This fix only covers the ``cryptography`` backend,
not the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/407).
known_issues:
- x509_certificate - when using the ``ownca`` provider with the ``pyopenssl``
backend, changing the CA's public key does not cause regeneration of the certificate
(https://github.com/ansible-collections/community.crypto/pull/407).
- x509_certificate - when using the ``ownca`` provider with the ``pyopenssl``
backend, it is possible to specify a CA private key which is not related to
the CA certificate (https://github.com/ansible-collections/community.crypto/pull/407).
- x509_certificate - when using the ``selfsigned`` provider with the ``pyopenssl``
backend, changing the private key does not cause regeneration of the certificate
(https://github.com/ansible-collections/community.crypto/pull/407).
release_summary: Regular bugfix release.
fragments:
- 1.9.12.yml
- 402-x509_certificate-ownca-subject.yml
- 403-certificate_complete_chain-same-subject.yml
- 407-x509_certificate-signature.yml
release_date: '2022-02-21'
1.9.13:
changes:
bugfixes:
- luks_device - fix parsing of ``lsblk`` output when device name ends with ``crypt``
(https://github.com/ansible-collections/community.crypto/issues/409, https://github.com/ansible-collections/community.crypto/pull/410).
release_summary: Regular bugfix release.
fragments:
- 1.9.13.yml
- 410-luks_device-lsblk-parsing.yml
release_date: '2022-03-04'
1.9.14:
changes:
bugfixes:
- Make collection more robust when PyOpenSSL is used with an incompatible cryptography
version (https://github.com/ansible-collections/community.crypto/pull/446).
- openssh_* modules - fix exception handling to report traceback to users for
enhanced traceability (https://github.com/ansible-collections/community.crypto/pull/417).
- x509_crl - fix crash when ``issuer`` for a revoked certificate is specified
(https://github.com/ansible-collections/community.crypto/pull/441).
release_summary: Regular bugfix release.
fragments:
- 1.9.14.yml
- 417-openssh_modules-fix-exception-reporting.yml
- 441-x509-crl-cert-issuer.yml
- 446-fix.yml
release_date: '2022-05-09'
1.9.15:
changes:
bugfixes:
- Include ``PSF-license.txt`` file for ``plugins/module_utils/_version.py``.
release_summary: Maintenance release.
fragments:
- 1.9.15.yml
- psf-license.yml
release_date: '2022-05-16'
1.9.16:
changes:
bugfixes:
- Include ``simplified_bsd.txt`` license file for the ECS module utils.
- certificate_complete_chain - do not stop execution if an unsupported signature
algorithm is encountered; warn instead (https://github.com/ansible-collections/community.crypto/pull/457).
release_summary: Maintenance and bugfix release.
fragments:
- 1.9.16.yml
- 457-certificate_complete_chain-unsupported-algorithm.yml
- simplified-bsd-license.yml
release_date: '2022-06-02'
1.9.17:
changes:
bugfixes:
- Include ``Apache-2.0.txt`` file for ``plugins/module_utils/crypto/_obj2txt.py``
and ``plugins/module_utils/crypto/_objects_data.py``.
- openssl_csr - the module no longer crashes with 'permitted_subtrees/excluded_subtrees
must be a non-empty list or None' if only one of ``name_constraints_permitted``
and ``name_constraints_excluded`` is provided (https://github.com/ansible-collections/community.crypto/issues/481).
- x509_crl - do not crash when signing CRL with Ed25519 or Ed448 keys (https://github.com/ansible-collections/community.crypto/issues/473,
https://github.com/ansible-collections/community.crypto/pull/474).
release_summary: Bugfix release.
fragments:
- 1.9.17.yml
- 474-x509_crl-ed25519-ed448.yml
- 481-fix-excluded_subtrees-must-be-a-non-empty-list-or-None.yml
- apache-license.yml
release_date: '2022-06-17'
1.9.18:
changes:
bugfixes:
- openssl_pkcs12 - when using the pyOpenSSL backend, do not crash when trying
to read non-existing other certificates (https://github.com/ansible-collections/community.crypto/issues/486,
https://github.com/ansible-collections/community.crypto/pull/487).
release_summary: Bugfix release.
fragments:
- 1.9.18.yml
- 487-openssl_pkcs12-other-certs-crash.yml
release_date: '2022-07-09'
1.9.19:
changes:
bugfixes:
- openssl_privatekey_pipe - ensure compatibility with newer versions of ansible-core
(https://github.com/ansible-collections/community.crypto/pull/515).
release_summary: Bugfix release.
fragments:
- 1.9.19.yml
- 515-action-module-compat.yml
release_date: '2022-11-01'
1.9.2:
changes:
release_summary: Bugfix release to fix the changelog. No other change compared
@@ -535,6 +677,51 @@ releases:
fragments:
- 1.9.2.yml
release_date: '2021-08-30'
1.9.20:
changes:
bugfixes:
- openssl_publickey_info - do not crash with internal error when public key
cannot be parsed (https://github.com/ansible-collections/community.crypto/pull/551).
release_summary: Bugfix release.
fragments:
- 1.9.20.yml
- 551-publickey-info.yml
release_date: '2023-01-01'
1.9.21:
changes:
bugfixes:
- action plugin helper - fix handling of deprecations for ansible-core 2.14.2
(https://github.com/ansible-collections/community.crypto/pull/572).
- openssl_csr, openssl_csr_pipe - prevent invalid values for ``crl_distribution_points``
that do not have one of ``full_name``, ``relative_name``, and ``crl_issuer``
(https://github.com/ansible-collections/community.crypto/pull/560).
release_summary: Bugfix release.
fragments:
- 1.9.21.yml
- 560-openssl_csr-crl_distribution_points.yml
- 572-action-module.yml
release_date: '2023-04-16'
1.9.22:
changes:
bugfixes:
- openssh_keypair - always generate a new key pair if the private key does not
exist. Previously, the module would fail when ``regenerate=fail`` without
an existing key, contradicting the documentation (https://github.com/ansible-collections/community.crypto/pull/598).
release_summary: Bugfix release.
fragments:
- 1.9.22.yml
- 598-openssh_keypair-generate-new-key.yml
release_date: '2023-06-15'
1.9.23:
changes:
bugfixes:
- openssl_pkcs12 - modify autodetect to not detect pyOpenSSL >= 23.3.0, which
removed PKCS#12 support (https://github.com/ansible-collections/community.crypto/pull/666).
release_summary: Bugfix release.
fragments:
- 1.9.23.yml
- pkcs12.yml
release_date: '2023-10-29'
1.9.3:
changes:
bugfixes:
@@ -562,3 +749,82 @@ releases:
- 279-acme-openssl.yml
- 282-acme_challenge_cert_helper-error.yml
release_date: '2021-09-28'
1.9.5:
changes:
bugfixes:
- get_certificate - fix compatibility with the cryptography 35.0.0 release (https://github.com/ansible-collections/community.crypto/pull/294).
- openssl_csr_info - fix compatibility with the cryptography 35.0.0 release
(https://github.com/ansible-collections/community.crypto/pull/294).
- openssl_csr_info - fix compatibility with the cryptography 35.0.0 release
in PyOpenSSL backend (https://github.com/ansible-collections/community.crypto/pull/300).
- openssl_pkcs12 - fix compatibility with the cryptography 35.0.0 release (https://github.com/ansible-collections/community.crypto/pull/296).
- x509_certificate_info - fix compatibility with the cryptography 35.0.0 release
(https://github.com/ansible-collections/community.crypto/pull/294).
- x509_certificate_info - fix compatibility with the cryptography 35.0.0 release
in PyOpenSSL backend (https://github.com/ansible-collections/community.crypto/pull/300).
release_summary: Bugfix release to fully support cryptography 35.0.0.
fragments:
- 1.9.5.yml
- 294-cryptography-35.0.0.yml
- 296-openssl_pkcs12-cryptography-35.yml
- 300-pyopenssl-cryptography-35.yml
release_date: '2021-10-06'
1.9.6:
changes:
bugfixes:
- cryptography backend - improve Unicode handling for Python 2 (https://github.com/ansible-collections/community.crypto/pull/313).
release_summary: Regular bugfix release.
fragments:
- 1.9.6.yml
- 313-unicode-names.yml
release_date: '2021-10-30'
1.9.7:
changes:
bugfixes:
- acme_certificate - avoid passing multiple certificates to ``cryptography``'s
X.509 certificate loader when ``fullchain_dest`` is used (https://github.com/ansible-collections/community.crypto/pull/324).
- get_certificate, openssl_csr_info, x509_certificate_info - add fallback code
for extension parsing that works with cryptography 36.0.0 and newer. This
code re-serializes de-serialized extensions and thus can return slightly different
values if the extension in the original CSR resp. certificate was not canonicalized
correctly. This code is currently used as a fallback if the existing code
stops working, but we will switch it to be the main code in a future release
(https://github.com/ansible-collections/community.crypto/pull/331).
- luks_device - now also runs a built-in LUKS signature cleaner on ``state=absent``
to make sure that also the secondary LUKS2 header is wiped when older versions
of wipefs are used (https://github.com/ansible-collections/community.crypto/issues/326,
https://github.com/ansible-collections/community.crypto/pull/327).
- openssl_pkcs12 - use new PKCS#12 deserialization infrastructure from cryptography
36.0.0 if available (https://github.com/ansible-collections/community.crypto/pull/302).
minor_changes:
- acme_* modules - fix usage of ``fetch_url`` with changes in latest ansible-core
``devel`` branch (https://github.com/ansible-collections/community.crypto/pull/339).
release_summary: Bugfix release with extra forward compatibility for newer versions
of cryptography.
fragments:
- 1.9.7.yml
- 302-openssl_pkcs12-cryptography-36.0.0.yml
- 324-acme_certificate-fullchain.yml
- 327-luks_device-wipe.yml
- 331-cryptography-extensions.yml
- fetch_url-devel.yml
release_date: '2021-11-22'
1.9.8:
changes:
release_summary: Documentation fix release. No actual code changes.
fragments:
- 1.9.8.yml
release_date: '2021-12-13'
1.9.9:
changes:
bugfixes:
- Various modules and plugins - use vendored version of ``distutils.version``
instead of the deprecated Python standard library ``distutils`` (https://github.com/ansible-collections/community.crypto/pull/353).
- certificate_complete_chain - do not append root twice if the chain already
ends with a root certificate (https://github.com/ansible-collections/community.crypto/pull/360).
- certificate_complete_chain - do not hang when infinite loop is found (https://github.com/ansible-collections/community.crypto/issues/355,
https://github.com/ansible-collections/community.crypto/pull/360).
fragments:
- 353-distutils.version.yml
- 360-certificate_complete_chain-loop.yml
release_date: '2022-01-11'

View File

@@ -3,7 +3,7 @@
How to create a small CA
========================
The `community.crypto collection <https://galaxy.ansible.com/community/crypto>`_ offers multiple modules that create private keys, certificate signing requests, and certificates. This guide shows how to create your own small CA and how to use it to sign certificates.
The `community.crypto collection <https://galaxy.ansible.com/ui/repo/published/community/crypto/>`_ offers multiple modules that create private keys, certificate signing requests, and certificates. This guide shows how to create your own small CA and how to use it to sign certificates.
In all examples, we assume that the CA's private key is password protected, where the password is provided in the ``secret_ca_passphrase`` variable.
@@ -71,7 +71,7 @@ In the following example, we assume that the certificate to sign (including its
- name: Sign certificate with our CA
community.crypto.x509_certificate_pipe:
csr_content: "{{ ca_csr.csr }}"
csr_content: "{{ csr.csr }}"
provider: ownca
ownca_path: /path/to/ca-certificate.pem
ownca_privatekey_path: /path/to/ca-certificate.key
@@ -128,7 +128,7 @@ Please note that the above procedure is **not idempotent**. The following extend
- name: Sign certificate with our CA
community.crypto.x509_certificate_pipe:
content: "{{ (certificate.content | b64decode) if certificate_exists.stat.exists else omit }}"
csr_content: "{{ ca_csr.csr }}"
csr_content: "{{ csr.csr }}"
provider: ownca
ownca_path: /path/to/ca-certificate.pem
ownca_privatekey_path: /path/to/ca-certificate.key

View File

@@ -3,7 +3,7 @@
How to create self-signed certificates
======================================
The `community.crypto collection <https://galaxy.ansible.com/community/crypto>`_ offers multiple modules that create private keys, certificate signing requests, and certificates. This guide shows how to create self-signed certificates.
The `community.crypto collection <https://galaxy.ansible.com/ui/repo/published/community/crypto/>`_ offers multiple modules that create private keys, certificate signing requests, and certificates. This guide shows how to create self-signed certificates.
For creating any kind of certificate, you always have to start with a private key. You can use the :ref:`community.crypto.openssl_privatekey module <ansible_collections.community.crypto.openssl_privatekey_module>` to create a private key. If you only specify ``path``, the default parameters will be used. This will result in a 4096 bit RSA private key:

View File

@@ -1,6 +1,6 @@
namespace: community
name: crypto
version: 1.9.4
version: 1.9.23
readme: README.md
authors:
- Ansible (github.com/ansible)

View File

@@ -45,7 +45,7 @@ options:
type: path
privatekey_content:
description:
- Path to the private key to use when signing the certificate.
- Content of the private key to use when signing the certificate.
- This is mutually exclusive with I(privatekey_path).
type: str
@@ -457,8 +457,8 @@ options:
- Time will always be interpreted as UTC.
- Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
+ C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- Note that if using relative time this module is NOT idempotent.
- If this value is not specified, the certificate will start being valid from now.
- Note that this value is B(not used to determine whether an existing certificate should be regenerated).
- This is only used by the C(ownca) provider.
type: str
default: +0s
@@ -470,8 +470,8 @@ options:
- Time will always be interpreted as UTC.
- Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
+ C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- Note that if using relative time this module is NOT idempotent.
- If this value is not specified, the certificate will stop being valid 10 years from now.
- Note that this value is B(not used to determine whether an existing certificate should be regenerated).
- This is only used by the C(ownca) provider.
- On macOS 10.15 and onwards, TLS server certificates must have a validity period of 825 days or fewer.
Please see U(https://support.apple.com/en-us/HT210176) for more details.
@@ -548,8 +548,8 @@ options:
- Time will always be interpreted as UTC.
- Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
+ C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- Note that if using relative time this module is NOT idempotent.
- If this value is not specified, the certificate will start being valid from now.
- Note that this value is B(not used to determine whether an existing certificate should be regenerated).
- This is only used by the C(selfsigned) provider.
type: str
default: +0s
@@ -562,8 +562,8 @@ options:
- Time will always be interpreted as UTC.
- Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
+ C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- Note that if using relative time this module is NOT idempotent.
- If this value is not specified, the certificate will stop being valid 10 years from now.
- Note that this value is B(not used to determine whether an existing certificate should be regenerated).
- This is only used by the C(selfsigned) provider.
- On macOS 10.15 and onwards, TLS server certificates must have a validity period of 825 days or fewer.
Please see U(https://support.apple.com/en-us/HT210176) for more details.

View File

@@ -100,7 +100,7 @@ options:
description:
- Determines which format the private key is written in. By default, PKCS1 (traditional OpenSSL format)
is used for all keys which support it. Please note that not every key can be exported in any format.
- The value C(auto) selects a fromat based on the key format. The value C(auto_ignore) does the same,
- The value C(auto) selects a format based on the key format. The value C(auto_ignore) does the same,
but for existing private key files, it will not force a regenerate when its format is not the automatically
selected one for generation.
- Note that if the format for an existing private key mismatches, the key is B(regenerated) by default.

View File

@@ -0,0 +1,343 @@
# Vendored copy of distutils/version.py from CPython 3.9.5
#
# Implements multiple version numbering conventions for the
# Python Module Distribution Utilities.
#
# PSF License (see PSF-license.txt or https://opensource.org/licenses/Python-2.0)
#
"""Provides classes to represent module version numbers (one class for
each style of version numbering). There are currently two such classes
implemented: StrictVersion and LooseVersion.
Every version number class implements the following interface:
* the 'parse' method takes a string and parses it to some internal
representation; if the string is an invalid version number,
'parse' raises a ValueError exception
* the class constructor takes an optional string argument which,
if supplied, is passed to 'parse'
* __str__ reconstructs the string that was passed to 'parse' (or
an equivalent string -- ie. one that will generate an equivalent
version number instance)
* __repr__ generates Python code to recreate the version number instance
* _cmp compares the current instance with either another instance
of the same class or a string (which will be parsed to an instance
of the same class, thus must follow the same rules)
"""
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import re
try:
RE_FLAGS = re.VERBOSE | re.ASCII
except AttributeError:
RE_FLAGS = re.VERBOSE
class Version:
"""Abstract base class for version numbering classes. Just provides
constructor (__init__) and reproducer (__repr__), because those
seem to be the same for all version numbering classes; and route
rich comparisons to _cmp.
"""
def __init__(self, vstring=None):
if vstring:
self.parse(vstring)
def __repr__(self):
return "%s ('%s')" % (self.__class__.__name__, str(self))
def __eq__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c == 0
def __lt__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c < 0
def __le__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c <= 0
def __gt__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c > 0
def __ge__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c >= 0
# Interface for version-number classes -- must be implemented
# by the following classes (the concrete ones -- Version should
# be treated as an abstract class).
# __init__ (string) - create and take same action as 'parse'
# (string parameter is optional)
# parse (string) - convert a string representation to whatever
# internal representation is appropriate for
# this style of version numbering
# __str__ (self) - convert back to a string; should be very similar
# (if not identical to) the string supplied to parse
# __repr__ (self) - generate Python code to recreate
# the instance
# _cmp (self, other) - compare two version numbers ('other' may
# be an unparsed version string, or another
# instance of your version class)
class StrictVersion(Version):
"""Version numbering for anal retentives and software idealists.
Implements the standard interface for version number classes as
described above. A version number consists of two or three
dot-separated numeric components, with an optional "pre-release" tag
on the end. The pre-release tag consists of the letter 'a' or 'b'
followed by a number. If the numeric components of two version
numbers are equal, then one with a pre-release tag will always
be deemed earlier (lesser) than one without.
The following are valid version numbers (shown in the order that
would be obtained by sorting according to the supplied cmp function):
0.4 0.4.0 (these two are equivalent)
0.4.1
0.5a1
0.5b3
0.5
0.9.6
1.0
1.0.4a3
1.0.4b1
1.0.4
The following are examples of invalid version numbers:
1
2.7.2.2
1.3.a4
1.3pl1
1.3c4
The rationale for this version numbering system will be explained
in the distutils documentation.
"""
version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
RE_FLAGS)
def parse(self, vstring):
match = self.version_re.match(vstring)
if not match:
raise ValueError("invalid version number '%s'" % vstring)
(major, minor, patch, prerelease, prerelease_num) = \
match.group(1, 2, 4, 5, 6)
if patch:
self.version = tuple(map(int, [major, minor, patch]))
else:
self.version = tuple(map(int, [major, minor])) + (0,)
if prerelease:
self.prerelease = (prerelease[0], int(prerelease_num))
else:
self.prerelease = None
def __str__(self):
if self.version[2] == 0:
vstring = '.'.join(map(str, self.version[0:2]))
else:
vstring = '.'.join(map(str, self.version))
if self.prerelease:
vstring = vstring + self.prerelease[0] + str(self.prerelease[1])
return vstring
def _cmp(self, other):
if isinstance(other, str):
other = StrictVersion(other)
elif not isinstance(other, StrictVersion):
return NotImplemented
if self.version != other.version:
# numeric versions don't match
# prerelease stuff doesn't matter
if self.version < other.version:
return -1
else:
return 1
# have to compare prerelease
# case 1: neither has prerelease; they're equal
# case 2: self has prerelease, other doesn't; other is greater
# case 3: self doesn't have prerelease, other does: self is greater
# case 4: both have prerelease: must compare them!
if (not self.prerelease and not other.prerelease):
return 0
elif (self.prerelease and not other.prerelease):
return -1
elif (not self.prerelease and other.prerelease):
return 1
elif (self.prerelease and other.prerelease):
if self.prerelease == other.prerelease:
return 0
elif self.prerelease < other.prerelease:
return -1
else:
return 1
else:
raise AssertionError("never get here")
# end class StrictVersion
# The rules according to Greg Stein:
# 1) a version number has 1 or more numbers separated by a period or by
# sequences of letters. If only periods, then these are compared
# left-to-right to determine an ordering.
# 2) sequences of letters are part of the tuple for comparison and are
# compared lexicographically
# 3) recognize the numeric components may have leading zeroes
#
# The LooseVersion class below implements these rules: a version number
# string is split up into a tuple of integer and string components, and
# comparison is a simple tuple comparison. This means that version
# numbers behave in a predictable and obvious way, but a way that might
# not necessarily be how people *want* version numbers to behave. There
# wouldn't be a problem if people could stick to purely numeric version
# numbers: just split on period and compare the numbers as tuples.
# However, people insist on putting letters into their version numbers;
# the most common purpose seems to be:
# - indicating a "pre-release" version
# ('alpha', 'beta', 'a', 'b', 'pre', 'p')
# - indicating a post-release patch ('p', 'pl', 'patch')
# but of course this can't cover all version number schemes, and there's
# no way to know what a programmer means without asking him.
#
# The problem is what to do with letters (and other non-numeric
# characters) in a version number. The current implementation does the
# obvious and predictable thing: keep them as strings and compare
# lexically within a tuple comparison. This has the desired effect if
# an appended letter sequence implies something "post-release":
# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
#
# However, if letters in a version number imply a pre-release version,
# the "obvious" thing isn't correct. Eg. you would expect that
# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
# implemented here, this just isn't so.
#
# Two possible solutions come to mind. The first is to tie the
# comparison algorithm to a particular set of semantic rules, as has
# been done in the StrictVersion class above. This works great as long
# as everyone can go along with bondage and discipline. Hopefully a
# (large) subset of Python module programmers will agree that the
# particular flavour of bondage and discipline provided by StrictVersion
# provides enough benefit to be worth using, and will submit their
# version numbering scheme to its domination. The free-thinking
# anarchists in the lot will never give in, though, and something needs
# to be done to accommodate them.
#
# Perhaps a "moderately strict" version class could be implemented that
# lets almost anything slide (syntactically), and makes some heuristic
# assumptions about non-digits in version number strings. This could
# sink into special-case-hell, though; if I was as talented and
# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
# just as happy dealing with things like "2g6" and "1.13++". I don't
# think I'm smart enough to do it right though.
#
# In any case, I've coded the test suite for this module (see
# ../test/test_version.py) specifically to fail on things like comparing
# "1.2a2" and "1.2". That's not because the *code* is doing anything
# wrong, it's because the simple, obvious design doesn't match my
# complicated, hairy expectations for real-world version numbers. It
# would be a snap to fix the test suite to say, "Yep, LooseVersion does
# the Right Thing" (ie. the code matches the conception). But I'd rather
# have a conception that matches common notions about version numbers.
class LooseVersion(Version):
"""Version numbering for anarchists and software realists.
Implements the standard interface for version number classes as
described above. A version number consists of a series of numbers,
separated by either periods or strings of letters. When comparing
version numbers, the numeric components will be compared
numerically, and the alphabetic components lexically. The following
are all valid version numbers, in no particular order:
1.5.1
1.5.2b2
161
3.10a
8.02
3.4j
1996.07.12
3.2.pl0
3.1.1.6
2g6
11g
0.960923
2.2beta29
1.13++
5.5.kw
2.0b1pl0
In fact, there is no such thing as an invalid version number under
this scheme; the rules for comparison are simple and predictable,
but may not always give the results you want (for some definition
of "want").
"""
component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
def __init__(self, vstring=None):
if vstring:
self.parse(vstring)
def parse(self, vstring):
# I've given up on thinking I can reconstruct the version string
# from the parsed tuple -- so I just store the string here for
# use by __str__
self.vstring = vstring
components = [x for x in self.component_re.split(vstring) if x and x != '.']
for i, obj in enumerate(components):
try:
components[i] = int(obj)
except ValueError:
pass
self.version = components
def __str__(self):
return self.vstring
def __repr__(self):
return "LooseVersion ('%s')" % str(self)
def _cmp(self, other):
if isinstance(other, str):
other = LooseVersion(other)
elif not isinstance(other, LooseVersion):
return NotImplemented
if self.version == other.version:
return 0
if self.version < other.version:
return -1
if self.version > other.version:
return 1
# end class LooseVersion

View File

@@ -14,8 +14,9 @@ import json
import locale
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.urls import fetch_url
from ansible.module_utils.common.text.converters import to_bytes
from ansible.module_utils.urls import fetch_url
from ansible.module_utils.six import PY3
from ansible_collections.community.crypto.plugins.module_utils.acme.backend_openssl_cli import (
OpenSSLCLIBackend,
@@ -228,9 +229,14 @@ class ACMEClient(object):
resp, info = fetch_url(self.module, url, data=data, headers=headers, method='POST')
_assert_fetch_url_success(self.module, resp, info)
result = {}
try:
# In Python 2, reading from a closed response yields a TypeError.
# In Python 3, read() simply returns ''
if PY3 and resp.closed:
raise TypeError
content = resp.read()
except AttributeError:
except (AttributeError, TypeError):
content = info.pop('body', None)
if content or not parse_json_result:
@@ -284,8 +290,12 @@ class ACMEClient(object):
_assert_fetch_url_success(self.module, resp, info)
try:
# In Python 2, reading from a closed response yields a TypeError.
# In Python 3, read() simply returns ''
if PY3 and resp.closed:
raise TypeError
content = resp.read()
except AttributeError:
except (AttributeError, TypeError):
content = info.pop('body', None)
# Process result

View File

@@ -14,7 +14,9 @@ import datetime
import os
import sys
from ansible.module_utils.common.text.converters import to_bytes, to_native
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.acme.backends import (
CryptoBackend,
@@ -41,6 +43,10 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptograp
cryptography_name_to_oid,
)
from ansible_collections.community.crypto.plugins.module_utils.crypto.pem import (
extract_first_pem,
)
try:
import cryptography
import cryptography.hazmat.backends
@@ -53,7 +59,6 @@ try:
import cryptography.hazmat.primitives.serialization
import cryptography.x509
import cryptography.x509.oid
from distutils.version import LooseVersion
CRYPTOGRAPHY_VERSION = cryptography.__version__
HAS_CURRENT_CRYPTOGRAPHY = (LooseVersion(CRYPTOGRAPHY_VERSION) >= LooseVersion('1.5'))
if HAS_CURRENT_CRYPTOGRAPHY:
@@ -357,6 +362,9 @@ class CryptographyBackend(CryptoBackend):
if cert_content is None:
return -1
# Make sure we have at most one PEM. Otherwise cryptography 36.0.0 will barf.
cert_content = to_bytes(extract_first_pem(to_text(cert_content)) or '')
try:
cert = cryptography.x509.load_pem_x509_certificate(cert_content, _cryptography_backend)
except Exception as e:

View File

@@ -7,8 +7,8 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible.module_utils.six import binary_type
from ansible.module_utils.common.text.converters import to_text
from ansible.module_utils.six import binary_type, PY3
def format_error_problem(problem, subproblem_prefix=''):
@@ -52,8 +52,12 @@ class ACMEProtocolException(ModuleFailException):
# Try to get hold of content, if response is given and content is not provided
if content is None and content_json is None and response is not None:
try:
# In Python 2, reading from a closed response yields a TypeError.
# In Python 3, read() simply returns ''
if PY3 and response.closed:
raise TypeError
content = response.read()
except AttributeError:
except (AttributeError, TypeError):
content = info.pop('body', None)
# Make sure that content_json is None or a dictionary

View File

@@ -2,6 +2,8 @@
# 2.0, and the BSD License. See the LICENSE file at
# https://github.com/pyca/cryptography/blob/master/LICENSE for complete details.
#
# The Apache 2.0 license has been included as Apache-2.0.txt in this collection.
#
# Adapted from cryptography's hazmat/backends/openssl/decode_asn1.py
#
# Copyright (c) 2015, 2016 Paul Kehrer (@reaperhulk)
@@ -20,6 +22,10 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
# WARNING: this function no longer works with cryptography 35.0.0 and newer!
# It must **ONLY** be used in compatibility code for older
# cryptography versions!
def obj2txt(openssl_lib, openssl_ffi, obj):
# Set to 80 on the recommendation of
# https://www.openssl.org/docs/crypto/OBJ_nid2ln.html#return_values

View File

@@ -5,7 +5,7 @@
# In case the following data structure has any copyrightable content, note that it is licensed as follows:
# Copyright (c) the OpenSSL contributors
# Licensed under the Apache License 2.0
# https://github.com/openssl/openssl/blob/master/LICENSE
# https://github.com/openssl/openssl/blob/master/LICENSE.txt or Apache-2.0.txt
from __future__ import absolute_import, division, print_function
__metaclass__ = type

View File

@@ -20,13 +20,13 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
from distutils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
try:
import OpenSSL # noqa
from OpenSSL import crypto # noqa
HAS_PYOPENSSL = True
except ImportError:
except (ImportError, AttributeError):
# Error handled in the calling module.
HAS_PYOPENSSL = False

View File

@@ -26,15 +26,41 @@ import re
from ansible.module_utils.common.text.converters import to_text, to_bytes
from ._asn1 import serialize_asn1_string_as_der
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
try:
import cryptography
from cryptography import x509
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
import ipaddress
except ImportError:
# Error handled in the calling module.
pass
try:
import cryptography.hazmat.primitives.asymmetric.rsa
except ImportError:
pass
try:
import cryptography.hazmat.primitives.asymmetric.ec
except ImportError:
pass
try:
import cryptography.hazmat.primitives.asymmetric.dsa
except ImportError:
pass
try:
import cryptography.hazmat.primitives.asymmetric.ed25519
except ImportError:
pass
try:
import cryptography.hazmat.primitives.asymmetric.ed448
except ImportError:
pass
try:
# This is a separate try/except since this is only present in cryptography 2.5 or newer
from cryptography.hazmat.primitives.serialization.pkcs12 import (
@@ -44,9 +70,23 @@ except ImportError:
# Error handled in the calling module.
_load_key_and_certificates = None
try:
# This is a separate try/except since this is only present in cryptography 36.0.0 or newer
from cryptography.hazmat.primitives.serialization.pkcs12 import (
load_pkcs12 as _load_pkcs12,
)
except ImportError:
# Error handled in the calling module.
_load_pkcs12 = None
from .basic import (
CRYPTOGRAPHY_HAS_DSA_SIGN,
CRYPTOGRAPHY_HAS_EC_SIGN,
CRYPTOGRAPHY_HAS_ED25519,
CRYPTOGRAPHY_HAS_ED25519_SIGN,
CRYPTOGRAPHY_HAS_ED448,
CRYPTOGRAPHY_HAS_ED448_SIGN,
CRYPTOGRAPHY_HAS_RSA_SIGN,
OpenSSLObjectError,
)
@@ -64,60 +104,114 @@ DOTTED_OID = re.compile(r'^\d+(?:\.\d+)+$')
def cryptography_get_extensions_from_cert(cert):
# Since cryptography won't give us the DER value for an extension
# (that is only stored for unrecognized extensions), we have to re-do
# the extension parsing outselves.
result = dict()
backend = cert._backend
x509_obj = cert._x509
try:
# Since cryptography won't give us the DER value for an extension
# (that is only stored for unrecognized extensions), we have to re-do
# the extension parsing outselves.
backend = default_backend()
try:
# For certain old versions of cryptography, backend is a MultiBackend object,
# which has no _lib attribute. In that case, revert to the old approach.
backend._lib
except AttributeError:
backend = cert._backend
x509_obj = cert._x509
# With cryptography 35.0.0, we can no longer use obj2txt. Unfortunately it still does
# not allow to get the raw value of an extension, so we have to use this ugly hack:
exts = list(cert.extensions)
for i in range(backend._lib.X509_get_ext_count(x509_obj)):
ext = backend._lib.X509_get_ext(x509_obj, i)
if ext == backend._ffi.NULL:
continue
crit = backend._lib.X509_EXTENSION_get_critical(ext)
data = backend._lib.X509_EXTENSION_get_data(ext)
backend.openssl_assert(data != backend._ffi.NULL)
der = backend._ffi.buffer(data.data, data.length)[:]
entry = dict(
critical=(crit == 1),
value=base64.b64encode(der),
)
try:
oid = obj2txt(backend._lib, backend._ffi, backend._lib.X509_EXTENSION_get_object(ext))
except AttributeError:
oid = exts[i].oid.dotted_string
result[oid] = entry
except Exception:
# In case the above method breaks, we likely have cryptography 36.0.0 or newer.
# Use it's public_bytes() feature in that case. We will later switch this around
# so that this code will be the default, but for now this will act as a fallback
# since it will re-serialize de-serialized data, which can be different (if the
# original data was not canonicalized) from what was contained in the certificate.
for ext in cert.extensions:
result[ext.oid.dotted_string] = dict(
critical=ext.critical,
value=base64.b64encode(ext.value.public_bytes()),
)
for i in range(backend._lib.X509_get_ext_count(x509_obj)):
ext = backend._lib.X509_get_ext(x509_obj, i)
if ext == backend._ffi.NULL:
continue
crit = backend._lib.X509_EXTENSION_get_critical(ext)
data = backend._lib.X509_EXTENSION_get_data(ext)
backend.openssl_assert(data != backend._ffi.NULL)
der = backend._ffi.buffer(data.data, data.length)[:]
entry = dict(
critical=(crit == 1),
value=base64.b64encode(der),
)
oid = obj2txt(backend._lib, backend._ffi, backend._lib.X509_EXTENSION_get_object(ext))
result[oid] = entry
return result
def cryptography_get_extensions_from_csr(csr):
# Since cryptography won't give us the DER value for an extension
# (that is only stored for unrecognized extensions), we have to re-do
# the extension parsing outselves.
result = dict()
backend = csr._backend
try:
# Since cryptography won't give us the DER value for an extension
# (that is only stored for unrecognized extensions), we have to re-do
# the extension parsing outselves.
backend = default_backend()
try:
# For certain old versions of cryptography, backend is a MultiBackend object,
# which has no _lib attribute. In that case, revert to the old approach.
backend._lib
except AttributeError:
backend = csr._backend
extensions = backend._lib.X509_REQ_get_extensions(csr._x509_req)
extensions = backend._ffi.gc(
extensions,
lambda ext: backend._lib.sk_X509_EXTENSION_pop_free(
ext,
backend._ffi.addressof(backend._lib._original_lib, "X509_EXTENSION_free")
extensions = backend._lib.X509_REQ_get_extensions(csr._x509_req)
extensions = backend._ffi.gc(
extensions,
lambda ext: backend._lib.sk_X509_EXTENSION_pop_free(
ext,
backend._ffi.addressof(backend._lib._original_lib, "X509_EXTENSION_free")
)
)
)
for i in range(backend._lib.sk_X509_EXTENSION_num(extensions)):
ext = backend._lib.sk_X509_EXTENSION_value(extensions, i)
if ext == backend._ffi.NULL:
continue
crit = backend._lib.X509_EXTENSION_get_critical(ext)
data = backend._lib.X509_EXTENSION_get_data(ext)
backend.openssl_assert(data != backend._ffi.NULL)
der = backend._ffi.buffer(data.data, data.length)[:]
entry = dict(
critical=(crit == 1),
value=base64.b64encode(der),
)
oid = obj2txt(backend._lib, backend._ffi, backend._lib.X509_EXTENSION_get_object(ext))
result[oid] = entry
# With cryptography 35.0.0, we can no longer use obj2txt. Unfortunately it still does
# not allow to get the raw value of an extension, so we have to use this ugly hack:
exts = list(csr.extensions)
for i in range(backend._lib.sk_X509_EXTENSION_num(extensions)):
ext = backend._lib.sk_X509_EXTENSION_value(extensions, i)
if ext == backend._ffi.NULL:
continue
crit = backend._lib.X509_EXTENSION_get_critical(ext)
data = backend._lib.X509_EXTENSION_get_data(ext)
backend.openssl_assert(data != backend._ffi.NULL)
der = backend._ffi.buffer(data.data, data.length)[:]
entry = dict(
critical=(crit == 1),
value=base64.b64encode(der),
)
try:
oid = obj2txt(backend._lib, backend._ffi, backend._lib.X509_EXTENSION_get_object(ext))
except AttributeError:
oid = exts[i].oid.dotted_string
result[oid] = entry
except Exception:
# In case the above method breaks, we likely have cryptography 36.0.0 or newer.
# Use it's public_bytes() feature in that case. We will later switch this around
# so that this code will be the default, but for now this will act as a fallback
# since it will re-serialize de-serialized data, which can be different (if the
# original data was not canonicalized) from what was contained in the CSR.
for ext in csr.extensions:
result[ext.oid.dotted_string] = dict(
critical=ext.critical,
value=base64.b64encode(ext.value.public_bytes()),
)
return result
@@ -280,11 +374,11 @@ def _dn_escape_value(value):
'''
Escape Distinguished Name's attribute value.
'''
value = value.replace('\\', '\\\\')
for ch in [',', '#', '+', '<', '>', ';', '"', '=', '/']:
value = value.replace(ch, '\\%s' % ch)
if value.startswith(' '):
value = r'\ ' + value[1:]
value = value.replace(u'\\', u'\\\\')
for ch in [u',', u'#', u'+', u'<', u'>', u';', u'"', u'=', u'/']:
value = value.replace(ch, u'\\%s' % ch)
if value.startswith(u' '):
value = u'\\ ' + value[1:]
return value
@@ -294,24 +388,24 @@ def cryptography_decode_name(name):
Raises an OpenSSLObjectError if the name is not supported.
'''
if isinstance(name, x509.DNSName):
return 'DNS:{0}'.format(name.value)
return u'DNS:{0}'.format(name.value)
if isinstance(name, x509.IPAddress):
if isinstance(name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network)):
return 'IP:{0}/{1}'.format(name.value.network_address.compressed, name.value.prefixlen)
return 'IP:{0}'.format(name.value.compressed)
return u'IP:{0}/{1}'.format(name.value.network_address.compressed, name.value.prefixlen)
return u'IP:{0}'.format(name.value.compressed)
if isinstance(name, x509.RFC822Name):
return 'email:{0}'.format(name.value)
return u'email:{0}'.format(name.value)
if isinstance(name, x509.UniformResourceIdentifier):
return 'URI:{0}'.format(name.value)
return u'URI:{0}'.format(name.value)
if isinstance(name, x509.DirectoryName):
return 'dirName:' + ''.join([
'/{0}={1}'.format(cryptography_oid_to_name(attribute.oid, short=True), _dn_escape_value(attribute.value))
return u'dirName:' + u''.join([
u'/{0}={1}'.format(to_text(cryptography_oid_to_name(attribute.oid, short=True)), _dn_escape_value(attribute.value))
for attribute in name.value
])
if isinstance(name, x509.RegisteredID):
return 'RID:{0}'.format(name.value.dotted_string)
return u'RID:{0}'.format(name.value.dotted_string)
if isinstance(name, x509.OtherName):
return 'otherName:{0};{1}'.format(name.type_id.dotted_string, _get_hex(name.value))
return u'otherName:{0};{1}'.format(name.type_id.dotted_string, _get_hex(name.value))
raise OpenSSLObjectError('Cannot decode name "{0}"'.format(name))
@@ -442,17 +536,111 @@ def cryptography_serial_number_of_cert(cert):
def parse_pkcs12(pkcs12_bytes, passphrase=None):
'''Returns a tuple (private_key, certificate, additional_certificates, friendly_name).
'''
if _load_key_and_certificates is None:
raise ValueError('load_key_and_certificates() not present in the current cryptography version')
private_key, certificate, additional_certificates = _load_key_and_certificates(
pkcs12_bytes, to_bytes(passphrase) if passphrase is not None else None)
if _load_pkcs12 is None and _load_key_and_certificates is None:
raise ValueError('neither load_pkcs12() nor load_key_and_certificates() present in the current cryptography version')
if passphrase is not None:
passphrase = to_bytes(passphrase)
# Main code for cryptography 36.0.0 and forward
if _load_pkcs12 is not None:
return _parse_pkcs12_36_0_0(pkcs12_bytes, passphrase)
if LooseVersion(cryptography.__version__) >= LooseVersion('35.0'):
return _parse_pkcs12_35_0_0(pkcs12_bytes, passphrase)
return _parse_pkcs12_legacy(pkcs12_bytes, passphrase)
def _parse_pkcs12_36_0_0(pkcs12_bytes, passphrase=None):
# Requires cryptography 36.0.0 or newer
pkcs12 = _load_pkcs12(pkcs12_bytes, passphrase)
additional_certificates = [cert.certificate for cert in pkcs12.additional_certs]
private_key = pkcs12.key
certificate = None
friendly_name = None
if pkcs12.cert:
certificate = pkcs12.cert.certificate
friendly_name = pkcs12.cert.friendly_name
return private_key, certificate, additional_certificates, friendly_name
def _parse_pkcs12_35_0_0(pkcs12_bytes, passphrase=None):
# Backwards compatibility code for cryptography 35.x
private_key, certificate, additional_certificates = _load_key_and_certificates(pkcs12_bytes, passphrase)
friendly_name = None
if certificate:
# See https://github.com/pyca/cryptography/issues/5760#issuecomment-842687238
maybe_name = certificate._backend._lib.X509_alias_get0(
certificate._x509, certificate._backend._ffi.NULL)
if maybe_name != certificate._backend._ffi.NULL:
friendly_name = certificate._backend._ffi.string(maybe_name)
backend = default_backend()
# This code basically does what load_key_and_certificates() does, but without error-checking.
# Since load_key_and_certificates succeeded, it should not fail.
pkcs12 = backend._ffi.gc(
backend._lib.d2i_PKCS12_bio(backend._bytes_to_bio(pkcs12_bytes).bio, backend._ffi.NULL),
backend._lib.PKCS12_free)
certificate_x509_ptr = backend._ffi.new("X509 **")
with backend._zeroed_null_terminated_buf(to_bytes(passphrase) if passphrase is not None else None) as passphrase_buffer:
backend._lib.PKCS12_parse(
pkcs12,
passphrase_buffer,
backend._ffi.new("EVP_PKEY **"),
certificate_x509_ptr,
backend._ffi.new("Cryptography_STACK_OF_X509 **"))
if certificate_x509_ptr[0] != backend._ffi.NULL:
maybe_name = backend._lib.X509_alias_get0(certificate_x509_ptr[0], backend._ffi.NULL)
if maybe_name != backend._ffi.NULL:
friendly_name = backend._ffi.string(maybe_name)
return private_key, certificate, additional_certificates, friendly_name
def _parse_pkcs12_legacy(pkcs12_bytes, passphrase=None):
# Backwards compatibility code for cryptography < 35.0.0
private_key, certificate, additional_certificates = _load_key_and_certificates(pkcs12_bytes, passphrase)
friendly_name = None
if certificate:
# See https://github.com/pyca/cryptography/issues/5760#issuecomment-842687238
backend = certificate._backend
maybe_name = backend._lib.X509_alias_get0(certificate._x509, backend._ffi.NULL)
if maybe_name != backend._ffi.NULL:
friendly_name = backend._ffi.string(maybe_name)
return private_key, certificate, additional_certificates, friendly_name
def cryptography_verify_signature(signature, data, hash_algorithm, signer_public_key):
'''
Check whether the given signature of the given data was signed by the given public key object.
'''
try:
if CRYPTOGRAPHY_HAS_RSA_SIGN and isinstance(signer_public_key, cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey):
signer_public_key.verify(signature, data, padding.PKCS1v15(), hash_algorithm)
return True
if CRYPTOGRAPHY_HAS_EC_SIGN and isinstance(signer_public_key, cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey):
signer_public_key.verify(signature, data, cryptography.hazmat.primitives.asymmetric.ec.ECDSA(hash_algorithm))
return True
if CRYPTOGRAPHY_HAS_DSA_SIGN and isinstance(signer_public_key, cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey):
signer_public_key.verify(signature, data, hash_algorithm)
return True
if CRYPTOGRAPHY_HAS_ED25519_SIGN and isinstance(signer_public_key, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey):
signer_public_key.verify(signature, data)
return True
if CRYPTOGRAPHY_HAS_ED448_SIGN and isinstance(signer_public_key, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PublicKey):
signer_public_key.verify(signature, data)
return True
raise OpenSSLObjectError(u'Unsupported public key type {0}'.format(type(signer_public_key)))
except InvalidSignature:
return False
def cryptography_verify_certificate_signature(certificate, signer_public_key):
'''
Check whether the given X509 certificate object was signed by the given public key object.
'''
return cryptography_verify_signature(
certificate.signature,
certificate.tbs_certificate_bytes,
certificate.signature_hash_algorithm,
signer_public_key
)

View File

@@ -11,11 +11,11 @@ __metaclass__ = type
import abc
import traceback
from distutils.version import LooseVersion
from ansible.module_utils import six
from ansible.module_utils.basic import missing_required_lib
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.common import ArgumentSpec
from ansible_collections.community.crypto.plugins.module_utils.crypto.basic import (
@@ -45,7 +45,7 @@ try:
import OpenSSL
from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError:
except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:

View File

@@ -37,7 +37,7 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.module_bac
try:
import OpenSSL
from OpenSSL import crypto
except ImportError:
except (ImportError, AttributeError):
pass
try:

View File

@@ -15,12 +15,12 @@ import datetime
import re
import traceback
from distutils.version import LooseVersion
from ansible.module_utils import six
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.common.text.converters import to_native, to_text, to_bytes
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.crypto.support import (
load_certificate,
get_fingerprint_of_bytes,
@@ -59,7 +59,7 @@ try:
# OpenSSL 1.0.x or older
OPENSSL_MUST_STAPLE_NAME = b"1.3.6.1.5.5.7.1.24"
OPENSSL_MUST_STAPLE_VALUE = b"DER:30:03:02:01:05"
except ImportError:
except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:

View File

@@ -10,11 +10,12 @@ __metaclass__ = type
import os
from distutils.version import LooseVersion
from random import randrange
from ansible.module_utils.common.text.converters import to_bytes
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.crypto.basic import (
OpenSSLBadPassphraseError,
)
@@ -27,8 +28,10 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.support im
)
from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptography_support import (
cryptography_compare_public_keys,
cryptography_key_needs_digest_for_signing,
cryptography_serial_number_of_cert,
cryptography_verify_certificate_signature,
)
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.certificate import (
@@ -40,7 +43,7 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.module_bac
try:
from OpenSSL import crypto
except ImportError:
except (ImportError, AttributeError):
pass
try:
@@ -106,6 +109,9 @@ class OwnCACertificateBackendCryptography(CertificateBackend):
except OpenSSLBadPassphraseError as exc:
module.fail_json(msg=str(exc))
if not cryptography_compare_public_keys(self.ca_cert.public_key(), self.ca_private_key.public_key()):
raise CertificateError('The CA private key does not belong to the CA certificate')
if cryptography_key_needs_digest_for_signing(self.ca_private_key):
if self.digest is None:
raise CertificateError(
@@ -172,6 +178,16 @@ class OwnCACertificateBackendCryptography(CertificateBackend):
if super(OwnCACertificateBackendCryptography, self).needs_regeneration():
return True
self._ensure_existing_certificate_loaded()
# Check whether certificate is signed by CA certificate
if not cryptography_verify_certificate_signature(self.existing_certificate, self.ca_cert.public_key()):
return True
# Check subject
if self.ca_cert.subject != self.existing_certificate.issuer:
return True
# Check AuthorityKeyIdentifier
if self.create_authority_key_identifier:
try:
@@ -184,7 +200,6 @@ class OwnCACertificateBackendCryptography(CertificateBackend):
except cryptography.x509.ExtensionNotFound:
expected_ext = x509.AuthorityKeyIdentifier.from_issuer_public_key(self.ca_cert.public_key())
self._ensure_existing_certificate_loaded()
try:
ext = self.existing_certificate.extensions.get_extension_for_class(x509.AuthorityKeyIdentifier)
if ext.value != expected_ext:
@@ -296,6 +311,18 @@ class OwnCACertificateBackendPyOpenSSL(CertificateBackend):
"""Return bytes for self.cert."""
return crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert)
def needs_regeneration(self):
if super(OwnCACertificateBackendPyOpenSSL, self).needs_regeneration():
return True
self._ensure_existing_certificate_loaded()
# Check subject
if self.ca_cert.get_subject() != self.existing_certificate.get_issuer():
return True
return False
def dump(self, include_certificate):
result = super(OwnCACertificateBackendPyOpenSSL, self).dump(include_certificate)
result.update({

View File

@@ -22,6 +22,7 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.support im
from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptography_support import (
cryptography_key_needs_digest_for_signing,
cryptography_serial_number_of_cert,
cryptography_verify_certificate_signature,
)
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.certificate import (
@@ -32,7 +33,7 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.module_bac
try:
from OpenSSL import crypto
except ImportError:
except (ImportError, AttributeError):
pass
try:
@@ -134,6 +135,18 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend):
"""Return bytes for self.cert."""
return self.cert.public_bytes(Encoding.PEM)
def needs_regeneration(self):
if super(SelfSignedCertificateBackendCryptography, self).needs_regeneration():
return True
self._ensure_existing_certificate_loaded()
# Check whether certificate is signed by private key
if not cryptography_verify_certificate_signature(self.existing_certificate, self.privatekey.public_key()):
return True
return False
def dump(self, include_certificate):
result = super(SelfSignedCertificateBackendCryptography, self).dump(include_certificate)

View File

@@ -9,10 +9,10 @@ __metaclass__ = type
import traceback
from distutils.version import LooseVersion
from ansible.module_utils.basic import missing_required_lib
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptography_support import (
cryptography_oid_to_name,
)

View File

@@ -12,12 +12,12 @@ import abc
import binascii
import traceback
from distutils.version import LooseVersion
from ansible.module_utils import six
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.common.text.converters import to_bytes, to_text
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.crypto.basic import (
OpenSSLObjectError,
OpenSSLBadPassphraseError,
@@ -63,7 +63,7 @@ try:
import OpenSSL
from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError:
except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:
@@ -452,8 +452,12 @@ def parse_crl_distribution_points(module, crl_distribution_points):
reasons=None,
)
if parse_crl_distribution_point['full_name'] is not None:
if not parse_crl_distribution_point['full_name']:
raise OpenSSLObjectError('full_name must not be empty')
params['full_name'] = [cryptography_get_name(name, 'full name') for name in parse_crl_distribution_point['full_name']]
if parse_crl_distribution_point['relative_name'] is not None:
if not parse_crl_distribution_point['relative_name']:
raise OpenSSLObjectError('relative_name must not be empty')
try:
params['relative_name'] = cryptography_parse_relative_distinguished_name(parse_crl_distribution_point['relative_name'])
except Exception:
@@ -462,6 +466,8 @@ def parse_crl_distribution_points(module, crl_distribution_points):
raise OpenSSLObjectError('Cannot specify relative_name for cryptography < 1.6')
raise
if parse_crl_distribution_point['crl_issuer'] is not None:
if not parse_crl_distribution_point['crl_issuer']:
raise OpenSSLObjectError('crl_issuer must not be empty')
params['crl_issuer'] = [cryptography_get_name(name, 'CRL issuer') for name in parse_crl_distribution_point['crl_issuer']]
if parse_crl_distribution_point['reasons'] is not None:
reasons = []
@@ -469,7 +475,7 @@ def parse_crl_distribution_points(module, crl_distribution_points):
reasons.append(REVOCATION_REASON_MAP[reason])
params['reasons'] = frozenset(reasons)
result.append(cryptography.x509.DistributionPoint(**params))
except OpenSSLObjectError as e:
except (OpenSSLObjectError, ValueError) as e:
raise OpenSSLObjectError('Error while parsing CRL distribution point #{index}: {error}'.format(index=index, error=e))
return result
@@ -528,8 +534,8 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
if self.name_constraints_permitted or self.name_constraints_excluded:
try:
csr = csr.add_extension(cryptography.x509.NameConstraints(
[cryptography_get_name(name, 'name constraints permitted') for name in self.name_constraints_permitted],
[cryptography_get_name(name, 'name constraints excluded') for name in self.name_constraints_excluded],
[cryptography_get_name(name, 'name constraints permitted') for name in self.name_constraints_permitted] or None,
[cryptography_get_name(name, 'name constraints excluded') for name in self.name_constraints_excluded] or None,
), critical=self.name_constraints_critical)
except TypeError as e:
raise OpenSSLObjectError('Error while parsing name constraint: {0}'.format(e))
@@ -678,8 +684,8 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
def _check_nameConstraints(extensions):
current_nc_ext = _find_extension(extensions, cryptography.x509.NameConstraints)
current_nc_perm = [to_text(altname) for altname in current_nc_ext.value.permitted_subtrees] if current_nc_ext else []
current_nc_excl = [to_text(altname) for altname in current_nc_ext.value.excluded_subtrees] if current_nc_ext else []
current_nc_perm = [to_text(altname) for altname in current_nc_ext.value.permitted_subtrees or []] if current_nc_ext else []
current_nc_excl = [to_text(altname) for altname in current_nc_ext.value.excluded_subtrees or []] if current_nc_ext else []
nc_perm = [to_text(cryptography_get_name(altname, 'name constraints permitted')) for altname in self.name_constraints_permitted]
nc_excl = [to_text(cryptography_get_name(altname, 'name constraints excluded')) for altname in self.name_constraints_excluded]
if set(nc_perm) != set(current_nc_perm) or set(nc_excl) != set(current_nc_excl):
@@ -851,7 +857,8 @@ def get_csr_argument_spec():
'aa_compromise',
]),
),
mutually_exclusive=[('full_name', 'relative_name')]
mutually_exclusive=[('full_name', 'relative_name')],
required_one_of=[('full_name', 'relative_name', 'crl_issuer')],
),
select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']),
),

View File

@@ -13,12 +13,12 @@ import abc
import binascii
import traceback
from distutils.version import LooseVersion
from ansible.module_utils import six
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.common.text.converters import to_native, to_text, to_bytes
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.crypto.support import (
load_certificate_request,
get_fingerprint_of_bytes,
@@ -57,7 +57,7 @@ try:
# OpenSSL 1.0.x or older
OPENSSL_MUST_STAPLE_NAME = b"1.3.6.1.5.5.7.1.24"
OPENSSL_MUST_STAPLE_VALUE = b"DER:30:03:02:01:05"
except ImportError:
except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:

View File

@@ -12,12 +12,12 @@ import abc
import base64
import traceback
from distutils.version import LooseVersion
from ansible.module_utils import six
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.common.text.converters import to_bytes
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.crypto.basic import (
CRYPTOGRAPHY_HAS_X25519,
CRYPTOGRAPHY_HAS_X25519_FULL,
@@ -54,7 +54,7 @@ try:
import OpenSSL
from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError:
except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:

View File

@@ -12,12 +12,12 @@ __metaclass__ = type
import abc
import traceback
from distutils.version import LooseVersion
from ansible.module_utils import six
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.common.text.converters import to_native, to_bytes
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.crypto.basic import (
CRYPTOGRAPHY_HAS_ED25519,
CRYPTOGRAPHY_HAS_ED448,
@@ -49,7 +49,7 @@ try:
import OpenSSL
from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError:
except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:

View File

@@ -10,12 +10,12 @@ __metaclass__ = type
import abc
import traceback
from distutils.version import LooseVersion
from ansible.module_utils import six
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.common.text.converters import to_native
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.crypto.basic import (
CRYPTOGRAPHY_HAS_X25519,
CRYPTOGRAPHY_HAS_X448,
@@ -38,7 +38,7 @@ try:
import OpenSSL
from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError:
except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:
@@ -215,7 +215,7 @@ class PublicKeyInfoRetrieval(object):
try:
self.key = load_publickey(content=self.content, backend=self.backend)
except OpenSSLObjectError as e:
raise PublicKeyParseError(to_native(e))
raise PublicKeyParseError(to_native(e), {})
pk = self._get_public_key(binary=True)
result['fingerprints'] = get_fingerprint_of_bytes(

View File

@@ -72,3 +72,13 @@ def split_pem_list(text, keep_inbetween=False):
result.append(''.join(current))
current = [] if keep_inbetween else None
return result
def extract_first_pem(text):
'''
Given one PEM or multiple concatenated PEM objects, return only the first one, or None if there is none.
'''
all_pems = split_pem_list(text)
if not all_pems:
return None
return all_pems[0]

View File

@@ -21,13 +21,15 @@ __metaclass__ = type
import base64
from ansible.module_utils.common.text.converters import to_bytes, to_text
from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native
from ansible_collections.community.crypto.plugins.module_utils.compat import ipaddress as compat_ipaddress
from ._objects import OID_LOOKUP
try:
import OpenSSL
except ImportError:
except (ImportError, AttributeError):
# Error handled in the calling module.
pass
@@ -87,18 +89,25 @@ def pyopenssl_get_extensions_from_cert(cert):
critical=bool(ext.get_critical()),
value=base64.b64encode(ext.get_data()),
)
oid = obj2txt(
OpenSSL._util.lib,
OpenSSL._util.ffi,
OpenSSL._util.lib.X509_EXTENSION_get_object(ext._extension)
)
# This could also be done a bit simpler:
#
# oid = obj2txt(OpenSSL._util.lib, OpenSSL._util.ffi, OpenSSL._util.lib.OBJ_nid2obj(ext._nid))
#
# Unfortunately this gives the wrong result in case the linked OpenSSL
# doesn't know the OID. That's why we have to get the OID dotted string
# similarly to how cryptography does it.
try:
oid = obj2txt(
OpenSSL._util.lib,
OpenSSL._util.ffi,
OpenSSL._util.lib.X509_EXTENSION_get_object(ext._extension)
)
# This could also be done a bit simpler:
#
# oid = obj2txt(OpenSSL._util.lib, OpenSSL._util.ffi, OpenSSL._util.lib.OBJ_nid2obj(ext._nid))
#
# Unfortunately this gives the wrong result in case the linked OpenSSL
# doesn't know the OID. That's why we have to get the OID dotted string
# similarly to how cryptography does it.
except AttributeError:
# When PyOpenSSL is used with cryptography >= 35.0.0, obj2txt cannot be used.
# We try to figure out the OID with our internal lookup table, and if we fail,
# we use the short name OpenSSL returns.
oid = to_native(ext.get_short_name())
oid = OID_LOOKUP.get(oid, oid)
result[oid] = entry
return result
@@ -113,18 +122,25 @@ def pyopenssl_get_extensions_from_csr(csr):
critical=bool(ext.get_critical()),
value=base64.b64encode(ext.get_data()),
)
oid = obj2txt(
OpenSSL._util.lib,
OpenSSL._util.ffi,
OpenSSL._util.lib.X509_EXTENSION_get_object(ext._extension)
)
# This could also be done a bit simpler:
#
# oid = obj2txt(OpenSSL._util.lib, OpenSSL._util.ffi, OpenSSL._util.lib.OBJ_nid2obj(ext._nid))
#
# Unfortunately this gives the wrong result in case the linked OpenSSL
# doesn't know the OID. That's why we have to get the OID dotted string
# similarly to how cryptography does it.
try:
oid = obj2txt(
OpenSSL._util.lib,
OpenSSL._util.ffi,
OpenSSL._util.lib.X509_EXTENSION_get_object(ext._extension)
)
# This could also be done a bit simpler:
#
# oid = obj2txt(OpenSSL._util.lib, OpenSSL._util.ffi, OpenSSL._util.lib.OBJ_nid2obj(ext._nid))
#
# Unfortunately this gives the wrong result in case the linked OpenSSL
# doesn't know the OID. That's why we have to get the OID dotted string
# similarly to how cryptography does it.
except AttributeError:
# When PyOpenSSL is used with cryptography >= 35.0.0, obj2txt cannot be used.
# We try to figure out the OID with our internal lookup table, and if we fail,
# we use the short name OpenSSL returns.
oid = to_native(ext.get_short_name())
oid = OID_LOOKUP.get(oid, oid)
result[oid] = entry
return result

View File

@@ -32,7 +32,7 @@ from ansible.module_utils.common.text.converters import to_native, to_bytes
try:
from OpenSSL import crypto
HAS_PYOPENSSL = True
except ImportError:
except (ImportError, AttributeError):
# Error handled in the calling module.
HAS_PYOPENSSL = False

View File

@@ -7,7 +7,7 @@
# their own license to the complete work.
#
# Copyright (c), Entrust Datacard Corporation, 2019
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:

View File

@@ -21,9 +21,11 @@ __metaclass__ = type
import abc
import os
import stat
import traceback
from ansible.module_utils import six
from ansible.module_utils.common.text.converters import to_native
from ansible_collections.community.crypto.plugins.module_utils.openssh.utils import (
parse_openssh_version,
)
@@ -75,7 +77,14 @@ class OpensshModule(object):
self.check_mode = self.module.check_mode
def execute(self):
self._execute()
try:
self._execute()
except Exception as e:
self.module.fail_json(
msg="unexpected error occurred: %s" % to_native(e),
exception=traceback.format_exc(),
)
self.module.exit_json(**self.result)
@abc.abstractmethod

View File

@@ -21,12 +21,13 @@ __metaclass__ = type
import abc
import os
from distutils.version import LooseVersion
from ansible.module_utils import six
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.common.text.converters import to_native, to_text, to_bytes
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.openssh.cryptography import (
HAS_OPENSSH_SUPPORT,
HAS_OPENSSH_PRIVATE_FORMAT,
@@ -170,8 +171,10 @@ class KeypairBackend(OpensshModule):
pass
def _should_generate(self):
if self.regenerate == 'never':
return self.original_private_key is None
if self.original_private_key is None:
return True
elif self.regenerate == 'never':
return False
elif self.regenerate == 'fail':
if not self._private_key_valid():
self.module.fail_json(
@@ -179,7 +182,7 @@ class KeypairBackend(OpensshModule):
"To force regeneration, call the module with `generate` set to " +
"`partial_idempotence`, `full_idempotence` or `always`, or with `force=yes`."
)
return self.original_private_key is None
return False
elif self.regenerate in ('partial_idempotence', 'full_idempotence'):
return not self._private_key_valid()
else:

View File

@@ -20,10 +20,11 @@ __metaclass__ = type
import os
from base64 import b64encode, b64decode
from distutils.version import LooseVersion
from getpass import getuser
from socket import gethostname
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
try:
from cryptography import __version__ as CRYPTOGRAPHY_VERSION
from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Felix Fontein <felix@fontein.de>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""Provide version object to compare version numbers."""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
# Once we drop support for Ansible 2.9, ansible-base 2.10, and ansible-core 2.11, we can
# remove the _version.py file, and replace the following import by
#
# from ansible.module_utils.compat.version import LooseVersion
from ._version import LooseVersion

View File

@@ -54,7 +54,7 @@ EXAMPLES = '''
- name: Check whether an account with the given account key exists
community.crypto.acme_account_info:
account_key_src: /etc/pki/cert/private/account.key
register: account_data
register: account_data
- name: Verify that account exists
assert:
that:
@@ -70,7 +70,7 @@ EXAMPLES = '''
acme_account_info:
account_key_content: "{{ acme_account_key }}"
account_uri: "{{ acme_account_uri }}"
register: account_data
register: account_data
- name: Verify that account exists
assert:
that:
@@ -101,7 +101,7 @@ account:
returned: always
type: list
elements: str
sample: "['mailto:me@example.com', 'tel:00123456789']"
sample: ['mailto:me@example.com', 'tel:00123456789']
status:
description: the account's status
returned: always

View File

@@ -308,7 +308,7 @@ EXAMPLES = r'''
# - copy:
# dest: /var/www/html/{{ sample_com_challenge['challenge_data']['sample.com']['http-01']['resource'] }}
# content: "{{ sample_com_challenge['challenge_data']['sample.com']['http-01']['resource_value'] }}"
# when: sample_com_challenge is changed and 'sample.com' in sample_com_challenge['challenge_data']
# when: sample_com_challenge is changed and 'sample.com' in sample_com_challenge['challenge_data']
#
# Alternative way:
#
@@ -467,7 +467,20 @@ authorizations:
- Maps an identifier to ACME authorization objects. See U(https://tools.ietf.org/html/rfc8555#section-7.1.4).
returned: changed
type: dict
sample: '{"example.com":{...}}'
sample:
example.com:
identifier:
type: dns
value: example.com
status: valid
expires: '2022-08-04T01:02:03.45Z'
challenges:
- url: https://example.org/acme/challenge/12345
type: http-01
status: valid
token: A5b1C3d2E9f8G7h6
validated: '2022-08-01T01:01:02.34Z'
wildcard: false
order_uri:
description: ACME order URI.
returned: changed

View File

@@ -145,6 +145,8 @@ import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_bytes, to_text
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.acme.errors import ModuleFailException
from ansible_collections.community.crypto.plugins.module_utils.acme.io import (
@@ -164,7 +166,6 @@ try:
import cryptography.x509
import cryptography.x509.oid
import ipaddress
from distutils.version import LooseVersion
HAS_CRYPTOGRAPHY = (LooseVersion(cryptography.__version__) >= LooseVersion('1.3'))
_cryptography_backend = cryptography.hazmat.backends.default_backend()
except ImportError as dummy:

View File

@@ -183,7 +183,7 @@ directory:
description: The ACME directory's content
returned: always
type: dict
sample: |
sample:
{
"a85k3x9f91A4": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417",
"keyChange": "https://acme-v02.api.letsencrypt.org/acme/key-change",
@@ -203,7 +203,7 @@ headers:
description: The request's HTTP headers (with lowercase keys)
returned: always
type: dict
sample: |
sample:
{
"boulder-requester": "12345",
"cache-control": "max-age=0, no-cache, no-store",
@@ -214,7 +214,7 @@ headers:
"cookies_string": "",
"date": "Wed, 07 Nov 2018 12:34:56 GMT",
"expires": "Wed, 07 Nov 2018 12:44:56 GMT",
"link": "<https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf>;rel=\"terms-of-service\"",
"link": '<https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf>;rel="terms-of-service"',
"msg": "OK (904 bytes)",
"pragma": "no-cache",
"replay-nonce": "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGH",

View File

@@ -124,6 +124,8 @@ import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_bytes
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.crypto.pem import (
split_pem_list,
)
@@ -131,6 +133,7 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.pem import
CRYPTOGRAPHY_IMP_ERR = None
try:
import cryptography
import cryptography.exceptions
import cryptography.hazmat.backends
import cryptography.hazmat.primitives.serialization
import cryptography.hazmat.primitives.asymmetric.rsa
@@ -140,7 +143,6 @@ try:
import cryptography.hazmat.primitives.asymmetric.utils
import cryptography.x509
import cryptography.x509.oid
from distutils.version import LooseVersion
HAS_CRYPTOGRAPHY = (LooseVersion(cryptography.__version__) >= LooseVersion('1.5'))
_cryptography_backend = cryptography.hazmat.backends.default_backend()
except ImportError as dummy:
@@ -189,6 +191,9 @@ def is_parent(module, cert, potential_parent):
return True
except cryptography.exceptions.InvalidSignature as dummy:
return False
except cryptography.exceptions.UnsupportedAlgorithm as dummy:
module.warn('Unsupported algorithm "{0}"'.format(cert.cert.signature_hash_algorithm))
return False
except Exception as e:
module.fail_json(msg='Unknown error on signature validation: {0}'.format(e))
@@ -236,13 +241,17 @@ class CertificateSet(object):
def __init__(self, module):
self.module = module
self.certificates = set()
self.certificate_by_issuer = dict()
self.certificates_by_issuer = dict()
self.certificate_by_cert = dict()
def _load_file(self, path):
certs = load_PEM_list(self.module, path, fail_on_error=False)
for cert in certs:
self.certificates.add(cert)
self.certificate_by_issuer[cert.cert.subject] = cert
if cert.cert.subject not in self.certificates_by_issuer:
self.certificates_by_issuer[cert.cert.subject] = []
self.certificates_by_issuer[cert.cert.subject].append(cert)
self.certificate_by_cert[cert.cert] = cert
def load(self, path):
'''
@@ -260,8 +269,8 @@ class CertificateSet(object):
'''
Search for the parent (issuer) of a certificate. Return ``None`` if none was found.
'''
potential_parent = self.certificate_by_issuer.get(cert.cert.issuer)
if potential_parent is not None:
potential_parents = self.certificates_by_issuer.get(cert.cert.issuer, [])
for potential_parent in potential_parents:
if is_parent(self.module, cert, potential_parent):
return potential_parent
return None
@@ -274,6 +283,16 @@ def format_cert(cert):
return str(cert.cert)
def check_cycle(module, occured_certificates, next):
'''
Make sure that next is not in occured_certificates so far, and add it.
'''
next_cert = next.cert
if next_cert in occured_certificates:
module.fail_json(msg='Found cycle while building certificate chain')
occured_certificates.add(next_cert)
def main():
module = AnsibleModule(
argument_spec=dict(
@@ -312,13 +331,19 @@ def main():
# Try to complete chain
current = chain[-1]
completed = []
occured_certificates = set([cert.cert for cert in chain])
if current.cert in roots.certificate_by_cert:
# Don't try to complete the chain when it's already ending with a root certificate
current = None
while current:
root = roots.find_parent(current)
if root:
check_cycle(module, occured_certificates, root)
completed.append(root)
break
intermediate = intermediates.find_parent(current)
if intermediate:
check_cycle(module, occured_certificates, intermediate)
completed.append(intermediate)
current = intermediate
else:

View File

@@ -519,11 +519,11 @@ import re
import time
import traceback
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_native, to_bytes
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.io import (
write_file,
)

View File

@@ -167,7 +167,6 @@ import base64
import datetime
import traceback
from distutils.version import LooseVersion
from os.path import isfile
from socket import create_connection, setdefaulttimeout, socket
from ssl import get_server_certificate, DER_cert_to_PEM_cert, CERT_NONE, CERT_REQUIRED
@@ -175,6 +174,8 @@ from ssl import get_server_certificate, DER_cert_to_PEM_cert, CERT_NONE, CERT_RE
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_bytes
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptography_support import (
cryptography_oid_to_name,
cryptography_get_extensions_from_cert,
@@ -197,7 +198,7 @@ try:
import OpenSSL
from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError:
except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:

View File

@@ -350,12 +350,40 @@ STDERR = 2
# used to get <luks-name> out of lsblk output in format 'crypt <luks-name>'
# regex takes care of any possible blank characters
LUKS_NAME_REGEX = re.compile(r'\s*crypt\s+([^\s]*)\s*')
LUKS_NAME_REGEX = re.compile(r'^crypt\s+([^\s]*)\s*$')
# used to get </luks/device> out of lsblk output
# in format 'device: </luks/device>'
LUKS_DEVICE_REGEX = re.compile(r'\s*device:\s+([^\s]*)\s*')
# See https://gitlab.com/cryptsetup/cryptsetup/-/wikis/LUKS-standard/on-disk-format.pdf
LUKS_HEADER = b'LUKS\xba\xbe'
LUKS_HEADER_L = 6
# See https://gitlab.com/cryptsetup/LUKS2-docs/-/blob/master/luks2_doc_wip.pdf
LUKS2_HEADER_OFFSETS = [0x4000, 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000]
LUKS2_HEADER2 = b'SKUL\xba\xbe'
def wipe_luks_headers(device):
wipe_offsets = []
with open(device, 'rb') as f:
# f.seek(0)
data = f.read(LUKS_HEADER_L)
if data == LUKS_HEADER:
wipe_offsets.append(0)
for offset in LUKS2_HEADER_OFFSETS:
f.seek(offset)
data = f.read(LUKS_HEADER_L)
if data == LUKS2_HEADER2:
wipe_offsets.append(offset)
if wipe_offsets:
with open(device, 'wb') as f:
for offset in wipe_offsets:
f.seek(offset)
f.write(b'\x00\x00\x00\x00\x00\x00')
class Handler(object):
def __init__(self, module):
@@ -418,13 +446,11 @@ class CryptHandler(Handler):
raise ValueError('Error while obtaining LUKS name for %s: %s'
% (device, result[STDERR]))
m = LUKS_NAME_REGEX.search(result[STDOUT])
try:
name = m.group(1)
except AttributeError:
name = None
return name
for line in result[STDOUT].splitlines(False):
m = LUKS_NAME_REGEX.match(line)
if m:
return m.group(1)
return None
def get_container_device_by_name(self, name):
''' obtain device name based on the LUKS container name
@@ -515,9 +541,17 @@ class CryptHandler(Handler):
self.run_luks_close(name)
result = self._run_command([wipefs_bin, '--all', device])
if result[RETURN_CODE] != 0:
raise ValueError('Error while wiping luks container %s: %s'
raise ValueError('Error while wiping LUKS container signatures for %s: %s'
% (device, result[STDERR]))
# For LUKS2, sometimes both `cryptsetup erase` and `wipefs` do **not**
# erase all LUKS signatures (they seem to miss the second header). That's
# why we do it ourselves here.
try:
wipe_luks_headers(device)
except Exception as exc:
raise ValueError('Error while wiping LUKS container signatures for %s: %s' % (device, exc))
def run_luks_add_key(self, device, keyfile, passphrase, new_keyfile,
new_passphrase, pbkdf):
''' Add new key from a keyfile or passphrase to given 'device';
@@ -785,6 +819,7 @@ def run_module():
module = AnsibleModule(argument_spec=module_args,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive)
module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C', LC_CTYPE='C')
if module.params['device'] is not None:
try:

View File

@@ -258,11 +258,12 @@ info:
'''
import os
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.common.text.converters import to_native, to_text
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.openssh.backends.common import (
KeygenCommand,
OpensshModule,
@@ -378,7 +379,7 @@ class Certificate(OpensshModule):
def _is_fully_valid(self):
return self._is_partially_valid() and all([
self._compare_options(),
self._compare_options() if self.original_data.type == 'user' else True,
self.original_data.key_id == self.identifier,
self.original_data.public_key == self._get_key_fingerprint(self.public_key),
self.original_data.signing_key == self._get_key_fingerprint(self.signing_key),

View File

@@ -178,7 +178,7 @@ public_key:
description: The public key of the generated SSH private key.
returned: changed or success
type: str
sample: ssh-rsa AAAAB3Nza(...omitted...)veL4E3Xcw== test_key
sample: ssh-rsa AAAAB3Nza(...omitted...)veL4E3Xcw==
comment:
description: The comment of the generated key.
returned: changed or success

View File

@@ -177,7 +177,7 @@ subject:
returned: changed or success
type: list
elements: list
sample: "[('CN', 'www.ansible.com'), ('O', 'Ansible')]"
sample: [['CN', 'www.ansible.com'], ['O', 'Ansible']]
subjectAltName:
description: The alternative names this CSR is valid for
returned: changed or success
@@ -329,9 +329,10 @@ def main():
if not os.path.isdir(base_dir):
module.fail_json(name=base_dir, msg='The directory %s does not exist or the file is not a directory' % base_dir)
backend = module.params['select_crypto_backend']
backend, module_backend = select_backend(module, backend)
try:
backend = module.params['select_crypto_backend']
backend, module_backend = select_backend(module, backend)
csr = CertificateSigningRequestModule(module, module_backend)
if module.params['state'] == 'present':
csr.generate(module)

View File

@@ -85,7 +85,7 @@ basic_constraints:
returned: success
type: list
elements: str
sample: "[CA:TRUE, pathlen:1]"
sample: ['CA:TRUE', 'pathlen:1']
basic_constraints_critical:
description: Whether the C(basic_constraints) extension is critical.
returned: success
@@ -95,7 +95,7 @@ extended_key_usage:
returned: success
type: list
elements: str
sample: "[Biometric Info, DVCS, Time Stamping]"
sample: [Biometric Info, DVCS, Time Stamping]
extended_key_usage_critical:
description: Whether the C(extended_key_usage) extension is critical.
returned: success
@@ -114,12 +114,12 @@ extensions_by_oid:
returned: success
type: str
sample: "MAMCAQU="
sample: '{"1.3.6.1.5.5.7.1.24": { "critical": false, "value": "MAMCAQU="}}'
sample: {"1.3.6.1.5.5.7.1.24": { "critical": false, "value": "MAMCAQU="}}
key_usage:
description: Entries in the C(key_usage) extension, or C(none) if extension is not present.
returned: success
type: str
sample: "[Key Agreement, Data Encipherment]"
sample: [Key Agreement, Data Encipherment]
key_usage_critical:
description: Whether the C(key_usage) extension is critical.
returned: success
@@ -129,7 +129,7 @@ subject_alt_name:
returned: success
type: list
elements: str
sample: "[DNS:www.ansible.com, IP:1.2.3.4]"
sample: ["DNS:www.ansible.com", "IP:1.2.3.4"]
subject_alt_name_critical:
description: Whether the C(subject_alt_name) extension is critical.
returned: success
@@ -171,13 +171,13 @@ subject:
- Note that for repeated values, only the last one will be returned.
returned: success
type: dict
sample: '{"commonName": "www.example.com", "emailAddress": "test@example.com"}'
sample: {"commonName": "www.example.com", "emailAddress": "test@example.com"}
subject_ordered:
description: The CSR's subject as an ordered list of tuples.
returned: success
type: list
elements: list
sample: '[["commonName", "www.example.com"], ["emailAddress": "test@example.com"]]'
sample: [["commonName", "www.example.com"], ["emailAddress": "test@example.com"]]
public_key:
description: CSR's public key in PEM format
returned: success
@@ -285,14 +285,14 @@ authority_cert_issuer:
returned: success and if the pyOpenSSL backend is I(not) used
type: list
elements: str
sample: "[DNS:www.ansible.com, IP:1.2.3.4]"
sample: ["DNS:www.ansible.com", "IP:1.2.3.4"]
authority_cert_serial_number:
description:
- The CSR's authority cert serial number.
- Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
returned: success and if the pyOpenSSL backend is I(not) used
type: int
sample: '12345'
sample: 12345
'''

View File

@@ -66,7 +66,7 @@ subject:
returned: changed or success
type: list
elements: list
sample: "[('CN', 'www.ansible.com'), ('O', 'Ansible')]"
sample: [['CN', 'www.ansible.com'], ['O', 'Ansible']]
subjectAltName:
description: The alternative names this CSR is valid for
returned: changed or success
@@ -160,9 +160,10 @@ def main():
supports_check_mode=True,
)
backend = module.params['select_crypto_backend']
backend, module_backend = select_backend(module, backend)
try:
backend = module.params['select_crypto_backend']
backend, module_backend = select_backend(module, backend)
csr = CertificateSigningRequestModule(module, module_backend)
csr.generate(module)
result = csr.dump()

View File

@@ -128,11 +128,11 @@ import re
import tempfile
import traceback
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_native
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.io import (
load_file_if_exists,
write_file,

View File

@@ -23,7 +23,7 @@ description:
# Please note that the C(pyopenssl) backend has been deprecated in community.crypto x.y.0,
# and will be removed in community.crypto (x+1).0.0.
requirements:
- PyOpenSSL >= 0.15 or cryptography >= 3.0
- PyOpenSSL >= 0.15, < 23.3.0 or cryptography >= 3.0
options:
action:
description:
@@ -239,11 +239,11 @@ import os
import stat
import traceback
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_bytes, to_native
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.io import (
load_file_if_exists,
write_file,
@@ -270,13 +270,15 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.pem import
MINIMAL_CRYPTOGRAPHY_VERSION = '3.0'
MINIMAL_PYOPENSSL_VERSION = '0.15'
MAXIMAL_PYOPENSSL_VERSION = '23.3.0'
PYOPENSSL_IMP_ERR = None
try:
import OpenSSL
from OpenSSL import crypto
from OpenSSL.crypto import load_pkcs12 as _load_pkcs12 # this got removed in pyOpenSSL 23.3.0
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError:
except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:
@@ -542,6 +544,8 @@ class PkcsPyOpenSSL(Pkcs):
return crypto.dump_certificate(crypto.FILETYPE_PEM, cert) if cert else None
def _dump_other_certificates(self, pkcs12):
if pkcs12.get_ca_certificates() is None:
return []
return [
crypto.dump_certificate(crypto.FILETYPE_PEM, other_cert)
for other_cert in pkcs12.get_ca_certificates()
@@ -635,7 +639,11 @@ def select_backend(module, backend):
if backend == 'auto':
# Detection what is possible
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
can_use_pyopenssl = (
PYOPENSSL_FOUND and
PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION) and
PYOPENSSL_VERSION < LooseVersion(MAXIMAL_PYOPENSSL_VERSION)
)
# If no restrictions are provided, first try cryptography, then pyOpenSSL
if module.params['iter_size'] is not None or module.params['maciter_size'] is not None:
@@ -649,14 +657,17 @@ def select_backend(module, backend):
# Success?
if backend == 'auto':
module.fail_json(msg=("Can't detect any of the required Python libraries "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format(
"cryptography (>= {0}) or PyOpenSSL (>= {1}, < {2})").format(
MINIMAL_CRYPTOGRAPHY_VERSION,
MINIMAL_PYOPENSSL_VERSION))
MINIMAL_PYOPENSSL_VERSION,
MAXIMAL_PYOPENSSL_VERSION))
if backend == 'pyopenssl':
if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
exception=PYOPENSSL_IMP_ERR)
msg = missing_required_lib(
'pyOpenSSL >= {0}, < {1}'.format(MINIMAL_PYOPENSSL_VERSION, MAXIMAL_PYOPENSSL_VERSION)
)
module.fail_json(msg=msg, exception=PYOPENSSL_IMP_ERR)
# module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated',
# version='x.0.0', collection_name='community.crypto')
return backend, PkcsPyOpenSSL(module)

View File

@@ -51,7 +51,6 @@ seealso:
EXAMPLES = r'''
- name: Generate an OpenSSL private key with the default values (4096 bits, RSA)
community.crypto.openssl_privatekey_pipe:
path: /etc/ssl/private/ansible.com.pem
register: output
no_log: true # make sure that private key data is not accidentally revealed in logs!
- name: Show generated key

View File

@@ -13,8 +13,9 @@ DOCUMENTATION = r'''
module: openssl_publickey
short_description: Generate an OpenSSL public key from its private key.
description:
- This module allows one to (re)generate OpenSSL public keys from their private keys.
- Keys are generated in PEM or OpenSSH format.
- This module allows one to (re)generate public keys from their private keys.
- Public keys are generated in PEM or OpenSSH format. Private keys must be OpenSSL PEM keys.
OpenSSH private keys are not supported, use the M(community.crypto.openssh_keypair) module to manage these.
- "The module can use the cryptography Python library, or the pyOpenSSL Python
library. By default, it tries to detect which one is available. This can be
overridden with the I(select_crypto_backend) option. When I(format) is C(OpenSSH),
@@ -182,11 +183,11 @@ publickey:
import os
import traceback
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_native
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.io import (
load_file_if_exists,
write_file,
@@ -217,7 +218,7 @@ try:
import OpenSSL
from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError:
except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:

View File

@@ -61,7 +61,7 @@ EXAMPLES = r'''
path: /etc/ssl/private/ansible.com.pem
- name: Create public key from private key
community.crypto.openssl_privatekey:
community.crypto.openssl_publickey:
privatekey_path: /etc/ssl/private/ansible.com.pem
path: /etc/ssl/ansible.com.pub

View File

@@ -96,9 +96,10 @@ signature:
import os
import traceback
from distutils.version import LooseVersion
import base64
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
MINIMAL_PYOPENSSL_VERSION = '0.11'
MINIMAL_CRYPTOGRAPHY_VERSION = '1.4'
@@ -107,7 +108,7 @@ try:
import OpenSSL
from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError:
except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:

View File

@@ -96,9 +96,10 @@ valid:
import os
import traceback
from distutils.version import LooseVersion
import base64
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
MINIMAL_PYOPENSSL_VERSION = '0.11'
MINIMAL_CRYPTOGRAPHY_VERSION = '1.4'
@@ -107,7 +108,7 @@ try:
import OpenSSL
from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError:
except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:

View File

@@ -129,7 +129,7 @@ basic_constraints:
returned: success
type: list
elements: str
sample: "[CA:TRUE, pathlen:1]"
sample: ["CA:TRUE", "pathlen:1"]
basic_constraints_critical:
description: Whether the C(basic_constraints) extension is critical.
returned: success
@@ -139,7 +139,7 @@ extended_key_usage:
returned: success
type: list
elements: str
sample: "[Biometric Info, DVCS, Time Stamping]"
sample: [Biometric Info, DVCS, Time Stamping]
extended_key_usage_critical:
description: Whether the C(extended_key_usage) extension is critical.
returned: success
@@ -158,12 +158,12 @@ extensions_by_oid:
returned: success
type: str
sample: "MAMCAQU="
sample: '{"1.3.6.1.5.5.7.1.24": { "critical": false, "value": "MAMCAQU="}}'
sample: {"1.3.6.1.5.5.7.1.24": { "critical": false, "value": "MAMCAQU="}}
key_usage:
description: Entries in the C(key_usage) extension, or C(none) if extension is not present.
returned: success
type: str
sample: "[Key Agreement, Data Encipherment]"
sample: [Key Agreement, Data Encipherment]
key_usage_critical:
description: Whether the C(key_usage) extension is critical.
returned: success
@@ -173,7 +173,7 @@ subject_alt_name:
returned: success
type: list
elements: str
sample: "[DNS:www.ansible.com, IP:1.2.3.4]"
sample: ["DNS:www.ansible.com", "IP:1.2.3.4"]
subject_alt_name_critical:
description: Whether the C(subject_alt_name) extension is critical.
returned: success
@@ -192,36 +192,36 @@ issuer:
- Note that for repeated values, only the last one will be returned.
returned: success
type: dict
sample: '{"organizationName": "Ansible", "commonName": "ca.example.com"}'
sample: {"organizationName": "Ansible", "commonName": "ca.example.com"}
issuer_ordered:
description: The certificate's issuer as an ordered list of tuples.
returned: success
type: list
elements: list
sample: '[["organizationName", "Ansible"], ["commonName": "ca.example.com"]]'
sample: [["organizationName", "Ansible"], ["commonName": "ca.example.com"]]
subject:
description:
- The certificate's subject as a dictionary.
- Note that for repeated values, only the last one will be returned.
returned: success
type: dict
sample: '{"commonName": "www.example.com", "emailAddress": "test@example.com"}'
sample: {"commonName": "www.example.com", "emailAddress": "test@example.com"}
subject_ordered:
description: The certificate's subject as an ordered list of tuples.
returned: success
type: list
elements: list
sample: '[["commonName", "www.example.com"], ["emailAddress": "test@example.com"]]'
sample: [["commonName", "www.example.com"], ["emailAddress": "test@example.com"]]
not_after:
description: C(notAfter) date as ASN.1 TIME.
returned: success
type: str
sample: 20190413202428Z
sample: '20190413202428Z'
not_before:
description: C(notBefore) date as ASN.1 TIME.
returned: success
type: str
sample: 20190331202428Z
sample: '20190331202428Z'
public_key:
description: Certificate's public key in PEM format.
returned: success
@@ -359,14 +359,14 @@ authority_cert_issuer:
returned: success and if the pyOpenSSL backend is I(not) used
type: list
elements: str
sample: "[DNS:www.ansible.com, IP:1.2.3.4]"
sample: ["DNS:www.ansible.com", "IP:1.2.3.4"]
authority_cert_serial_number:
description:
- The certificate's authority cert serial number.
- Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
returned: success and if the pyOpenSSL backend is I(not) used
type: int
sample: '12345'
sample: 12345
ocsp_uri:
description: The OCSP responder URI, if included in the certificate. Will be
C(none) if no OCSP responder URI is included.

View File

@@ -286,13 +286,13 @@ issuer:
- Note that for repeated values, only the last one will be returned.
returned: success
type: dict
sample: '{"organizationName": "Ansible", "commonName": "ca.example.com"}'
sample: {"organizationName": "Ansible", "commonName": "ca.example.com"}
issuer_ordered:
description: The CRL's issuer as an ordered list of tuples.
returned: success
type: list
elements: list
sample: '[["organizationName", "Ansible"], ["commonName": "ca.example.com"]]'
sample: [["organizationName", "Ansible"], ["commonName": "ca.example.com"]]
last_update:
description: The point in time from which this CRL can be trusted as ASN.1 TIME.
returned: success
@@ -326,7 +326,7 @@ revoked_certificates:
description: The certificate's issuer.
type: list
elements: str
sample: '["DNS:ca.example.org"]'
sample: ["DNS:ca.example.org"]
issuer_critical:
description: Whether the certificate issuer extension is critical.
type: bool
@@ -367,11 +367,11 @@ import base64
import os
import traceback
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_native, to_text
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
from ansible_collections.community.crypto.plugins.module_utils.io import (
write_file,
)
@@ -392,6 +392,7 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.support im
from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptography_support import (
cryptography_get_name,
cryptography_key_needs_digest_for_signing,
cryptography_name_to_oid,
cryptography_oid_to_name,
cryptography_serial_number_of_cert,
@@ -612,8 +613,12 @@ class CRL(OpenSSLObject):
return False
if self.next_update != self.crl.next_update and not self.ignore_timestamps:
return False
if self.digest.name != self.crl.signature_hash_algorithm.name:
return False
if cryptography_key_needs_digest_for_signing(self.privatekey):
if self.crl.signature_hash_algorithm is None or self.digest.name != self.crl.signature_hash_algorithm.name:
return False
else:
if self.crl.signature_hash_algorithm is not None:
return False
want_issuer = [(cryptography_name_to_oid(entry[0]), entry[1]) for entry in self.issuer]
if want_issuer != [(sub.oid, sub.value) for sub in self.crl.issuer]:
@@ -664,9 +669,7 @@ class CRL(OpenSSLObject):
revoked_cert = revoked_cert.revocation_date(entry['revocation_date'])
if entry['issuer'] is not None:
revoked_cert = revoked_cert.add_extension(
x509.CertificateIssuer([
cryptography_get_name(name, 'issuer') for name in entry['issuer']
]),
x509.CertificateIssuer(entry['issuer']),
entry['issuer_critical']
)
if entry['reason'] is not None:
@@ -681,7 +684,10 @@ class CRL(OpenSSLObject):
)
crl = crl.add_revoked_certificate(revoked_cert.build(backend))
self.crl = crl.sign(self.privatekey, self.digest, backend=backend)
digest = None
if cryptography_key_needs_digest_for_signing(self.privatekey):
digest = self.digest
self.crl = crl.sign(self.privatekey, digest, backend=backend)
if self.format == 'pem':
return self.crl.public_bytes(Encoding.PEM)
else:

View File

@@ -78,23 +78,23 @@ issuer:
- Note that for repeated values, only the last one will be returned.
returned: success
type: dict
sample: '{"organizationName": "Ansible", "commonName": "ca.example.com"}'
sample: {"organizationName": "Ansible", "commonName": "ca.example.com"}
issuer_ordered:
description: The CRL's issuer as an ordered list of tuples.
returned: success
type: list
elements: list
sample: '[["organizationName", "Ansible"], ["commonName": "ca.example.com"]]'
sample: [["organizationName", "Ansible"], ["commonName": "ca.example.com"]]
last_update:
description: The point in time from which this CRL can be trusted as ASN.1 TIME.
returned: success
type: str
sample: 20190413202428Z
sample: '20190413202428Z'
next_update:
description: The point in time from which a new CRL will be issued and the client has to check for it as ASN.1 TIME.
returned: success
type: str
sample: 20190413202428Z
sample: '20190413202428Z'
digest:
description: The signature algorithm used to sign the CRL.
returned: success
@@ -113,12 +113,12 @@ revoked_certificates:
revocation_date:
description: The point in time the certificate was revoked as ASN.1 TIME.
type: str
sample: 20190413202428Z
sample: '20190413202428Z'
issuer:
description: The certificate's issuer.
type: list
elements: str
sample: '["DNS:ca.example.org"]'
sample: ["DNS:ca.example.org"]
issuer_critical:
description: Whether the certificate issuer extension is critical.
type: bool
@@ -140,7 +140,7 @@ revoked_certificates:
The point in time it was known/suspected that the private key was compromised
or that the certificate otherwise became invalid as ASN.1 TIME.
type: str
sample: 20190413202428Z
sample: '20190413202428Z'
invalidity_date_critical:
description: Whether the invalidity date extension is critical.
type: bool

View File

@@ -115,13 +115,12 @@ class AnsibleActionModule(object):
self.required_by = required_by
self._diff = self.__action_plugin._play_context.diff
self._verbosity = self.__action_plugin._display.verbosity
self._string_conversion_action = C.STRING_CONVERSION_ACTION
self.aliases = {}
self._legal_inputs = []
self._options_context = list()
self.params = copy.deepcopy(action_plugin._task.args)
self.params = copy.deepcopy(self.__action_plugin._task.args)
self.no_log_values = set()
if HAS_ARGSPEC_VALIDATOR:
self._validator = ArgumentSpecValidator(
@@ -145,9 +144,14 @@ class AnsibleActionModule(object):
# warnings and deprecations that do not work in plugins. This is a copy of that code adjusted
# for our use-case:
for d in self._validation_result._deprecations:
self.deprecate(
"Alias '{name}' is deprecated. See the module docs for more information".format(name=d['name']),
version=d.get('version'), date=d.get('date'), collection_name=d.get('collection_name'))
# Before ansible-core 2.14.2, deprecations were always for aliases:
if 'name' in d:
self.deprecate(
"Alias '{name}' is deprecated. See the module docs for more information".format(name=d['name']),
version=d.get('version'), date=d.get('date'), collection_name=d.get('collection_name'))
# Since ansible-core 2.14.2, a message is present that can be directly printed:
if 'msg' in d:
self.deprecate(d['msg'], version=d.get('version'), date=d.get('date'), collection_name=d.get('collection_name'))
for w in self._validation_result._warnings:
self.warn('Both option {option} and its alias {alias} are set.'.format(option=w['option'], alias=w['alias']))
@@ -444,7 +448,7 @@ class AnsibleActionModule(object):
}
# Ignore, warn, or error when converting to a string.
allow_conversion = opts.get(self._string_conversion_action, True)
allow_conversion = opts.get(C.STRING_CONVERSION_ACTION, True)
try:
return check_type_str(value, allow_conversion)
except TypeError:
@@ -459,10 +463,10 @@ class AnsibleActionModule(object):
from_msg = '{0}: {1!r}'.format(param, value)
to_msg = '{0}: {1!r}'.format(param, to_text(value))
if self._string_conversion_action == 'error':
if C.STRING_CONVERSION_ACTION == 'error':
msg = common_msg.capitalize()
raise TypeError(to_native(msg))
elif self._string_conversion_action == 'warn':
elif C.STRING_CONVERSION_ACTION == 'warn':
msg = ('The value "{0}" (type {1.__class__.__name__}) was converted to "{2}" (type string). '
'If this does not look like what you expect, {3}').format(from_msg, value, to_msg, common_msg)
self.warn(to_native(msg))

8
simplified_bsd.txt Normal file
View File

@@ -0,0 +1,8 @@
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,4 @@
---
collections:
- community.general
- community.internal_test_tools

View File

@@ -158,7 +158,7 @@
method: get
register: validation_result
loop: "{{ http01challenge.results | map(attribute='output_json') | list }}"
until: "validation_result.output_json.status != 'pending'"
until: "validation_result.output_json.status not in ['pending', 'processing']"
retries: 20
delay: 1
- debug: var=validation_result

View File

@@ -107,7 +107,7 @@
- "'headers' in item"
- "'output_text' in item"
- "'output_json' in item"
- item.output_json.status == 'pending'
- item.output_json.status in ['pending', 'processing']
- item.output_json.type == 'http-01'
- item.output_json.url == item.invocation.module_args.url
- "'token' in item.output_json"

View File

@@ -1,3 +1,4 @@
dependencies:
- prepare_jinja2_compat
- setup_openssl
- setup_remote_tmp_dir

View File

@@ -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 }}'

View File

@@ -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

View File

@@ -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'

View File

@@ -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'"

View File

@@ -4,6 +4,7 @@
####################################################################
- block:
- name: Make sure testhost directory exists
file:
path: '{{ remote_tmp_dir }}/files/'
@@ -13,68 +14,14 @@
copy:
src: '{{ role_path }}/files/'
dest: '{{ remote_tmp_dir }}/files/'
- name: Find root for cert 1
certificate_complete_chain:
input_chain: '{{ lookup("file", "cert1-fullchain.pem", rstrip=False) }}'
root_certificates:
- '{{ remote_tmp_dir }}/files/roots/'
register: cert1_root
- name: Verify root for cert 1
assert:
that:
- cert1_root.complete_chain | join('') == (lookup('file', 'cert1.pem', rstrip=False) ~ lookup('file', 'cert1-chain.pem', rstrip=False) ~ lookup('file', 'cert1-root.pem', rstrip=False))
- cert1_root.root == lookup('file', 'cert1-root.pem', rstrip=False)
- name: Find rootchain for cert 1
certificate_complete_chain:
input_chain: '{{ lookup("file", "cert1.pem", rstrip=False) }}'
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('') == (lookup('file', 'cert1.pem', rstrip=False) ~ lookup('file', 'cert1-chain.pem', rstrip=False) ~ lookup('file', 'cert1-root.pem', rstrip=False))
- cert1_rootchain.chain[:-1] | join('') == lookup('file', 'cert1-chain.pem', rstrip=False)
- cert1_rootchain.root == lookup('file', 'cert1-root.pem', rstrip=False)
- name: Find root for cert 2
certificate_complete_chain:
input_chain: '{{ lookup("file", "cert2-fullchain.pem", rstrip=False) }}'
root_certificates:
- '{{ remote_tmp_dir }}/files/roots/'
register: cert2_root
- name: Verify root for cert 2
assert:
that:
- cert2_root.complete_chain | join('') == (lookup('file', 'cert2.pem', rstrip=False) ~ lookup('file', 'cert2-chain.pem', rstrip=False) ~ lookup('file', 'cert2-root.pem', rstrip=False))
- cert2_root.root == lookup('file', 'cert2-root.pem', rstrip=False)
- name: Find rootchain for cert 2
certificate_complete_chain:
input_chain: '{{ lookup("file", "cert2.pem", rstrip=False) }}'
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('') == (lookup('file', 'cert2.pem', rstrip=False) ~ lookup('file', 'cert2-chain.pem', rstrip=False) ~ lookup('file', 'cert2-root.pem', rstrip=False))
- cert2_rootchain.chain[:-1] | join('') == lookup('file', 'cert2-chain.pem', rstrip=False)
- cert2_rootchain.root == lookup('file', 'cert2-root.pem', rstrip=False)
- name: Find alternate rootchain for cert 2
certificate_complete_chain:
input_chain: '{{ lookup("file", "cert2.pem", rstrip=True) }}'
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('') == (lookup('file', 'cert2.pem', rstrip=False) ~ lookup('file', 'cert2-altchain.pem', rstrip=False) ~ lookup('file', 'cert2-altroot.pem', rstrip=False))
- cert2_rootchain_alt.chain[:-1] | join('') == lookup('file', 'cert2-altchain.pem', rstrip=False)
- cert2_rootchain_alt.root == lookup('file', 'cert2-altroot.pem', rstrip=False)
- name: Run tests with copied certificates
import_tasks: existing.yml
- name: Create more certificates
import_tasks: create.yml
- name: Run tests with created certificates
import_tasks: created.yml
when: cryptography_version.stdout is version('1.5', '>=')

View File

@@ -6,6 +6,7 @@
keyfile: "{{ remote_tmp_dir }}/keyfile1"
keysize: 256
pbkdf:
algorithm: pbkdf2
iteration_count: 1000
become: yes
register: create_with_keysize
@@ -16,6 +17,7 @@
keyfile: "{{ remote_tmp_dir }}/keyfile1"
keysize: 256
pbkdf:
algorithm: pbkdf2
iteration_count: 1000
become: yes
register: create_idem_with_keysize
@@ -26,6 +28,7 @@
keyfile: "{{ remote_tmp_dir }}/keyfile1"
keysize: 512
pbkdf:
algorithm: pbkdf2
iteration_count: 1000
become: yes
register: create_idem_with_diff_keysize
@@ -36,6 +39,7 @@
keyfile: "{{ remote_tmp_dir }}/keyfile1"
passphrase: "{{ cryptfile_passphrase1 }}"
pbkdf:
algorithm: pbkdf2
iteration_count: 1000
ignore_errors: yes
become: yes

View File

@@ -86,6 +86,27 @@
regenerate: full_idempotence
register: default_options
- name: Generate host cert full_idempotence
openssh_cert:
type: host
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ signing_key }}"
valid_from: always
valid_to: forever
regenerate: full_idempotence
- name: Generate host cert full_idempotence again
openssh_cert:
type: host
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ signing_key }}"
valid_from: always
valid_to: forever
regenerate: full_idempotence
register: host_cert_full_idempotence
- name: Assert options results
assert:
that:
@@ -95,6 +116,7 @@
- explicit_extension_after is not changed
- explicit_extension_and_directive is changed
- default_options is not changed
- host_cert_full_idempotence is not changed
- name: Remove certificate
openssh_cert:

View File

@@ -24,6 +24,7 @@
type: rsa
size: 1024
backend: "{{ backend }}"
regenerate: "{{ item }}"
loop: "{{ regenerate_values }}"
- name: "({{ backend }}) Regenerate - setup password protected keys"
command: 'ssh-keygen -f {{ remote_tmp_dir }}/regenerate-b-{{ item }} -N {{ passphrase }}'

View File

@@ -924,7 +924,6 @@
- CN=ca.example.com
reasons:
- certificate_hold
- {}
select_crypto_backend: '{{ select_crypto_backend }}'
register: crl_distribution_endpoints_1
@@ -947,7 +946,6 @@
- CN=ca.example.com
reasons:
- certificate_hold
- {}
select_crypto_backend: '{{ select_crypto_backend }}'
register: crl_distribution_endpoints_2
@@ -958,9 +956,7 @@
subject:
commonName: www.ansible.com
crl_distribution_points:
- full_name:
- "URI:https://ca.example.com/revocations.crl"
crl_issuer:
- crl_issuer:
- "URI:https://ca.example.com/"
reasons:
- key_compromise

View File

@@ -8,7 +8,7 @@
select_crypto_backend: '{{ select_crypto_backend }}'
register: result
- name: "({{ select_crypto_backend }}) Check whether subject behaves as expected"
- name: "({{ select_crypto_backend }}) Check whether subject and extensions behaves as expected"
assert:
that:
- result.subject.organizationalUnitName == 'ACME Department'
@@ -16,6 +16,21 @@
- "['organizationalUnitName', 'ACME Department'] in result.subject_ordered"
- result.public_key_type == 'RSA'
- result.public_key_data.size == default_rsa_key_size
# TLS Feature
- result.extensions_by_oid['1.3.6.1.5.5.7.1.24'].critical == false
- result.extensions_by_oid['1.3.6.1.5.5.7.1.24'].value == 'MAMCAQU='
# Key Usage
- result.extensions_by_oid['2.5.29.15'].critical == true
- result.extensions_by_oid['2.5.29.15'].value in ['AwMA/4A=', 'AwMH/4A=']
# Subject Alternative Names
- result.extensions_by_oid['2.5.29.17'].critical == false
- result.extensions_by_oid['2.5.29.17'].value == 'MGCCD3d3dy5hbnNpYmxlLmNvbYcEAQIDBIcQAAAAAAAAAAAAAAAAAAAAAYEQdGVzdEBleGFtcGxlLm9yZ4YjaHR0cHM6Ly9leGFtcGxlLm9yZy90ZXN0L2luZGV4Lmh0bWw='
# Basic Constraints
- result.extensions_by_oid['2.5.29.19'].critical == true
- result.extensions_by_oid['2.5.29.19'].value == 'MAYBAf8CARc='
# Extended Key Usage
- result.extensions_by_oid['2.5.29.37'].critical == false
- result.extensions_by_oid['2.5.29.37'].value == 'MHQGCCsGAQUFBwMBBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwgGCCsGAQUFBwMJBgRVHSUABggrBgEFBQcBAwYIKwYBBQUHAwoGCCsGAQUFBwMHBggrBgEFBQcBAg=='
- name: "({{ select_crypto_backend }}) Check SubjectKeyIdentifier and AuthorityKeyIdentifier"
assert:
@@ -24,6 +39,10 @@
- result.authority_key_identifier == "44:55:66:77"
- result.authority_cert_issuer == expected_authority_cert_issuer
- result.authority_cert_serial_number == 12345
# Subject Key Identifier
- result.extensions_by_oid['2.5.29.14'].critical == false
# Authority Key Identifier
- result.extensions_by_oid['2.5.29.35'].critical == false
vars:
expected_authority_cert_issuer:
- "DNS:ca.example.org"

View File

@@ -45,6 +45,18 @@
return_content: true
register: p12_standard_idempotency
- name: "({{ select_crypto_backend }}) Generate PKCS#12 file again, idempotency (empty other_certificates)"
openssl_pkcs12:
select_crypto_backend: '{{ select_crypto_backend }}'
path: '{{ remote_tmp_dir }}/ansible.p12'
friendly_name: abracadabra
privatekey_path: '{{ remote_tmp_dir }}/ansible_pkey1.pem'
certificate_path: '{{ remote_tmp_dir }}/ansible1.crt'
state: present
return_content: true
other_certificates: []
register: p12_standard_idempotency_no_certs
- name: "({{ select_crypto_backend }}) Read ansible.p12"
slurp:
src: '{{ remote_tmp_dir }}/ansible.p12'

View File

@@ -65,7 +65,10 @@
vars:
select_crypto_backend: pyopenssl
when: pyopenssl_version.stdout is version('0.15', '>=')
when: >-
pyopenssl_version.stdout is version('0.15', '>=')
and
pyopenssl_version.stdout is version('23.3.0', '<')
- block:
- name: Running tests with cryptography backend
@@ -75,4 +78,11 @@
when: cryptography_version.stdout is version('3.0', '>=')
when: pyopenssl_version.stdout is version('0.15', '>=') or cryptography_version.stdout is version('3.0', '>=')
when: >-
(
pyopenssl_version.stdout is version('0.15', '>=')
and
pyopenssl_version.stdout is version('23.3.0', '<')
)
or
cryptography_version.stdout is version('3.0', '>=')

View File

@@ -25,6 +25,7 @@
- p12_dumped is changed
- p12_standard_idempotency is not changed
- p12_standard_idempotency_check is not changed
- p12_standard_idempotency_no_certs is not changed
- p12_multiple_certs_idempotency is not changed
- p12_dumped_idempotency is not changed
- p12_dumped_check_mode is not changed
@@ -83,4 +84,4 @@
- p12_empty is changed
- p12_empty_idem is not changed
- p12_empty_concat_idem is not changed
- empty_contents == (empty_expected_pyopenssl if select_crypto_backend == 'pyopenssl' else empty_expected_cryptography)
- (empty_contents == empty_expected_cryptography) or (empty_contents == empty_expected_pyopenssl and select_crypto_backend == 'pyopenssl')

View File

@@ -303,11 +303,18 @@
size: '{{ default_rsa_key_size }}'
select_crypto_backend: '{{ select_crypto_backend }}'
register: privatekey_mode_1
- name: "({{ select_crypto_backend }}) Stat for privatekey_mode"
stat:
path: '{{ remote_tmp_dir }}/privatekey_mode.pem'
register: privatekey_mode_1_stat
- name: "({{ select_crypto_backend }}) Collect file information"
community.internal_test_tools.files_collect:
files:
- path: '{{ remote_tmp_dir }}/privatekey_mode.pem'
register: privatekey_mode_1_fileinfo
- name: "({{ select_crypto_backend }}) Generate privatekey_mode (mode 0400, idempotency)"
openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekey_mode.pem'
@@ -316,13 +323,6 @@
select_crypto_backend: '{{ select_crypto_backend }}'
register: privatekey_mode_2
- name: Make sure that mtime actually changes.
# The "privatekey_mode_1_stat.stat.mtime != privatekey_mode_3_stat.stat.mtime" test should be
# changed to compare content instead of mtime. On macOS 10.15, mtime resolution is one second,
# and the machine (VM) is fast enough that both modifications can happen in the same second.
pause:
seconds: 1
- name: "({{ select_crypto_backend }}) Generate privatekey_mode (mode 0400, force)"
openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekey_mode.pem'
@@ -331,11 +331,17 @@
size: '{{ default_rsa_key_size }}'
select_crypto_backend: '{{ select_crypto_backend }}'
register: privatekey_mode_3
- name: "({{ select_crypto_backend }}) Stat for privatekey_mode"
stat:
path: '{{ remote_tmp_dir }}/privatekey_mode.pem'
register: privatekey_mode_3_stat
- name: "({{ select_crypto_backend }}) Make sure that file changed"
community.internal_test_tools.files_diff:
state: '{{ privatekey_mode_1_fileinfo }}'
register: privatekey_mode_3_file_change
- block:
- name: "({{ select_crypto_backend }}) Generate privatekey_fmt_1 - auto format"
openssl_privatekey:

View File

@@ -186,7 +186,7 @@
- privatekey_mode_2 is not changed
- privatekey_mode_3 is changed
- privatekey_mode_3_stat.stat.mode == '0400'
- privatekey_mode_1_stat.stat.mtime != privatekey_mode_3_stat.stat.mtime
- privatekey_mode_3_file_change is changed
- name: "({{ select_crypto_backend }}) Validate format 1"
assert:

View File

@@ -32,13 +32,19 @@ system_python_version_data:
- '3.8'
'11.1':
- '3.9'
'12.0':
- '3.10'
FreeBSD:
'12.1':
- '3.6'
'12.2':
- '3.7'
'12.3':
- '3.8'
'13.0':
- '3.7'
'13.1':
- '3.8'
RedHat:
'7':
- '2.7'
@@ -55,3 +61,6 @@ cannot_upgrade_cryptography:
- '3.8' # on the VMs in CI, system packages are used for this version as well
'13.0':
- '3.8' # on the VMs in CI, system packages are used for this version as well
Ubuntu:
'18':
- '3.9' # this is the default container for ansible-core 2.12; upgrading cryptography wrecks pyOpenSSL

View File

@@ -1,7 +1,18 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: create ~/tmp
file:
path: '~/tmp'
state: directory
- name: create temporary directory
tempfile:
state: directory
suffix: .test
path: '~/tmp'
register: remote_tmp_dir
notify:
- delete temporary directory

View File

@@ -1,2 +1,3 @@
dependencies:
- setup_ssh_keygen
- prepare_jinja2_compat

View File

@@ -5,13 +5,22 @@
####################################################################
- name: Start an ssh agent to use for tests
shell: eval $(ssh-agent)>/dev/null&&echo "${SSH_AGENT_PID};${SSH_AUTH_SOCK}"
register: openssh_agent_env_vars
shell: ssh-agent -c | grep "^setenv"
register: openssh_agent_stdout
- name: Convert output to dictionary
set_fact:
openssh_agent_env: >-
{{
openssh_agent_stdout.stdout_lines | map('regex_replace', '^setenv ([^ ]+) ([^ ]+);', '\1')
| zip(openssh_agent_stdout.stdout_lines | map('regex_replace', '^setenv ([^ ]+) ([^ ]+);', '\2'))
| list | items2dict(key_name=0, value_name=1)
}}
- name: Register ssh agent facts
set_fact:
openssh_agent_pid: "{{ openssh_agent_env_vars.stdout.split(';')[0] }}"
openssh_agent_sock: "{{ openssh_agent_env_vars.stdout.split(';')[1] }}"
openssh_agent_pid: "{{ openssh_agent_env.SSH_AGENT_PID }}"
openssh_agent_sock: "{{ openssh_agent_env.SSH_AUTH_SOCK }}"
- name: stat agent socket
stat:

View File

@@ -67,6 +67,13 @@
get_url:
url: https://raw.githubusercontent.com/diafygi/acme-tiny/master/acme_tiny.py
dest: "{{ remote_tmp_dir }}/acme-tiny"
when: ansible_python_version is version('2.7', '>=')
- name: Get hold of acme-tiny executable (Python 2.6)
command:
cmd: >-
curl https://raw.githubusercontent.com/diafygi/acme-tiny/master/acme_tiny.py --output "{{ remote_tmp_dir }}/acme-tiny"
when: ansible_python_version is version('2.7', '<')
- name: Make sure acme-tiny is executable
file:

View File

@@ -14,14 +14,20 @@
- name: (OwnCA, {{select_crypto_backend}}) Generate CA CSR
openssl_csr:
path: '{{ remote_tmp_dir }}/ca_csr.csr'
path: '{{ item.path }}'
privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
subject:
commonName: Example CA
subject: '{{ item.subject }}'
useCommonNameForSAN: no
basic_constraints:
- 'CA:TRUE'
basic_constraints_critical: yes
loop:
- path: '{{ remote_tmp_dir }}/ca_csr.csr'
subject:
commonName: Example CA
- path: '{{ remote_tmp_dir }}/ca_csr2.csr'
subject:
commonName: Example CA 2
- name: (OwnCA, {{select_crypto_backend}}) Generate CA CSR (privatekey passphrase)
openssl_csr:
@@ -62,6 +68,15 @@
- result_check_mode is changed
- result is changed
- name: (OwnCA, {{select_crypto_backend}}) Generate selfsigned CA certificate with different commonName
x509_certificate:
path: '{{ remote_tmp_dir }}/ca_cert2.pem'
csr_path: '{{ remote_tmp_dir }}/ca_csr2.csr'
privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: selfsigned
selfsigned_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
- name: (OwnCA, {{select_crypto_backend}}) Generate selfsigned CA certificate (privatekey passphrase)
x509_certificate:
path: '{{ remote_tmp_dir }}/ca_cert_pw.pem'
@@ -110,6 +125,54 @@
select_crypto_backend: '{{ select_crypto_backend }}'
check_mode: yes
- name: (OwnCA, {{select_crypto_backend}}) Copy ownca certificate to new file to check regeneration
copy:
src: '{{ remote_tmp_dir }}/ownca_cert.pem'
dest: '{{ item }}'
remote_src: true
loop:
- '{{ remote_tmp_dir }}/ownca_cert_ca_cn.pem'
- '{{ remote_tmp_dir }}/ownca_cert_ca_key.pem'
- name: (OwnCA, {{select_crypto_backend}}) Regenerate ownca certificate with different CA subject
x509_certificate:
path: '{{ remote_tmp_dir }}/ownca_cert_ca_cn.pem'
csr_path: '{{ remote_tmp_dir }}/csr.csr'
privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_path: '{{ remote_tmp_dir }}/ca_cert2.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
return_content: yes
register: ownca_certificate_ca_subject_changed
- name: (OwnCA, {{select_crypto_backend}}) Regenerate ownca certificate with different CA key
x509_certificate:
path: '{{ remote_tmp_dir }}/ownca_cert_ca_key.pem'
csr_path: '{{ remote_tmp_dir }}/csr.csr'
privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_path: '{{ remote_tmp_dir }}/ca_cert_pw.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey_pw.pem'
ownca_privatekey_passphrase: hunter2
provider: ownca
ownca_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
return_content: yes
register: ownca_certificate_ca_key_changed
- name: (OwnCA, {{select_crypto_backend}}) Get certificate information
community.crypto.x509_certificate_info:
path: '{{ remote_tmp_dir }}/ownca_cert.pem'
select_crypto_backend: '{{ select_crypto_backend }}'
register: result
- name: (OwnCA, {{select_crypto_backend}}) Get private key information
community.crypto.openssl_privatekey_info:
path: '{{ remote_tmp_dir }}/privatekey.pem'
select_crypto_backend: '{{ select_crypto_backend }}'
register: result_privatekey
- name: (OwnCA, {{select_crypto_backend}}) Check ownca certificate
x509_certificate:
path: '{{ remote_tmp_dir }}/ownca_cert.pem'
@@ -285,7 +348,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_backup.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
backup: yes
@@ -296,7 +359,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_backup.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
backup: yes
@@ -307,7 +370,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_backup.pem'
csr_path: '{{ remote_tmp_dir }}/csr.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
backup: yes
@@ -335,7 +398,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
ownca_create_subject_key_identifier: always_create
@@ -348,7 +411,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
ownca_create_subject_key_identifier: always_create
@@ -361,7 +424,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
ownca_create_subject_key_identifier: never_create
@@ -374,7 +437,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
ownca_create_subject_key_identifier: never_create
@@ -387,7 +450,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
ownca_create_subject_key_identifier: always_create
@@ -400,7 +463,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
ownca_create_authority_key_identifier: yes
@@ -413,7 +476,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
ownca_create_authority_key_identifier: yes
@@ -426,7 +489,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
ownca_create_authority_key_identifier: no
@@ -439,7 +502,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
ownca_create_authority_key_identifier: no
@@ -452,7 +515,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
ownca_privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
ownca_create_authority_key_identifier: yes

View File

@@ -31,6 +31,14 @@
- ownca_certificate.notBefore == ownca_certificate_idempotence.notBefore
- ownca_certificate.notAfter == ownca_certificate_idempotence.notAfter
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate regeneration
assert:
that:
- ownca_certificate_ca_subject_changed is changed
# ownca_certificate_ca_key_changed is not changed for the pyopenssl backend,
# see https://github.com/ansible-collections/community.crypto/pull/406
- ownca_certificate_ca_key_changed is changed or select_crypto_backend == 'pyopenssl'
- name: (OwnCA validation, {{select_crypto_backend}}) Read certificate
slurp:
src: '{{ remote_tmp_dir }}/ownca_cert.pem'

View File

@@ -8,7 +8,7 @@
select_crypto_backend: '{{ select_crypto_backend }}'
register: result
- name: Check whether issuer and subject behave as expected
- name: Check whether issuer and subject and extensions behave as expected
assert:
that:
- result.issuer.organizationalUnitName == 'ACME Department'
@@ -19,6 +19,28 @@
- "['organizationalUnitName', 'ACME Department'] in result.subject_ordered"
- result.public_key_type == 'RSA'
- result.public_key_data.size == (default_rsa_key_size_certifiates | int)
- "result.subject_alt_name == [
'DNS:www.ansible.com',
'IP:1.2.3.4',
'IP:::1',
'email:test@example.org',
'URI:https://example.org/test/index.html'
]"
# TLS Feature
- result.extensions_by_oid['1.3.6.1.5.5.7.1.24'].critical == false
- result.extensions_by_oid['1.3.6.1.5.5.7.1.24'].value == 'MAMCAQU='
# Key Usage
- result.extensions_by_oid['2.5.29.15'].critical == true
- result.extensions_by_oid['2.5.29.15'].value in ['AwMA/4A=', 'AwMH/4A=']
# Subject Alternative Names
- result.extensions_by_oid['2.5.29.17'].critical == false
- result.extensions_by_oid['2.5.29.17'].value == 'MGCCD3d3dy5hbnNpYmxlLmNvbYcEAQIDBIcQAAAAAAAAAAAAAAAAAAAAAYEQdGVzdEBleGFtcGxlLm9yZ4YjaHR0cHM6Ly9leGFtcGxlLm9yZy90ZXN0L2luZGV4Lmh0bWw='
# Basic Constraints
- result.extensions_by_oid['2.5.29.19'].critical == true
- result.extensions_by_oid['2.5.29.19'].value == 'MAYBAf8CARc='
# Extended Key Usage
- result.extensions_by_oid['2.5.29.37'].critical == false
- result.extensions_by_oid['2.5.29.37'].value == 'MHQGCCsGAQUFBwMBBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwgGCCsGAQUFBwMJBgRVHSUABggrBgEFBQcBAwYIKwYBBQUHAwoGCCsGAQUFBwMHBggrBgEFBQcBAg=='
- name: Check SubjectKeyIdentifier and AuthorityKeyIdentifier
assert:
@@ -27,6 +49,10 @@
- result.authority_key_identifier == "44:55:66:77"
- result.authority_cert_issuer == expected_authority_cert_issuer
- result.authority_cert_serial_number == 12345
# Subject Key Identifier
- result.extensions_by_oid['2.5.29.14'].critical == false
# Authority Key Identifier
- result.extensions_by_oid['2.5.29.35'].critical == false
vars:
expected_authority_cert_issuer:
- "DNS:ca.example.org"
@@ -122,10 +148,39 @@
path: '{{ remote_tmp_dir }}/packed-cert-1.pem'
select_crypto_backend: '{{ select_crypto_backend }}'
register: result
- assert:
- name: Check extensions
assert:
that:
- "'ocsp_uri' in result"
- "result.ocsp_uri == 'http://ocsp.int-x3.letsencrypt.org'"
- result.extensions_by_oid | length == 9
# Precert Signed Certificate Timestamps
- result.extensions_by_oid['1.3.6.1.4.1.11129.2.4.2'].critical == false
- result.extensions_by_oid['1.3.6.1.4.1.11129.2.4.2'].value == 'BIHyAPAAdgDBFkrgp3LS1DktyArBB3DU8MSb3pkaSEDB+gdRZPYzYAAAAWTdAoU6AAAEAwBHMEUCIG5WpfKF536KKa9fnVlYbwcfrKh09Hi2MSRwU2kad49UAiEA4RUKjJOgw11IHFNdit+sy1RcCU3QCSOEQYrJ1/oPltAAdgApPFGWVMg5ZbqqUPxYB9S3b79Yeily3KTDDPTlRUf0eAAAAWTdAoc+AAAEAwBHMEUCIQCJjo75K4rVDSiWQe3XFLY6MiG3zcHQrKb0YhM17r1UKAIgGa8qMoN03DLp+Rm9nRJ9XLbTJz1vbuu9PyXUY741P8E='
# Authority Information Access
- result.extensions_by_oid['1.3.6.1.5.5.7.1.1'].critical == false
- result.extensions_by_oid['1.3.6.1.5.5.7.1.1'].value == 'MGEwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcwLwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcv'
# Subject Key Identifier
- result.extensions_by_oid['2.5.29.14'].critical == false
- result.extensions_by_oid['2.5.29.14'].value == 'BBRtcOI/yg62Ehbu5vQzxMUUdBOYMw=='
# Key Usage (The certificate has 'AwIFoA==', while de-serializing and re-serializing yields 'AwIAoA=='!)
- result.extensions_by_oid['2.5.29.15'].critical == true
- result.extensions_by_oid['2.5.29.15'].value in ['AwIFoA==', 'AwIAoA==']
# Subject Alternative Names
- result.extensions_by_oid['2.5.29.17'].critical == false
- result.extensions_by_oid['2.5.29.17'].value == 'MIIB5IIbY2VydC5pbnQteDEubGV0c2VuY3J5cHQub3JnghtjZXJ0LmludC14Mi5sZXRzZW5jcnlwdC5vcmeCG2NlcnQuaW50LXgzLmxldHNlbmNyeXB0Lm9yZ4IbY2VydC5pbnQteDQubGV0c2VuY3J5cHQub3JnghxjZXJ0LnJvb3QteDEubGV0c2VuY3J5cHQub3Jngh9jZXJ0LnN0YWdpbmcteDEubGV0c2VuY3J5cHQub3Jngh9jZXJ0LnN0Zy1pbnQteDEubGV0c2VuY3J5cHQub3JngiBjZXJ0LnN0Zy1yb290LXgxLmxldHNlbmNyeXB0Lm9yZ4ISY3AubGV0c2VuY3J5cHQub3JnghpjcC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZ4ITY3BzLmxldHNlbmNyeXB0Lm9yZ4IbY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3Jnghtjcmwucm9vdC14MS5sZXRzZW5jcnlwdC5vcmeCD2xldHNlbmNyeXB0Lm9yZ4IWb3JpZ2luLmxldHNlbmNyeXB0Lm9yZ4IXb3JpZ2luMi5sZXRzZW5jcnlwdC5vcmeCFnN0YXR1cy5sZXRzZW5jcnlwdC5vcmeCE3d3dy5sZXRzZW5jcnlwdC5vcmc='
# Basic Constraints
- result.extensions_by_oid['2.5.29.19'].critical == true
- result.extensions_by_oid['2.5.29.19'].value == 'MAA='
# Certificate Policies
- result.extensions_by_oid['2.5.29.32'].critical == false
- result.extensions_by_oid['2.5.29.32'].value == 'MIHzMAgGBmeBDAECATCB5gYLKwYBBAGC3xMBAQEwgdYwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5cHQub3JnMIGrBggrBgEFBQcCAjCBngyBm1RoaXMgQ2VydGlmaWNhdGUgbWF5IG9ubHkgYmUgcmVsaWVkIHVwb24gYnkgUmVseWluZyBQYXJ0aWVzIGFuZCBvbmx5IGluIGFjY29yZGFuY2Ugd2l0aCB0aGUgQ2VydGlmaWNhdGUgUG9saWN5IGZvdW5kIGF0IGh0dHBzOi8vbGV0c2VuY3J5cHQub3JnL3JlcG9zaXRvcnkv'
# Authority Key Identifier
- result.extensions_by_oid['2.5.29.35'].critical == false
- result.extensions_by_oid['2.5.29.35'].value == 'MBaAFKhKamMEfd265tE5t6ZFZe/zqOyh'
# Extended Key Usage
- result.extensions_by_oid['2.5.29.37'].critical == false
- result.extensions_by_oid['2.5.29.37'].value == 'MBQGCCsGAQUFBwMBBggrBgEFBQcDAg=='
- name: Check fingerprints
assert:
that:

View File

@@ -456,3 +456,90 @@
path: '{{ remote_tmp_dir }}/ca-crl2.crl'
list_revoked_certificates: false
register: crl_2_info_1
- name: Create CRL 3
x509_crl:
path: '{{ remote_tmp_dir }}/ca-crl3.crl'
privatekey_path: '{{ remote_tmp_dir }}/ca.key'
issuer:
CN: Ansible
last_update: +0d
next_update: +0d
revoked_certificates:
- serial_number: 1234
revocation_date: 20191001000000Z
issuer:
- "DNS:ca.example.org"
issuer_critical: true
register: crl_3
- name: Retrieve CRL 3 infos
x509_crl_info:
path: '{{ remote_tmp_dir }}/ca-crl3.crl'
list_revoked_certificates: true
register: crl_3_info
- name: Ed25519 and Ed448 tests (for cryptography >= 2.6)
block:
- name: Generate private keys
openssl_privatekey:
path: '{{ remote_tmp_dir }}/ca-{{ item }}.key'
type: '{{ item }}'
loop:
- Ed25519
- Ed448
register: ed25519_ed448_privatekey
ignore_errors: yes
- when: ed25519_ed448_privatekey is not failed
block:
- name: Create CRL
x509_crl:
path: '{{ remote_tmp_dir }}/ca-crl-{{ item }}.crl'
privatekey_path: '{{ remote_tmp_dir }}/ca-{{ item }}.key'
issuer:
CN: Ansible
last_update: 20191013000000Z
next_update: 20191113000000Z
revoked_certificates:
- path: '{{ remote_tmp_dir }}/cert-1.pem'
revocation_date: 20191013000000Z
- path: '{{ remote_tmp_dir }}/cert-2.pem'
revocation_date: 20191013000000Z
reason: key_compromise
reason_critical: yes
invalidity_date: 20191012000000Z
- serial_number: 1234
revocation_date: 20191001000000Z
register: ed25519_ed448_crl
loop:
- Ed25519
- Ed448
ignore_errors: yes
- name: Create CRL (idempotence)
x509_crl:
path: '{{ remote_tmp_dir }}/ca-crl-{{ item }}.crl'
privatekey_path: '{{ remote_tmp_dir }}/ca-{{ item }}.key'
issuer:
CN: Ansible
last_update: 20191013000000Z
next_update: 20191113000000Z
revoked_certificates:
- path: '{{ remote_tmp_dir }}/cert-1.pem'
revocation_date: 20191013000000Z
- path: '{{ remote_tmp_dir }}/cert-2.pem'
revocation_date: 20191013000000Z
reason: key_compromise
reason_critical: yes
invalidity_date: 20191012000000Z
- serial_number: 1234
revocation_date: 20191001000000Z
register: ed25519_ed448_crl_idempotence
loop:
- Ed25519
- Ed448
ignore_errors: yes
when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('2.6', '>=')

View File

@@ -90,3 +90,31 @@
assert:
that:
- "'revoked_certificates' not in crl_2_info_1"
- name: Validate CRL 3 info
assert:
that:
- crl_3.revoked_certificates == crl_3_info.revoked_certificates
- crl_3.revoked_certificates[0].issuer == [
"DNS:ca.example.org",
]
- name: Verify Ed25519 and Ed448 tests (for cryptography >= 2.6, < 2.8)
assert:
that:
- ed25519_ed448_crl.results[0] is failed
- ed25519_ed448_crl.results[1] is failed
- ed25519_ed448_crl_idempotence.results[0] is failed
- ed25519_ed448_crl_idempotence.results[1] is failed
when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('2.6', '>=') and cryptography_version.stdout is version('2.8', '<') and ed25519_ed448_privatekey is not failed
- name: Verify Ed25519 and Ed448 tests (for cryptography >= 2.8)
assert:
that:
- ed25519_ed448_crl is succeeded
- ed25519_ed448_crl.results[0] is changed
- ed25519_ed448_crl.results[1] is changed
- ed25519_ed448_crl_idempotence is succeeded
- ed25519_ed448_crl_idempotence.results[0] is not changed
- ed25519_ed448_crl_idempotence.results[1] is not changed
when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('2.8', '>=') and ed25519_ed448_privatekey is not failed

View File

@@ -1,3 +0,0 @@
integration_tests_dependencies:
- community.general
unit_tests_dependencies: []

View File

@@ -1,10 +1,13 @@
{
"include_symlinks": false,
"prefixes": [
"docs/docsite/"
"docs/docsite/",
"plugins/",
"roles/"
],
"output": "path-line-column-message",
"requirements": [
"antsibull"
"ansible-core",
"antsibull-docs"
]
}

Some files were not shown because too many files have changed in this diff Show More