Compare commits

...

77 Commits

Author SHA1 Message Date
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
86 changed files with 1907 additions and 354 deletions

View File

@@ -19,6 +19,11 @@ schedules:
branches: branches:
include: include:
- main - main
- cron: 0 12 * * 0
displayName: Weekly (old stable branches)
always: true
branches:
include:
- stable-* - stable-*
variables: variables:
@@ -42,19 +47,19 @@ pool: Standard
stages: stages:
### Sanity & units ### Sanity & units
- stage: Ansible_devel - stage: Ansible_2_13
displayName: Sanity & Units devel displayName: Sanity & Units 2.13
dependsOn: [] dependsOn: []
jobs: jobs:
- template: templates/matrix.yml - template: templates/matrix.yml
parameters: parameters:
targets: targets:
- name: Sanity - name: Sanity
test: 'devel/sanity/1' test: '2.13/sanity/1'
- name: Sanity Extra # Only on devel - name: Sanity Extra # Only on devel
test: 'devel/sanity/extra' test: '2.13/sanity/extra'
- name: Units - name: Units
test: 'devel/units/1' test: '2.13/units/1'
- stage: Ansible_2_12 - stage: Ansible_2_12
displayName: Sanity & Units 2.12 displayName: Sanity & Units 2.12
dependsOn: [] dependsOn: []
@@ -100,24 +105,20 @@ stages:
- name: Units - name: Units
test: '2.9/units/1' test: '2.9/units/1'
### Docker ### Docker
- stage: Docker_devel - stage: Docker_2_13
displayName: Docker devel displayName: Docker 2.13
dependsOn: [] dependsOn: []
jobs: jobs:
- template: templates/matrix.yml - template: templates/matrix.yml
parameters: parameters:
testFormat: devel/linux/{0}/1 testFormat: 2.13/linux/{0}/1
targets: targets:
- name: CentOS 6
test: centos6
- name: CentOS 7 - name: CentOS 7
test: centos7 test: centos7
- name: CentOS 8
test: centos8
- name: Fedora 33
test: fedora33
- name: Fedora 34 - name: Fedora 34
test: fedora34 test: fedora34
- name: Fedora 35
test: fedora35
- name: openSUSE 15 py2 - name: openSUSE 15 py2
test: opensuse15py2 test: opensuse15py2
- name: openSUSE 15 py3 - name: openSUSE 15 py3
@@ -134,8 +135,8 @@ stages:
parameters: parameters:
testFormat: 2.12/linux/{0}/1 testFormat: 2.12/linux/{0}/1
targets: targets:
- name: CentOS 8 - name: CentOS 6
test: centos8 test: centos6
- name: Fedora 33 - name: Fedora 33
test: fedora33 test: fedora33
- name: openSUSE 15 py3 - name: openSUSE 15 py3
@@ -152,8 +153,6 @@ stages:
targets: targets:
- name: CentOS 7 - name: CentOS 7
test: centos7 test: centos7
- name: CentOS 8
test: centos8
- name: Fedora 32 - name: Fedora 32
test: fedora32 test: fedora32
- name: openSUSE 15 py2 - name: openSUSE 15 py2
@@ -194,22 +193,22 @@ stages:
test: ubuntu1804 test: ubuntu1804
### Remote ### Remote
- stage: Remote_devel - stage: Remote_2_13
displayName: Remote devel displayName: Remote 2.13
dependsOn: [] dependsOn: []
jobs: jobs:
- template: templates/matrix.yml - template: templates/matrix.yml
parameters: parameters:
testFormat: devel/{0}/1 testFormat: 2.13/{0}/1
targets: targets:
- name: macOS 11.1 - name: macOS 12.0
test: macos/11.1 test: macos/12.0
- name: RHEL 7.9 - name: RHEL 7.9
test: rhel/7.9 test: rhel/7.9
- name: RHEL 8.4 - name: RHEL 8.5
test: rhel/8.4 test: rhel/8.5
- name: FreeBSD 12.2 - name: FreeBSD 12.3
test: freebsd/12.2 test: freebsd/12.3
- name: FreeBSD 13.0 - name: FreeBSD 13.0
test: freebsd/13.0 test: freebsd/13.0
- stage: Remote_2_12 - stage: Remote_2_12
@@ -265,21 +264,20 @@ stages:
- name: 'RHEL 7.8' - name: 'RHEL 7.8'
test: 'rhel/7.8' test: 'rhel/7.8'
### cloud ### cloud
- stage: Cloud_devel - stage: Cloud_2_13
displayName: Cloud devel displayName: Cloud 2.13
dependsOn: [] dependsOn: []
jobs: jobs:
- template: templates/matrix.yml - template: templates/matrix.yml
parameters: parameters:
nameFormat: Python {0} nameFormat: Python {0}
testFormat: devel/cloud/{0}/1 testFormat: 2.13/cloud/{0}/1
targets: targets:
- test: 2.6
- test: 2.7 - test: 2.7
- test: 3.5 - test: 3.5
- test: 3.6 - test: 3.6
- test: 3.7 - test: 3.7
- test: 3.8 # - test: 3.8
- test: 3.9 - test: 3.9
- test: "3.10" - test: "3.10"
- stage: Cloud_2_12 - stage: Cloud_2_12
@@ -291,6 +289,7 @@ stages:
nameFormat: Python {0} nameFormat: Python {0}
testFormat: 2.12/cloud/{0}/1 testFormat: 2.12/cloud/{0}/1
targets: targets:
- test: 2.6
- test: 3.9 - test: 3.9
- stage: Cloud_2_11 - stage: Cloud_2_11
displayName: Cloud 2.11 displayName: Cloud 2.11
@@ -321,29 +320,29 @@ stages:
nameFormat: Python {0} nameFormat: Python {0}
testFormat: 2.9/cloud/{0}/1 testFormat: 2.9/cloud/{0}/1
targets: targets:
- test: 3.5 - test: 2.7
## Finally ## Finally
- stage: Summary - stage: Summary
condition: succeededOrFailed() condition: succeededOrFailed()
dependsOn: dependsOn:
- Ansible_devel - Ansible_2_13
- Ansible_2_12 - Ansible_2_12
- Ansible_2_11 - Ansible_2_11
- Ansible_2_10 - Ansible_2_10
- Ansible_2_9 - Ansible_2_9
- Remote_devel - Remote_2_13
- Remote_2_12 - Remote_2_12
- Remote_2_11 - Remote_2_11
- Remote_2_10 - Remote_2_10
- Remote_2_9 - Remote_2_9
- Docker_devel - Docker_2_13
- Docker_2_12 - Docker_2_12
- Docker_2_11 - Docker_2_11
- Docker_2_10 - Docker_2_10
- Docker_2_9 - Docker_2_9
- Cloud_devel - Cloud_2_13
- Cloud_2_12 - Cloud_2_12
- Cloud_2_11 - Cloud_2_11
- Cloud_2_10 - Cloud_2_10

View File

@@ -11,7 +11,7 @@ mkdir "${agent_temp_directory}/coverage/"
options=(--venv --venv-system-site-packages --color -v) 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 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. # 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

@@ -12,4 +12,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 pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check
fi 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" summaryFileLocation: "$(outputPath)/reports/$(pipelinesCoverage).xml"
displayName: Publish to Azure Pipelines displayName: Publish to Azure Pipelines
condition: gt(variables.coverageFileCount, 0) 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 displayName: Publish to codecov.io
condition: gt(variables.coverageFileCount, 0) condition: gt(variables.coverageFileCount, 0)
continueOnError: true continueOnError: true

View File

@@ -5,6 +5,154 @@ Community Crypto Release Notes
.. contents:: Topics .. contents:: Topics
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 v1.9.4
====== ======

View File

@@ -11,7 +11,7 @@ Please note that this collection does **not** support Windows targets.
## Tested with Ansible ## 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 ## External requirements

View File

@@ -528,6 +528,89 @@ releases:
changes: changes:
release_summary: Accidental 1.9.1 release. Identical to 1.9.0. release_summary: Accidental 1.9.1 release. Identical to 1.9.0.
release_date: '2021-08-30' 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.2: 1.9.2:
changes: changes:
release_summary: Bugfix release to fix the changelog. No other change compared release_summary: Bugfix release to fix the changelog. No other change compared
@@ -562,3 +645,82 @@ releases:
- 279-acme-openssl.yml - 279-acme-openssl.yml
- 282-acme_challenge_cert_helper-error.yml - 282-acme_challenge_cert_helper-error.yml
release_date: '2021-09-28' 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

@@ -71,7 +71,7 @@ In the following example, we assume that the certificate to sign (including its
- name: Sign certificate with our CA - name: Sign certificate with our CA
community.crypto.x509_certificate_pipe: community.crypto.x509_certificate_pipe:
csr_content: "{{ ca_csr.csr }}" csr_content: "{{ csr.csr }}"
provider: ownca provider: ownca
ownca_path: /path/to/ca-certificate.pem ownca_path: /path/to/ca-certificate.pem
ownca_privatekey_path: /path/to/ca-certificate.key 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 - name: Sign certificate with our CA
community.crypto.x509_certificate_pipe: community.crypto.x509_certificate_pipe:
content: "{{ (certificate.content | b64decode) if certificate_exists.stat.exists else omit }}" content: "{{ (certificate.content | b64decode) if certificate_exists.stat.exists else omit }}"
csr_content: "{{ ca_csr.csr }}" csr_content: "{{ csr.csr }}"
provider: ownca provider: ownca
ownca_path: /path/to/ca-certificate.pem ownca_path: /path/to/ca-certificate.pem
ownca_privatekey_path: /path/to/ca-certificate.key ownca_privatekey_path: /path/to/ca-certificate.key

View File

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

View File

@@ -457,8 +457,8 @@ options:
- Time will always be interpreted as UTC. - Time will always be interpreted as UTC.
- Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
+ C([w | d | h | m | s]) (e.g. C(+32w1d2h). + 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. - 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. - This is only used by the C(ownca) provider.
type: str type: str
default: +0s default: +0s
@@ -470,8 +470,8 @@ options:
- Time will always be interpreted as UTC. - Time will always be interpreted as UTC.
- Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
+ C([w | d | h | m | s]) (e.g. C(+32w1d2h). + 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. - 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. - 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. - 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. 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. - Time will always be interpreted as UTC.
- Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
+ C([w | d | h | m | s]) (e.g. C(+32w1d2h). + 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. - 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. - This is only used by the C(selfsigned) provider.
type: str type: str
default: +0s default: +0s
@@ -562,8 +562,8 @@ options:
- Time will always be interpreted as UTC. - Time will always be interpreted as UTC.
- Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
+ C([w | d | h | m | s]) (e.g. C(+32w1d2h). + 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. - 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. - 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. - 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. Please see U(https://support.apple.com/en-us/HT210176) for more details.

View File

@@ -100,7 +100,7 @@ options:
description: description:
- Determines which format the private key is written in. By default, PKCS1 (traditional OpenSSL format) - 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. 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 but for existing private key files, it will not force a regenerate when its format is not the automatically
selected one for generation. selected one for generation.
- Note that if the format for an existing private key mismatches, the key is B(regenerated) by default. - 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 licenses/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 import locale
from ansible.module_utils.basic import missing_required_lib 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.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 ( from ansible_collections.community.crypto.plugins.module_utils.acme.backend_openssl_cli import (
OpenSSLCLIBackend, OpenSSLCLIBackend,
@@ -228,9 +229,14 @@ class ACMEClient(object):
resp, info = fetch_url(self.module, url, data=data, headers=headers, method='POST') resp, info = fetch_url(self.module, url, data=data, headers=headers, method='POST')
_assert_fetch_url_success(self.module, resp, info) _assert_fetch_url_success(self.module, resp, info)
result = {} result = {}
try: 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() content = resp.read()
except AttributeError: except (AttributeError, TypeError):
content = info.pop('body', None) content = info.pop('body', None)
if content or not parse_json_result: if content or not parse_json_result:
@@ -284,8 +290,12 @@ class ACMEClient(object):
_assert_fetch_url_success(self.module, resp, info) _assert_fetch_url_success(self.module, resp, info)
try: 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() content = resp.read()
except AttributeError: except (AttributeError, TypeError):
content = info.pop('body', None) content = info.pop('body', None)
# Process result # Process result

View File

@@ -14,7 +14,9 @@ import datetime
import os import os
import sys 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 ( from ansible_collections.community.crypto.plugins.module_utils.acme.backends import (
CryptoBackend, CryptoBackend,
@@ -41,6 +43,10 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptograp
cryptography_name_to_oid, cryptography_name_to_oid,
) )
from ansible_collections.community.crypto.plugins.module_utils.crypto.pem import (
extract_first_pem,
)
try: try:
import cryptography import cryptography
import cryptography.hazmat.backends import cryptography.hazmat.backends
@@ -53,7 +59,6 @@ try:
import cryptography.hazmat.primitives.serialization import cryptography.hazmat.primitives.serialization
import cryptography.x509 import cryptography.x509
import cryptography.x509.oid import cryptography.x509.oid
from distutils.version import LooseVersion
CRYPTOGRAPHY_VERSION = cryptography.__version__ CRYPTOGRAPHY_VERSION = cryptography.__version__
HAS_CURRENT_CRYPTOGRAPHY = (LooseVersion(CRYPTOGRAPHY_VERSION) >= LooseVersion('1.5')) HAS_CURRENT_CRYPTOGRAPHY = (LooseVersion(CRYPTOGRAPHY_VERSION) >= LooseVersion('1.5'))
if HAS_CURRENT_CRYPTOGRAPHY: if HAS_CURRENT_CRYPTOGRAPHY:
@@ -357,6 +362,9 @@ class CryptographyBackend(CryptoBackend):
if cert_content is None: if cert_content is None:
return -1 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: try:
cert = cryptography.x509.load_pem_x509_certificate(cert_content, _cryptography_backend) cert = cryptography.x509.load_pem_x509_certificate(cert_content, _cryptography_backend)
except Exception as e: except Exception as e:

View File

@@ -7,8 +7,8 @@
from __future__ import absolute_import, division, print_function from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
from ansible.module_utils.six import binary_type
from ansible.module_utils.common.text.converters import to_text 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=''): 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 # 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: if content is None and content_json is None and response is not None:
try: 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() content = response.read()
except AttributeError: except (AttributeError, TypeError):
content = info.pop('body', None) content = info.pop('body', None)
# Make sure that content_json is None or a dictionary # Make sure that content_json is None or a dictionary

View File

@@ -20,6 +20,10 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type __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): def obj2txt(openssl_lib, openssl_ffi, obj):
# Set to 80 on the recommendation of # Set to 80 on the recommendation of
# https://www.openssl.org/docs/crypto/OBJ_nid2ln.html#return_values # https://www.openssl.org/docs/crypto/OBJ_nid2ln.html#return_values

View File

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

View File

@@ -26,15 +26,41 @@ import re
from ansible.module_utils.common.text.converters import to_text, to_bytes from ansible.module_utils.common.text.converters import to_text, to_bytes
from ._asn1 import serialize_asn1_string_as_der from ._asn1 import serialize_asn1_string_as_der
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
try: try:
import cryptography import cryptography
from cryptography import x509 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 import serialization
from cryptography.hazmat.primitives.asymmetric import padding
import ipaddress import ipaddress
except ImportError: except ImportError:
# Error handled in the calling module. # Error handled in the calling module.
pass 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: try:
# This is a separate try/except since this is only present in cryptography 2.5 or newer # This is a separate try/except since this is only present in cryptography 2.5 or newer
from cryptography.hazmat.primitives.serialization.pkcs12 import ( from cryptography.hazmat.primitives.serialization.pkcs12 import (
@@ -44,9 +70,23 @@ except ImportError:
# Error handled in the calling module. # Error handled in the calling module.
_load_key_and_certificates = None _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 ( from .basic import (
CRYPTOGRAPHY_HAS_DSA_SIGN,
CRYPTOGRAPHY_HAS_EC_SIGN,
CRYPTOGRAPHY_HAS_ED25519, CRYPTOGRAPHY_HAS_ED25519,
CRYPTOGRAPHY_HAS_ED25519_SIGN,
CRYPTOGRAPHY_HAS_ED448, CRYPTOGRAPHY_HAS_ED448,
CRYPTOGRAPHY_HAS_ED448_SIGN,
CRYPTOGRAPHY_HAS_RSA_SIGN,
OpenSSLObjectError, OpenSSLObjectError,
) )
@@ -64,60 +104,114 @@ DOTTED_OID = re.compile(r'^\d+(?:\.\d+)+$')
def cryptography_get_extensions_from_cert(cert): 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() result = dict()
backend = cert._backend try:
x509_obj = cert._x509 # 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 return result
def cryptography_get_extensions_from_csr(csr): 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() 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._lib.X509_REQ_get_extensions(csr._x509_req)
extensions = backend._ffi.gc( extensions = backend._ffi.gc(
extensions, extensions,
lambda ext: backend._lib.sk_X509_EXTENSION_pop_free( lambda ext: backend._lib.sk_X509_EXTENSION_pop_free(
ext, ext,
backend._ffi.addressof(backend._lib._original_lib, "X509_EXTENSION_free") backend._ffi.addressof(backend._lib._original_lib, "X509_EXTENSION_free")
)
) )
)
for i in range(backend._lib.sk_X509_EXTENSION_num(extensions)): # With cryptography 35.0.0, we can no longer use obj2txt. Unfortunately it still does
ext = backend._lib.sk_X509_EXTENSION_value(extensions, i) # not allow to get the raw value of an extension, so we have to use this ugly hack:
if ext == backend._ffi.NULL: exts = list(csr.extensions)
continue
crit = backend._lib.X509_EXTENSION_get_critical(ext) for i in range(backend._lib.sk_X509_EXTENSION_num(extensions)):
data = backend._lib.X509_EXTENSION_get_data(ext) ext = backend._lib.sk_X509_EXTENSION_value(extensions, i)
backend.openssl_assert(data != backend._ffi.NULL) if ext == backend._ffi.NULL:
der = backend._ffi.buffer(data.data, data.length)[:] continue
entry = dict( crit = backend._lib.X509_EXTENSION_get_critical(ext)
critical=(crit == 1), data = backend._lib.X509_EXTENSION_get_data(ext)
value=base64.b64encode(der), backend.openssl_assert(data != backend._ffi.NULL)
) der = backend._ffi.buffer(data.data, data.length)[:]
oid = obj2txt(backend._lib, backend._ffi, backend._lib.X509_EXTENSION_get_object(ext)) entry = dict(
result[oid] = entry 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 return result
@@ -280,11 +374,11 @@ def _dn_escape_value(value):
''' '''
Escape Distinguished Name's attribute value. Escape Distinguished Name's attribute value.
''' '''
value = value.replace('\\', '\\\\') value = value.replace(u'\\', u'\\\\')
for ch in [',', '#', '+', '<', '>', ';', '"', '=', '/']: for ch in [u',', u'#', u'+', u'<', u'>', u';', u'"', u'=', u'/']:
value = value.replace(ch, '\\%s' % ch) value = value.replace(ch, u'\\%s' % ch)
if value.startswith(' '): if value.startswith(u' '):
value = r'\ ' + value[1:] value = u'\\ ' + value[1:]
return value return value
@@ -294,24 +388,24 @@ def cryptography_decode_name(name):
Raises an OpenSSLObjectError if the name is not supported. Raises an OpenSSLObjectError if the name is not supported.
''' '''
if isinstance(name, x509.DNSName): 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, x509.IPAddress):
if isinstance(name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network)): if isinstance(name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network)):
return 'IP:{0}/{1}'.format(name.value.network_address.compressed, name.value.prefixlen) return u'IP:{0}/{1}'.format(name.value.network_address.compressed, name.value.prefixlen)
return 'IP:{0}'.format(name.value.compressed) return u'IP:{0}'.format(name.value.compressed)
if isinstance(name, x509.RFC822Name): if isinstance(name, x509.RFC822Name):
return 'email:{0}'.format(name.value) return u'email:{0}'.format(name.value)
if isinstance(name, x509.UniformResourceIdentifier): if isinstance(name, x509.UniformResourceIdentifier):
return 'URI:{0}'.format(name.value) return u'URI:{0}'.format(name.value)
if isinstance(name, x509.DirectoryName): if isinstance(name, x509.DirectoryName):
return 'dirName:' + ''.join([ return u'dirName:' + u''.join([
'/{0}={1}'.format(cryptography_oid_to_name(attribute.oid, short=True), _dn_escape_value(attribute.value)) u'/{0}={1}'.format(to_text(cryptography_oid_to_name(attribute.oid, short=True)), _dn_escape_value(attribute.value))
for attribute in name.value for attribute in name.value
]) ])
if isinstance(name, x509.RegisteredID): 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): 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)) 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): def parse_pkcs12(pkcs12_bytes, passphrase=None):
'''Returns a tuple (private_key, certificate, additional_certificates, friendly_name). '''Returns a tuple (private_key, certificate, additional_certificates, friendly_name).
''' '''
if _load_key_and_certificates is None: if _load_pkcs12 is None and _load_key_and_certificates is None:
raise ValueError('load_key_and_certificates() not present in the current cryptography version') raise ValueError('neither load_pkcs12() nor load_key_and_certificates() 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 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 friendly_name = None
if certificate: if certificate:
# See https://github.com/pyca/cryptography/issues/5760#issuecomment-842687238 # See https://github.com/pyca/cryptography/issues/5760#issuecomment-842687238
maybe_name = certificate._backend._lib.X509_alias_get0( backend = default_backend()
certificate._x509, certificate._backend._ffi.NULL)
if maybe_name != certificate._backend._ffi.NULL: # This code basically does what load_key_and_certificates() does, but without error-checking.
friendly_name = certificate._backend._ffi.string(maybe_name) # 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 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 abc
import traceback import traceback
from distutils.version import LooseVersion
from ansible.module_utils import six from ansible.module_utils import six
from ansible.module_utils.basic import missing_required_lib 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.module_backends.common import ArgumentSpec
from ansible_collections.community.crypto.plugins.module_utils.crypto.basic import ( from ansible_collections.community.crypto.plugins.module_utils.crypto.basic import (
@@ -45,7 +45,7 @@ try:
import OpenSSL import OpenSSL
from OpenSSL import crypto from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__) PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError: except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc() PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False PYOPENSSL_FOUND = False
else: else:

View File

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

View File

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

View File

@@ -10,11 +10,12 @@ __metaclass__ = type
import os import os
from distutils.version import LooseVersion
from random import randrange from random import randrange
from ansible.module_utils.common.text.converters import to_bytes 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 ( from ansible_collections.community.crypto.plugins.module_utils.crypto.basic import (
OpenSSLBadPassphraseError, 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 ( from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptography_support import (
cryptography_compare_public_keys,
cryptography_key_needs_digest_for_signing, cryptography_key_needs_digest_for_signing,
cryptography_serial_number_of_cert, cryptography_serial_number_of_cert,
cryptography_verify_certificate_signature,
) )
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.certificate import ( 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: try:
from OpenSSL import crypto from OpenSSL import crypto
except ImportError: except (ImportError, AttributeError):
pass pass
try: try:
@@ -106,6 +109,9 @@ class OwnCACertificateBackendCryptography(CertificateBackend):
except OpenSSLBadPassphraseError as exc: except OpenSSLBadPassphraseError as exc:
module.fail_json(msg=str(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 cryptography_key_needs_digest_for_signing(self.ca_private_key):
if self.digest is None: if self.digest is None:
raise CertificateError( raise CertificateError(
@@ -172,6 +178,16 @@ class OwnCACertificateBackendCryptography(CertificateBackend):
if super(OwnCACertificateBackendCryptography, self).needs_regeneration(): if super(OwnCACertificateBackendCryptography, self).needs_regeneration():
return True 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 # Check AuthorityKeyIdentifier
if self.create_authority_key_identifier: if self.create_authority_key_identifier:
try: try:
@@ -184,7 +200,6 @@ class OwnCACertificateBackendCryptography(CertificateBackend):
except cryptography.x509.ExtensionNotFound: except cryptography.x509.ExtensionNotFound:
expected_ext = x509.AuthorityKeyIdentifier.from_issuer_public_key(self.ca_cert.public_key()) expected_ext = x509.AuthorityKeyIdentifier.from_issuer_public_key(self.ca_cert.public_key())
self._ensure_existing_certificate_loaded()
try: try:
ext = self.existing_certificate.extensions.get_extension_for_class(x509.AuthorityKeyIdentifier) ext = self.existing_certificate.extensions.get_extension_for_class(x509.AuthorityKeyIdentifier)
if ext.value != expected_ext: if ext.value != expected_ext:
@@ -296,6 +311,18 @@ class OwnCACertificateBackendPyOpenSSL(CertificateBackend):
"""Return bytes for self.cert.""" """Return bytes for self.cert."""
return crypto.dump_certificate(crypto.FILETYPE_PEM, 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): def dump(self, include_certificate):
result = super(OwnCACertificateBackendPyOpenSSL, self).dump(include_certificate) result = super(OwnCACertificateBackendPyOpenSSL, self).dump(include_certificate)
result.update({ 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 ( from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptography_support import (
cryptography_key_needs_digest_for_signing, cryptography_key_needs_digest_for_signing,
cryptography_serial_number_of_cert, cryptography_serial_number_of_cert,
cryptography_verify_certificate_signature,
) )
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.certificate import ( 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: try:
from OpenSSL import crypto from OpenSSL import crypto
except ImportError: except (ImportError, AttributeError):
pass pass
try: try:
@@ -134,6 +135,18 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend):
"""Return bytes for self.cert.""" """Return bytes for self.cert."""
return self.cert.public_bytes(Encoding.PEM) 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): def dump(self, include_certificate):
result = super(SelfSignedCertificateBackendCryptography, self).dump(include_certificate) result = super(SelfSignedCertificateBackendCryptography, self).dump(include_certificate)

View File

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

View File

@@ -12,12 +12,12 @@ import abc
import binascii import binascii
import traceback import traceback
from distutils.version import LooseVersion
from ansible.module_utils import six from ansible.module_utils import six
from ansible.module_utils.basic import missing_required_lib from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.common.text.converters import to_bytes, to_text 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 ( from ansible_collections.community.crypto.plugins.module_utils.crypto.basic import (
OpenSSLObjectError, OpenSSLObjectError,
OpenSSLBadPassphraseError, OpenSSLBadPassphraseError,
@@ -63,7 +63,7 @@ try:
import OpenSSL import OpenSSL
from OpenSSL import crypto from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__) PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError: except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc() PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False PYOPENSSL_FOUND = False
else: else:

View File

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

View File

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

View File

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

View File

@@ -10,12 +10,12 @@ __metaclass__ = type
import abc import abc
import traceback import traceback
from distutils.version import LooseVersion
from ansible.module_utils import six from ansible.module_utils import six
from ansible.module_utils.basic import missing_required_lib from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.common.text.converters import to_native 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 ( from ansible_collections.community.crypto.plugins.module_utils.crypto.basic import (
CRYPTOGRAPHY_HAS_X25519, CRYPTOGRAPHY_HAS_X25519,
CRYPTOGRAPHY_HAS_X448, CRYPTOGRAPHY_HAS_X448,
@@ -38,7 +38,7 @@ try:
import OpenSSL import OpenSSL
from OpenSSL import crypto from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__) PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError: except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc() PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False PYOPENSSL_FOUND = False
else: else:

View File

@@ -72,3 +72,13 @@ def split_pem_list(text, keep_inbetween=False):
result.append(''.join(current)) result.append(''.join(current))
current = [] if keep_inbetween else None current = [] if keep_inbetween else None
return result 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 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 ansible_collections.community.crypto.plugins.module_utils.compat import ipaddress as compat_ipaddress
from ._objects import OID_LOOKUP
try: try:
import OpenSSL import OpenSSL
except ImportError: except (ImportError, AttributeError):
# Error handled in the calling module. # Error handled in the calling module.
pass pass
@@ -87,18 +89,25 @@ def pyopenssl_get_extensions_from_cert(cert):
critical=bool(ext.get_critical()), critical=bool(ext.get_critical()),
value=base64.b64encode(ext.get_data()), value=base64.b64encode(ext.get_data()),
) )
oid = obj2txt( try:
OpenSSL._util.lib, oid = obj2txt(
OpenSSL._util.ffi, OpenSSL._util.lib,
OpenSSL._util.lib.X509_EXTENSION_get_object(ext._extension) OpenSSL._util.ffi,
) OpenSSL._util.lib.X509_EXTENSION_get_object(ext._extension)
# This could also be done a bit simpler: )
# # This could also be done a bit simpler:
# oid = obj2txt(OpenSSL._util.lib, OpenSSL._util.ffi, OpenSSL._util.lib.OBJ_nid2obj(ext._nid)) #
# # 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 # Unfortunately this gives the wrong result in case the linked OpenSSL
# similarly to how cryptography does it. # 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 result[oid] = entry
return result return result
@@ -113,18 +122,25 @@ def pyopenssl_get_extensions_from_csr(csr):
critical=bool(ext.get_critical()), critical=bool(ext.get_critical()),
value=base64.b64encode(ext.get_data()), value=base64.b64encode(ext.get_data()),
) )
oid = obj2txt( try:
OpenSSL._util.lib, oid = obj2txt(
OpenSSL._util.ffi, OpenSSL._util.lib,
OpenSSL._util.lib.X509_EXTENSION_get_object(ext._extension) OpenSSL._util.ffi,
) OpenSSL._util.lib.X509_EXTENSION_get_object(ext._extension)
# This could also be done a bit simpler: )
# # This could also be done a bit simpler:
# oid = obj2txt(OpenSSL._util.lib, OpenSSL._util.ffi, OpenSSL._util.lib.OBJ_nid2obj(ext._nid)) #
# # 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 # Unfortunately this gives the wrong result in case the linked OpenSSL
# similarly to how cryptography does it. # 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 result[oid] = entry
return result return result

View File

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

View File

@@ -21,9 +21,11 @@ __metaclass__ = type
import abc import abc
import os import os
import stat import stat
import traceback
from ansible.module_utils import six 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 ( from ansible_collections.community.crypto.plugins.module_utils.openssh.utils import (
parse_openssh_version, parse_openssh_version,
) )
@@ -75,7 +77,14 @@ class OpensshModule(object):
self.check_mode = self.module.check_mode self.check_mode = self.module.check_mode
def execute(self): 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) self.module.exit_json(**self.result)
@abc.abstractmethod @abc.abstractmethod

View File

@@ -21,12 +21,13 @@ __metaclass__ = type
import abc import abc
import os import os
from distutils.version import LooseVersion
from ansible.module_utils import six from ansible.module_utils import six
from ansible.module_utils.basic import missing_required_lib 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.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 ( from ansible_collections.community.crypto.plugins.module_utils.openssh.cryptography import (
HAS_OPENSSH_SUPPORT, HAS_OPENSSH_SUPPORT,
HAS_OPENSSH_PRIVATE_FORMAT, HAS_OPENSSH_PRIVATE_FORMAT,

View File

@@ -20,10 +20,11 @@ __metaclass__ = type
import os import os
from base64 import b64encode, b64decode from base64 import b64encode, b64decode
from distutils.version import LooseVersion
from getpass import getuser from getpass import getuser
from socket import gethostname from socket import gethostname
from ansible_collections.community.crypto.plugins.module_utils.version import LooseVersion
try: try:
from cryptography import __version__ as CRYPTOGRAPHY_VERSION from cryptography import __version__ as CRYPTOGRAPHY_VERSION
from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm 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 - name: Check whether an account with the given account key exists
community.crypto.acme_account_info: community.crypto.acme_account_info:
account_key_src: /etc/pki/cert/private/account.key account_key_src: /etc/pki/cert/private/account.key
register: account_data register: account_data
- name: Verify that account exists - name: Verify that account exists
assert: assert:
that: that:
@@ -70,7 +70,7 @@ EXAMPLES = '''
acme_account_info: acme_account_info:
account_key_content: "{{ acme_account_key }}" account_key_content: "{{ acme_account_key }}"
account_uri: "{{ acme_account_uri }}" account_uri: "{{ acme_account_uri }}"
register: account_data register: account_data
- name: Verify that account exists - name: Verify that account exists
assert: assert:
that: that:

View File

@@ -308,7 +308,7 @@ EXAMPLES = r'''
# - copy: # - copy:
# dest: /var/www/html/{{ sample_com_challenge['challenge_data']['sample.com']['http-01']['resource'] }} # 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'] }}" # 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: # Alternative way:
# #

View File

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

View File

@@ -124,6 +124,8 @@ import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_bytes 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 ( from ansible_collections.community.crypto.plugins.module_utils.crypto.pem import (
split_pem_list, split_pem_list,
) )
@@ -140,7 +142,6 @@ try:
import cryptography.hazmat.primitives.asymmetric.utils import cryptography.hazmat.primitives.asymmetric.utils
import cryptography.x509 import cryptography.x509
import cryptography.x509.oid import cryptography.x509.oid
from distutils.version import LooseVersion
HAS_CRYPTOGRAPHY = (LooseVersion(cryptography.__version__) >= LooseVersion('1.5')) HAS_CRYPTOGRAPHY = (LooseVersion(cryptography.__version__) >= LooseVersion('1.5'))
_cryptography_backend = cryptography.hazmat.backends.default_backend() _cryptography_backend = cryptography.hazmat.backends.default_backend()
except ImportError as dummy: except ImportError as dummy:
@@ -236,13 +237,17 @@ class CertificateSet(object):
def __init__(self, module): def __init__(self, module):
self.module = module self.module = module
self.certificates = set() self.certificates = set()
self.certificate_by_issuer = dict() self.certificates_by_issuer = dict()
self.certificate_by_cert = dict()
def _load_file(self, path): def _load_file(self, path):
certs = load_PEM_list(self.module, path, fail_on_error=False) certs = load_PEM_list(self.module, path, fail_on_error=False)
for cert in certs: for cert in certs:
self.certificates.add(cert) 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): def load(self, path):
''' '''
@@ -260,8 +265,8 @@ class CertificateSet(object):
''' '''
Search for the parent (issuer) of a certificate. Return ``None`` if none was found. Search for the parent (issuer) of a certificate. Return ``None`` if none was found.
''' '''
potential_parent = self.certificate_by_issuer.get(cert.cert.issuer) potential_parents = self.certificates_by_issuer.get(cert.cert.issuer, [])
if potential_parent is not None: for potential_parent in potential_parents:
if is_parent(self.module, cert, potential_parent): if is_parent(self.module, cert, potential_parent):
return potential_parent return potential_parent
return None return None
@@ -274,6 +279,16 @@ def format_cert(cert):
return str(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(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
@@ -312,13 +327,19 @@ def main():
# Try to complete chain # Try to complete chain
current = chain[-1] current = chain[-1]
completed = [] 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: while current:
root = roots.find_parent(current) root = roots.find_parent(current)
if root: if root:
check_cycle(module, occured_certificates, root)
completed.append(root) completed.append(root)
break break
intermediate = intermediates.find_parent(current) intermediate = intermediates.find_parent(current)
if intermediate: if intermediate:
check_cycle(module, occured_certificates, intermediate)
completed.append(intermediate) completed.append(intermediate)
current = intermediate current = intermediate
else: else:

View File

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

View File

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

View File

@@ -350,12 +350,40 @@ STDERR = 2
# used to get <luks-name> out of lsblk output in format 'crypt <luks-name>' # used to get <luks-name> out of lsblk output in format 'crypt <luks-name>'
# regex takes care of any possible blank characters # 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 # used to get </luks/device> out of lsblk output
# in format 'device: </luks/device>' # in format 'device: </luks/device>'
LUKS_DEVICE_REGEX = re.compile(r'\s*device:\s+([^\s]*)\s*') 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): class Handler(object):
def __init__(self, module): def __init__(self, module):
@@ -418,13 +446,11 @@ class CryptHandler(Handler):
raise ValueError('Error while obtaining LUKS name for %s: %s' raise ValueError('Error while obtaining LUKS name for %s: %s'
% (device, result[STDERR])) % (device, result[STDERR]))
m = LUKS_NAME_REGEX.search(result[STDOUT]) for line in result[STDOUT].splitlines(False):
m = LUKS_NAME_REGEX.match(line)
try: if m:
name = m.group(1) return m.group(1)
except AttributeError: return None
name = None
return name
def get_container_device_by_name(self, name): def get_container_device_by_name(self, name):
''' obtain device name based on the LUKS container name ''' obtain device name based on the LUKS container name
@@ -515,9 +541,17 @@ class CryptHandler(Handler):
self.run_luks_close(name) self.run_luks_close(name)
result = self._run_command([wipefs_bin, '--all', device]) result = self._run_command([wipefs_bin, '--all', device])
if result[RETURN_CODE] != 0: 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])) % (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, def run_luks_add_key(self, device, keyfile, passphrase, new_keyfile,
new_passphrase, pbkdf): new_passphrase, pbkdf):
''' Add new key from a keyfile or passphrase to given 'device'; ''' Add new key from a keyfile or passphrase to given 'device';
@@ -785,6 +819,7 @@ def run_module():
module = AnsibleModule(argument_spec=module_args, module = AnsibleModule(argument_spec=module_args,
supports_check_mode=True, supports_check_mode=True,
mutually_exclusive=mutually_exclusive) 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: if module.params['device'] is not None:
try: try:

View File

@@ -258,11 +258,12 @@ info:
''' '''
import os import os
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.common.text.converters import to_native, to_text 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 ( from ansible_collections.community.crypto.plugins.module_utils.openssh.backends.common import (
KeygenCommand, KeygenCommand,
OpensshModule, OpensshModule,
@@ -378,7 +379,7 @@ class Certificate(OpensshModule):
def _is_fully_valid(self): def _is_fully_valid(self):
return self._is_partially_valid() and all([ 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.key_id == self.identifier,
self.original_data.public_key == self._get_key_fingerprint(self.public_key), self.original_data.public_key == self._get_key_fingerprint(self.public_key),
self.original_data.signing_key == self._get_key_fingerprint(self.signing_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. description: The public key of the generated SSH private key.
returned: changed or success returned: changed or success
type: str type: str
sample: ssh-rsa AAAAB3Nza(...omitted...)veL4E3Xcw== test_key sample: ssh-rsa AAAAB3Nza(...omitted...)veL4E3Xcw==
comment: comment:
description: The comment of the generated key. description: The comment of the generated key.
returned: changed or success returned: changed or success

View File

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

View File

@@ -239,11 +239,11 @@ import os
import stat import stat
import traceback import traceback
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_bytes, to_native 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 ( from ansible_collections.community.crypto.plugins.module_utils.io import (
load_file_if_exists, load_file_if_exists,
write_file, write_file,
@@ -276,7 +276,7 @@ try:
import OpenSSL import OpenSSL
from OpenSSL import crypto from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__) PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError: except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc() PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False PYOPENSSL_FOUND = False
else: else:

View File

@@ -182,11 +182,11 @@ publickey:
import os import os
import traceback import traceback
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_native 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 ( from ansible_collections.community.crypto.plugins.module_utils.io import (
load_file_if_exists, load_file_if_exists,
write_file, write_file,
@@ -217,7 +217,7 @@ try:
import OpenSSL import OpenSSL
from OpenSSL import crypto from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__) PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError: except (ImportError, AttributeError):
PYOPENSSL_IMP_ERR = traceback.format_exc() PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False PYOPENSSL_FOUND = False
else: else:

View File

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

View File

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

View File

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

View File

@@ -367,11 +367,11 @@ import base64
import os import os
import traceback import traceback
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_native, to_text 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 ( from ansible_collections.community.crypto.plugins.module_utils.io import (
write_file, write_file,
) )
@@ -664,9 +664,7 @@ class CRL(OpenSSLObject):
revoked_cert = revoked_cert.revocation_date(entry['revocation_date']) revoked_cert = revoked_cert.revocation_date(entry['revocation_date'])
if entry['issuer'] is not None: if entry['issuer'] is not None:
revoked_cert = revoked_cert.add_extension( revoked_cert = revoked_cert.add_extension(
x509.CertificateIssuer([ x509.CertificateIssuer(entry['issuer']),
cryptography_get_name(name, 'issuer') for name in entry['issuer']
]),
entry['issuer_critical'] entry['issuer_critical']
) )
if entry['reason'] is not None: if entry['reason'] is not None:

View File

@@ -1,3 +1,4 @@
dependencies: dependencies:
- prepare_jinja2_compat
- setup_openssl - setup_openssl
- setup_remote_tmp_dir - 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: - block:
- name: Make sure testhost directory exists - name: Make sure testhost directory exists
file: file:
path: '{{ remote_tmp_dir }}/files/' path: '{{ remote_tmp_dir }}/files/'
@@ -13,68 +14,14 @@
copy: copy:
src: '{{ role_path }}/files/' src: '{{ role_path }}/files/'
dest: '{{ remote_tmp_dir }}/files/' dest: '{{ remote_tmp_dir }}/files/'
- name: Find root for cert 1
certificate_complete_chain: - name: Run tests with copied certificates
input_chain: '{{ lookup("file", "cert1-fullchain.pem", rstrip=False) }}' import_tasks: existing.yml
root_certificates:
- '{{ remote_tmp_dir }}/files/roots/' - name: Create more certificates
register: cert1_root import_tasks: create.yml
- name: Verify root for cert 1
assert: - name: Run tests with created certificates
that: import_tasks: created.yml
- 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)
when: cryptography_version.stdout is version('1.5', '>=') when: cryptography_version.stdout is version('1.5', '>=')

View File

@@ -86,6 +86,27 @@
regenerate: full_idempotence regenerate: full_idempotence
register: default_options 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 - name: Assert options results
assert: assert:
that: that:
@@ -95,6 +116,7 @@
- explicit_extension_after is not changed - explicit_extension_after is not changed
- explicit_extension_and_directive is changed - explicit_extension_and_directive is changed
- default_options is not changed - default_options is not changed
- host_cert_full_idempotence is not changed
- name: Remove certificate - name: Remove certificate
openssh_cert: openssh_cert:

View File

@@ -8,7 +8,7 @@
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
register: result 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: assert:
that: that:
- result.subject.organizationalUnitName == 'ACME Department' - result.subject.organizationalUnitName == 'ACME Department'
@@ -16,6 +16,21 @@
- "['organizationalUnitName', 'ACME Department'] in result.subject_ordered" - "['organizationalUnitName', 'ACME Department'] in result.subject_ordered"
- result.public_key_type == 'RSA' - result.public_key_type == 'RSA'
- result.public_key_data.size == default_rsa_key_size - 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" - name: "({{ select_crypto_backend }}) Check SubjectKeyIdentifier and AuthorityKeyIdentifier"
assert: assert:
@@ -24,6 +39,10 @@
- result.authority_key_identifier == "44:55:66:77" - result.authority_key_identifier == "44:55:66:77"
- result.authority_cert_issuer == expected_authority_cert_issuer - result.authority_cert_issuer == expected_authority_cert_issuer
- result.authority_cert_serial_number == 12345 - 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: vars:
expected_authority_cert_issuer: expected_authority_cert_issuer:
- "DNS:ca.example.org" - "DNS:ca.example.org"

View File

@@ -83,4 +83,4 @@
- p12_empty is changed - p12_empty is changed
- p12_empty_idem is not changed - p12_empty_idem is not changed
- p12_empty_concat_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 }}' size: '{{ default_rsa_key_size }}'
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
register: privatekey_mode_1 register: privatekey_mode_1
- name: "({{ select_crypto_backend }}) Stat for privatekey_mode" - name: "({{ select_crypto_backend }}) Stat for privatekey_mode"
stat: stat:
path: '{{ remote_tmp_dir }}/privatekey_mode.pem' path: '{{ remote_tmp_dir }}/privatekey_mode.pem'
register: privatekey_mode_1_stat 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)" - name: "({{ select_crypto_backend }}) Generate privatekey_mode (mode 0400, idempotency)"
openssl_privatekey: openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekey_mode.pem' path: '{{ remote_tmp_dir }}/privatekey_mode.pem'
@@ -316,13 +323,6 @@
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
register: privatekey_mode_2 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)" - name: "({{ select_crypto_backend }}) Generate privatekey_mode (mode 0400, force)"
openssl_privatekey: openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekey_mode.pem' path: '{{ remote_tmp_dir }}/privatekey_mode.pem'
@@ -331,11 +331,17 @@
size: '{{ default_rsa_key_size }}' size: '{{ default_rsa_key_size }}'
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
register: privatekey_mode_3 register: privatekey_mode_3
- name: "({{ select_crypto_backend }}) Stat for privatekey_mode" - name: "({{ select_crypto_backend }}) Stat for privatekey_mode"
stat: stat:
path: '{{ remote_tmp_dir }}/privatekey_mode.pem' path: '{{ remote_tmp_dir }}/privatekey_mode.pem'
register: privatekey_mode_3_stat 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: - block:
- name: "({{ select_crypto_backend }}) Generate privatekey_fmt_1 - auto format" - name: "({{ select_crypto_backend }}) Generate privatekey_fmt_1 - auto format"
openssl_privatekey: openssl_privatekey:

View File

@@ -186,7 +186,7 @@
- privatekey_mode_2 is not changed - privatekey_mode_2 is not changed
- privatekey_mode_3 is changed - privatekey_mode_3 is changed
- privatekey_mode_3_stat.stat.mode == '0400' - 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" - name: "({{ select_crypto_backend }}) Validate format 1"
assert: assert:

View File

@@ -32,11 +32,15 @@ system_python_version_data:
- '3.8' - '3.8'
'11.1': '11.1':
- '3.9' - '3.9'
'12.0':
- '3.10'
FreeBSD: FreeBSD:
'12.1': '12.1':
- '3.6' - '3.6'
'12.2': '12.2':
- '3.7' - '3.7'
'12.3':
- '3.8'
'13.0': '13.0':
- '3.7' - '3.7'
RedHat: RedHat:
@@ -55,3 +59,6 @@ cannot_upgrade_cryptography:
- '3.8' # on the VMs in CI, system packages are used for this version as well - '3.8' # on the VMs in CI, system packages are used for this version as well
'13.0': '13.0':
- '3.8' # on the VMs in CI, system packages are used for this version as well - '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

@@ -14,14 +14,20 @@
- name: (OwnCA, {{select_crypto_backend}}) Generate CA CSR - name: (OwnCA, {{select_crypto_backend}}) Generate CA CSR
openssl_csr: openssl_csr:
path: '{{ remote_tmp_dir }}/ca_csr.csr' path: '{{ item.path }}'
privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem' privatekey_path: '{{ remote_tmp_dir }}/ca_privatekey.pem'
subject: subject: '{{ item.subject }}'
commonName: Example CA
useCommonNameForSAN: no useCommonNameForSAN: no
basic_constraints: basic_constraints:
- 'CA:TRUE' - 'CA:TRUE'
basic_constraints_critical: yes 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) - name: (OwnCA, {{select_crypto_backend}}) Generate CA CSR (privatekey passphrase)
openssl_csr: openssl_csr:
@@ -62,6 +68,15 @@
- result_check_mode is changed - result_check_mode is changed
- result 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) - name: (OwnCA, {{select_crypto_backend}}) Generate selfsigned CA certificate (privatekey passphrase)
x509_certificate: x509_certificate:
path: '{{ remote_tmp_dir }}/ca_cert_pw.pem' path: '{{ remote_tmp_dir }}/ca_cert_pw.pem'
@@ -110,6 +125,54 @@
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
check_mode: yes 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 - name: (OwnCA, {{select_crypto_backend}}) Check ownca certificate
x509_certificate: x509_certificate:
path: '{{ remote_tmp_dir }}/ownca_cert.pem' path: '{{ remote_tmp_dir }}/ownca_cert.pem'
@@ -285,7 +348,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_backup.pem' path: '{{ remote_tmp_dir }}/ownca_cert_backup.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr' csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem' 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 provider: ownca
ownca_digest: sha256 ownca_digest: sha256
backup: yes backup: yes
@@ -296,7 +359,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_backup.pem' path: '{{ remote_tmp_dir }}/ownca_cert_backup.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr' csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem' 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 provider: ownca
ownca_digest: sha256 ownca_digest: sha256
backup: yes backup: yes
@@ -307,7 +370,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_backup.pem' path: '{{ remote_tmp_dir }}/ownca_cert_backup.pem'
csr_path: '{{ remote_tmp_dir }}/csr.csr' csr_path: '{{ remote_tmp_dir }}/csr.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem' 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 provider: ownca
ownca_digest: sha256 ownca_digest: sha256
backup: yes backup: yes
@@ -335,7 +398,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem' path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr' csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem' 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 provider: ownca
ownca_digest: sha256 ownca_digest: sha256
ownca_create_subject_key_identifier: always_create ownca_create_subject_key_identifier: always_create
@@ -348,7 +411,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem' path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr' csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem' 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 provider: ownca
ownca_digest: sha256 ownca_digest: sha256
ownca_create_subject_key_identifier: always_create ownca_create_subject_key_identifier: always_create
@@ -361,7 +424,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem' path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr' csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem' 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 provider: ownca
ownca_digest: sha256 ownca_digest: sha256
ownca_create_subject_key_identifier: never_create ownca_create_subject_key_identifier: never_create
@@ -374,7 +437,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem' path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr' csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem' 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 provider: ownca
ownca_digest: sha256 ownca_digest: sha256
ownca_create_subject_key_identifier: never_create ownca_create_subject_key_identifier: never_create
@@ -387,7 +450,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem' path: '{{ remote_tmp_dir }}/ownca_cert_ski.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr' csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem' 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 provider: ownca
ownca_digest: sha256 ownca_digest: sha256
ownca_create_subject_key_identifier: always_create ownca_create_subject_key_identifier: always_create
@@ -400,7 +463,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem' path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr' csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem' 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 provider: ownca
ownca_digest: sha256 ownca_digest: sha256
ownca_create_authority_key_identifier: yes ownca_create_authority_key_identifier: yes
@@ -413,7 +476,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem' path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr' csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem' 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 provider: ownca
ownca_digest: sha256 ownca_digest: sha256
ownca_create_authority_key_identifier: yes ownca_create_authority_key_identifier: yes
@@ -426,7 +489,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem' path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr' csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem' 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 provider: ownca
ownca_digest: sha256 ownca_digest: sha256
ownca_create_authority_key_identifier: no ownca_create_authority_key_identifier: no
@@ -439,7 +502,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem' path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr' csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem' 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 provider: ownca
ownca_digest: sha256 ownca_digest: sha256
ownca_create_authority_key_identifier: no ownca_create_authority_key_identifier: no
@@ -452,7 +515,7 @@
path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem' path: '{{ remote_tmp_dir }}/ownca_cert_aki.pem'
csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr' csr_path: '{{ remote_tmp_dir }}/csr_ecc.csr'
ownca_path: '{{ remote_tmp_dir }}/ca_cert.pem' 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 provider: ownca
ownca_digest: sha256 ownca_digest: sha256
ownca_create_authority_key_identifier: yes ownca_create_authority_key_identifier: yes

View File

@@ -31,6 +31,14 @@
- ownca_certificate.notBefore == ownca_certificate_idempotence.notBefore - ownca_certificate.notBefore == ownca_certificate_idempotence.notBefore
- ownca_certificate.notAfter == ownca_certificate_idempotence.notAfter - 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 - name: (OwnCA validation, {{select_crypto_backend}}) Read certificate
slurp: slurp:
src: '{{ remote_tmp_dir }}/ownca_cert.pem' src: '{{ remote_tmp_dir }}/ownca_cert.pem'

View File

@@ -8,7 +8,7 @@
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
register: result register: result
- name: Check whether issuer and subject behave as expected - name: Check whether issuer and subject and extensions behave as expected
assert: assert:
that: that:
- result.issuer.organizationalUnitName == 'ACME Department' - result.issuer.organizationalUnitName == 'ACME Department'
@@ -19,6 +19,28 @@
- "['organizationalUnitName', 'ACME Department'] in result.subject_ordered" - "['organizationalUnitName', 'ACME Department'] in result.subject_ordered"
- result.public_key_type == 'RSA' - result.public_key_type == 'RSA'
- result.public_key_data.size == (default_rsa_key_size_certifiates | int) - 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 - name: Check SubjectKeyIdentifier and AuthorityKeyIdentifier
assert: assert:
@@ -27,6 +49,10 @@
- result.authority_key_identifier == "44:55:66:77" - result.authority_key_identifier == "44:55:66:77"
- result.authority_cert_issuer == expected_authority_cert_issuer - result.authority_cert_issuer == expected_authority_cert_issuer
- result.authority_cert_serial_number == 12345 - 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: vars:
expected_authority_cert_issuer: expected_authority_cert_issuer:
- "DNS:ca.example.org" - "DNS:ca.example.org"
@@ -122,10 +148,39 @@
path: '{{ remote_tmp_dir }}/packed-cert-1.pem' path: '{{ remote_tmp_dir }}/packed-cert-1.pem'
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
register: result register: result
- assert: - name: Check extensions
assert:
that: that:
- "'ocsp_uri' in result" - "'ocsp_uri' in result"
- "result.ocsp_uri == 'http://ocsp.int-x3.letsencrypt.org'" - "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 - name: Check fingerprints
assert: assert:
that: that:

View File

@@ -456,3 +456,25 @@
path: '{{ remote_tmp_dir }}/ca-crl2.crl' path: '{{ remote_tmp_dir }}/ca-crl2.crl'
list_revoked_certificates: false list_revoked_certificates: false
register: crl_2_info_1 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

View File

@@ -90,3 +90,11 @@
assert: assert:
that: that:
- "'revoked_certificates' not in crl_2_info_1" - "'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",
]

View File

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

View File

@@ -5,6 +5,6 @@
], ],
"output": "path-line-column-message", "output": "path-line-column-message",
"requirements": [ "requirements": [
"antsibull" "antsibull-docs"
] ]
} }

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# Copyright (c) Ansible Project # Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""Check extra collection docs with antsibull-lint.""" """Check extra collection docs with antsibull-docs."""
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
@@ -14,7 +14,7 @@ def main():
"""Main entry point.""" """Main entry point."""
if not os.path.isdir(os.path.join('docs', 'docsite')): if not os.path.isdir(os.path.join('docs', 'docsite')):
return return
p = subprocess.run(['antsibull-lint', 'collection-docs', '.'], check=False) p = subprocess.run(['antsibull-docs', 'lint-collection-docs', '.'], check=False)
if p.returncode not in (0, 3): if p.returncode not in (0, 3):
print('{0}:0:0: unexpected return code {1}'.format(sys.argv[0], p.returncode)) print('{0}:0:0: unexpected return code {1}'.format(sys.argv[0], p.returncode))

View File

@@ -1,3 +1,9 @@
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
.azure-pipelines/scripts/publish-codecov.py compile-2.6!skip # Uses Python 3.6+ syntax
.azure-pipelines/scripts/publish-codecov.py compile-2.7!skip # Uses Python 3.6+ syntax
.azure-pipelines/scripts/publish-codecov.py compile-3.5!skip # Uses Python 3.6+ syntax
.azure-pipelines/scripts/publish-codecov.py future-import-boilerplate
.azure-pipelines/scripts/publish-codecov.py metaclass-boilerplate
plugins/module_utils/acme/__init__.py empty-init plugins/module_utils/acme/__init__.py empty-init
plugins/module_utils/compat/ipaddress.py future-import-boilerplate plugins/module_utils/compat/ipaddress.py future-import-boilerplate
plugins/module_utils/compat/ipaddress.py metaclass-boilerplate plugins/module_utils/compat/ipaddress.py metaclass-boilerplate

View File

@@ -1,3 +1,9 @@
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
.azure-pipelines/scripts/publish-codecov.py compile-2.6!skip # Uses Python 3.6+ syntax
.azure-pipelines/scripts/publish-codecov.py compile-2.7!skip # Uses Python 3.6+ syntax
.azure-pipelines/scripts/publish-codecov.py compile-3.5!skip # Uses Python 3.6+ syntax
.azure-pipelines/scripts/publish-codecov.py future-import-boilerplate
.azure-pipelines/scripts/publish-codecov.py metaclass-boilerplate
plugins/module_utils/acme/__init__.py empty-init plugins/module_utils/acme/__init__.py empty-init
plugins/module_utils/compat/ipaddress.py future-import-boilerplate plugins/module_utils/compat/ipaddress.py future-import-boilerplate
plugins/module_utils/compat/ipaddress.py metaclass-boilerplate plugins/module_utils/compat/ipaddress.py metaclass-boilerplate

View File

@@ -1,3 +1,4 @@
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
plugins/module_utils/acme/__init__.py empty-init plugins/module_utils/acme/__init__.py empty-init
plugins/module_utils/compat/ipaddress.py future-import-boilerplate plugins/module_utils/compat/ipaddress.py future-import-boilerplate
plugins/module_utils/compat/ipaddress.py metaclass-boilerplate plugins/module_utils/compat/ipaddress.py metaclass-boilerplate

View File

@@ -1,7 +1,7 @@
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
plugins/module_utils/acme/__init__.py empty-init plugins/module_utils/acme/__init__.py empty-init
plugins/module_utils/compat/ipaddress.py future-import-boilerplate plugins/module_utils/compat/ipaddress.py future-import-boilerplate
plugins/module_utils/compat/ipaddress.py metaclass-boilerplate plugins/module_utils/compat/ipaddress.py metaclass-boilerplate
plugins/module_utils/compat/ipaddress.py no-assert plugins/module_utils/compat/ipaddress.py no-assert
plugins/module_utils/compat/ipaddress.py no-unicode-literals plugins/module_utils/compat/ipaddress.py no-unicode-literals
plugins/module_utils/crypto/__init__.py empty-init plugins/module_utils/crypto/__init__.py empty-init
plugins/modules/acme_account_info.py validate-modules:return-syntax-error

View File

@@ -1,3 +1,9 @@
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
.azure-pipelines/scripts/publish-codecov.py compile-2.6!skip # Uses Python 3.6+ syntax
.azure-pipelines/scripts/publish-codecov.py compile-2.7!skip # Uses Python 3.6+ syntax
.azure-pipelines/scripts/publish-codecov.py compile-3.5!skip # Uses Python 3.6+ syntax
.azure-pipelines/scripts/publish-codecov.py future-import-boilerplate
.azure-pipelines/scripts/publish-codecov.py metaclass-boilerplate
plugins/module_utils/acme/__init__.py empty-init plugins/module_utils/acme/__init__.py empty-init
plugins/module_utils/compat/ipaddress.py future-import-boilerplate plugins/module_utils/compat/ipaddress.py future-import-boilerplate
plugins/module_utils/compat/ipaddress.py metaclass-boilerplate plugins/module_utils/compat/ipaddress.py metaclass-boilerplate

View File

@@ -1,4 +1,4 @@
-----BEGIN NEW CERTIFICATE REQUEST----- -----BEGIN CERTIFICATE REQUEST-----
MIIBJTCBzQIBADAWMRQwEgYDVQQDEwthbnNpYmxlLmNvbTBZMBMGByqGSM49AgEG MIIBJTCBzQIBADAWMRQwEgYDVQQDEwthbnNpYmxlLmNvbTBZMBMGByqGSM49AgEG
CCqGSM49AwEHA0IABACc9MgAFwMBJjoU0ZI18cIHnW1juoKG2DN5VrM60uvBvEEs CCqGSM49AwEHA0IABACc9MgAFwMBJjoU0ZI18cIHnW1juoKG2DN5VrM60uvBvEEs
4V0egJkNyM2Q4pp001zu14VcpQ0/Ei8xOOPxKZugVTBTBgkqhkiG9w0BCQ4xRjBE 4V0egJkNyM2Q4pp001zu14VcpQ0/Ei8xOOPxKZugVTBTBgkqhkiG9w0BCQ4xRjBE
@@ -6,4 +6,4 @@ MCMGA1UdEQQcMBqCC2V4YW1wbGUuY29tggtleGFtcGxlLm9yZzAMBgNVHRMBAf8E
AjAAMA8GA1UdDwEB/wQFAwMHgAAwCgYIKoZIzj0EAwIDRwAwRAIgcDyoRmwFVBDl AjAAMA8GA1UdDwEB/wQFAwMHgAAwCgYIKoZIzj0EAwIDRwAwRAIgcDyoRmwFVBDl
FvbFZtiSd5wmJU1ltM6JtcfnLWnjY54CICruOByrropFUkOKKb4xXOYsgaDT93Wr FvbFZtiSd5wmJU1ltM6JtcfnLWnjY54CICruOByrropFUkOKKb4xXOYsgaDT93Wr
URnCJfTLr2T3 URnCJfTLr2T3
-----END NEW CERTIFICATE REQUEST----- -----END CERTIFICATE REQUEST-----

View File

@@ -0,0 +1,12 @@
cryptography 35.0.0 does not support the 'NEW' in there; so to fix tests we removed it.
Once cryptography is fixed we should revert to the old version.
-----BEGIN NEW CERTIFICATE REQUEST-----
MIIBJTCBzQIBADAWMRQwEgYDVQQDEwthbnNpYmxlLmNvbTBZMBMGByqGSM49AgEG
CCqGSM49AwEHA0IABACc9MgAFwMBJjoU0ZI18cIHnW1juoKG2DN5VrM60uvBvEEs
4V0egJkNyM2Q4pp001zu14VcpQ0/Ei8xOOPxKZugVTBTBgkqhkiG9w0BCQ4xRjBE
MCMGA1UdEQQcMBqCC2V4YW1wbGUuY29tggtleGFtcGxlLm9yZzAMBgNVHRMBAf8E
AjAAMA8GA1UdDwEB/wQFAwMHgAAwCgYIKoZIzj0EAwIDRwAwRAIgcDyoRmwFVBDl
FvbFZtiSd5wmJU1ltM6JtcfnLWnjY54CICruOByrropFUkOKKb4xXOYsgaDT93Wr
URnCJfTLr2T3
-----END NEW CERTIFICATE REQUEST-----

View File

@@ -115,12 +115,14 @@ def test_format_error_problem(problem, subproblem_prefix, result):
def create_regular_response(response_text): def create_regular_response(response_text):
response = MagicMock() response = MagicMock()
response.read = MagicMock(return_value=response_text.encode('utf-8')) response.read = MagicMock(return_value=response_text.encode('utf-8'))
response.closed = False
return response return response
def create_error_response(): def create_error_response():
response = MagicMock() response = MagicMock()
response.read = MagicMock(side_effect=AttributeError('read')) response.read = MagicMock(side_effect=AttributeError('read'))
response.closed = True
return response return response

View File

@@ -58,6 +58,9 @@ def test_run_luks_remove(monkeypatch):
monkeypatch.setattr(luks_device.Handler, monkeypatch.setattr(luks_device.Handler,
"_run_command", "_run_command",
run_command_check) run_command_check)
monkeypatch.setattr(luks_device,
"wipe_luks_headers",
lambda device: True)
crypt = luks_device.CryptHandler(module) crypt = luks_device.CryptHandler(module)
crypt.run_luks_remove("dummy") crypt.run_luks_remove("dummy")

View File

@@ -1,11 +1,13 @@
coverage >= 4.2, < 5.0.0, != 4.3.2 ; python_version <= '3.7' # features in 4.2+ required, avoid known bug in 4.3.2 on python 2.6, coverage 5.0+ incompatible coverage >= 4.2, < 5.0.0, != 4.3.2 ; python_version <= '3.7' # features in 4.2+ required, avoid known bug in 4.3.2 on python 2.6, coverage 5.0+ incompatible
coverage >= 4.5.4, < 5.0.0 ; python_version > '3.7' # coverage had a bug in < 4.5.4 that would cause unit tests to hang in Python 3.8, coverage 5.0+ incompatible coverage >= 4.5.4, < 5.0.0 ; python_version > '3.7' # coverage had a bug in < 4.5.4 that would cause unit tests to hang in Python 3.8, coverage 5.0+ incompatible
cryptography < 2.2 ; python_version < '2.7' # cryptography 2.2 drops support for python 2.6 cryptography < 2.2 ; python_version < '2.7' # cryptography 2.2 drops support for python 2.6
cryptography >= 3.0, < 3.4 ; python_version < '3.6' # cryptography 3.4 drops support for python 2.7 cryptography >= 3.0, < 3.4 ; python_version < '3.5' # cryptography 3.4 drops support for python 2.7
cryptography >= 3.0, < 3.3 ; python_version == '3.5' # cryptography 3.3 drops support for python 3.5
urllib3 < 1.24 ; python_version < '2.7' # urllib3 1.24 and later require python 2.7 or later urllib3 < 1.24 ; python_version < '2.7' # urllib3 1.24 and later require python 2.7 or later
idna < 2.6, >= 2.5 # linode requires idna < 2.9, >= 2.5, requests requires idna < 2.6, but cryptography will cause the latest version to be installed instead idna < 2.6, >= 2.5 # linode requires idna < 2.9, >= 2.5, requests requires idna < 2.6, but cryptography will cause the latest version to be installed instead
requests < 2.20.0 ; python_version < '2.7' # requests 2.20.0 drops support for python 2.6 requests < 2.20.0 ; python_version < '2.7' # requests 2.20.0 drops support for python 2.6
virtualenv < 16.0.0 ; python_version < '2.7' # virtualenv 16.0.0 and later require python 2.7 or later virtualenv < 16.0.0 ; python_version < '2.7' # virtualenv 16.0.0 and later require python 2.7 or later
pyopenssl < 18.0.0 ; python_version < '2.7' # pyOpenSSL 18.0.0 and later require python 2.7 or later pyopenssl < 18.0.0 ; python_version < '2.7' # pyOpenSSL 18.0.0 and later require python 2.7 or later
pyopenssl < 22.0.0 ; python_version < '3.6' # pyOpenSSL 22.0.0 and later require python 3.6 or later
setuptools < 45 ; python_version <= '2.7' # setuptools 45 and later require python 3.5 or later setuptools < 45 ; python_version <= '2.7' # setuptools 45 and later require python 3.5 or later
cffi >= 1.14.2, != 1.14.3 # Yanked version which older versions of pip will still install: cffi >= 1.14.2, != 1.14.3 # Yanked version which older versions of pip will still install:

View File

@@ -17,6 +17,12 @@ fi
stage="${S:-prod}" stage="${S:-prod}"
provider="${P:-default}" provider="${P:-default}"
if [ "${platform}/${version}" == "freebsd/13.0" ]; then
# On FreeBSD 13.0, installing PyOpenSSL 22.0.0 tries to upgrade cryptography, which
# will fail due to missing Rust compiler.
echo "pyopenssl < 22.0.0 ; python_version >= '3.8'" >> tests/utils/constraints.txt
fi
# shellcheck disable=SC2086 # shellcheck disable=SC2086
ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
--remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}" --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}"

View File

@@ -49,7 +49,7 @@ function retry
echo "@* -> ${result}" echo "@* -> ${result}"
done done
echo "Command '@*' failed 3 times!" echo "Command '@*' failed 3 times!"
exit -1 exit 255
} }
command -v pip command -v pip
@@ -89,7 +89,7 @@ if [ "${test}" == "sanity/extra" ]; then
fi fi
# START: HACK install integration test dependencies # START: HACK install integration test dependencies
if [ "${test}" == "sanity/extra" ]; then if [ "${script}" != "units" ] && [ "${script}" != "sanity" ] || [ "${test}" == "sanity/extra" ]; then
# Nothing further should be added to this list. # Nothing further should be added to this list.
# This is to prevent modules or plugins in this collection having a runtime dependency on other collections. # This is to prevent modules or plugins in this collection having a runtime dependency on other collections.
retry git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/internal_test_tools" retry git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/internal_test_tools"
@@ -97,7 +97,7 @@ if [ "${test}" == "sanity/extra" ]; then
# retry ansible-galaxy -vvv collection install community.internal_test_tools # retry ansible-galaxy -vvv collection install community.internal_test_tools
fi fi
if [ "${script}" != "units" ] && [ "${script}" != "sanity" ] && [ "${ansible_version}" != "2.9" ]; then if [ "${script}" != "units" ] && [ "${script}" != "sanity" ] && [ "${test}" != "sanity/extra" ] && [ "${ansible_version}" != "2.9" ]; then
retry git clone --depth=1 --single-branch https://github.com/ansible-collections/community.general.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/general" retry git clone --depth=1 --single-branch https://github.com/ansible-collections/community.general.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/general"
# NOTE: we're installing with git to work around Galaxy being a huge PITA (https://github.com/ansible/galaxy/issues/2429) # NOTE: we're installing with git to work around Galaxy being a huge PITA (https://github.com/ansible/galaxy/issues/2429)
# retry ansible-galaxy -vvv collection install community.general # retry ansible-galaxy -vvv collection install community.general