mirror of
https://github.com/ansible-collections/community.crypto.git
synced 2026-05-06 13:22:58 +00:00
Bump version to 3.0.0-dev0, remove deprecated functionality and implement announced breaking changes (#873)
* Bump verison to 3.0.0-dev0. * Change check mode behavior for *_pipe modules. * Remove PyOpenSSL backend. * Remove PyOpenSSL setup. * Change default of asn1_base64. * Remove deprecated common module utils. * Remove get_default_argspec(). * Mark two methods as abstract. * Remove ACME v1 support. * Remove retrieve_acme_v1_certificate(). * Remove deprecated docs fragment. * Change meaning of mode parameter. * Mark no longer used option as 'to deprecate'.
This commit is contained in:
@@ -102,13 +102,13 @@ options:
|
||||
description:
|
||||
- URI to a terms of service document you agree to when using the ACME v1 service at O(acme_directory).
|
||||
- Default is latest gathered from O(acme_directory) URL.
|
||||
- This option will only be used when O(acme_version) is 1.
|
||||
- This option has no longer any effect.
|
||||
# TODO: deprecate!
|
||||
type: str
|
||||
terms_agreed:
|
||||
description:
|
||||
- Boolean indicating whether you agree to the terms of service document.
|
||||
- ACME servers can require this to be true.
|
||||
- This option will only be used when O(acme_version) is not 1.
|
||||
type: bool
|
||||
default: false
|
||||
modify_account:
|
||||
@@ -433,7 +433,6 @@ EXAMPLES = r"""
|
||||
data: "{{ sample_com_challenge }}"
|
||||
# We use Let's Encrypt's ACME v2 endpoint
|
||||
acme_directory: https://acme-v02.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
# The following makes sure that if a chain with /CN=DST Root CA X3 in its issuer is provided
|
||||
# as an alternative, it will be selected. These are the roots cross-signed by IdenTrust.
|
||||
# As long as Let's Encrypt provides alternate chains with the cross-signed root(s) when
|
||||
@@ -580,10 +579,8 @@ from ansible_collections.community.crypto.plugins.module_utils.acme.acme import
|
||||
from ansible_collections.community.crypto.plugins.module_utils.acme.certificates import (
|
||||
CertificateChain,
|
||||
Criterium,
|
||||
retrieve_acme_v1_certificate,
|
||||
)
|
||||
from ansible_collections.community.crypto.plugins.module_utils.acme.challenges import (
|
||||
Authorization,
|
||||
combine_identifier,
|
||||
normalize_combined_identifier,
|
||||
split_identifier,
|
||||
@@ -669,33 +666,21 @@ class ACMECertificateClient(object):
|
||||
|
||||
# Make sure account exists
|
||||
modify_account = module.params["modify_account"]
|
||||
if modify_account or self.version > 1:
|
||||
contact = []
|
||||
if module.params["account_email"]:
|
||||
contact.append("mailto:" + module.params["account_email"])
|
||||
created, account_data = self.account.setup_account(
|
||||
contact,
|
||||
agreement=module.params.get("agreement"),
|
||||
terms_agreed=module.params.get("terms_agreed"),
|
||||
allow_creation=modify_account,
|
||||
)
|
||||
if account_data is None:
|
||||
raise ModuleFailException(
|
||||
msg="Account does not exist or is deactivated."
|
||||
)
|
||||
updated = False
|
||||
if not created and account_data and modify_account:
|
||||
updated, account_data = self.account.update_account(
|
||||
account_data, contact
|
||||
)
|
||||
self.changed = created or updated
|
||||
else:
|
||||
# This happens if modify_account is False and the ACME v1
|
||||
# protocol is used. In this case, we do not call setup_account()
|
||||
# to avoid accidental creation of an account. This is OK
|
||||
# since for ACME v1, the account URI is not needed to send a
|
||||
# signed ACME request.
|
||||
pass
|
||||
contact = []
|
||||
if module.params["account_email"]:
|
||||
contact.append("mailto:" + module.params["account_email"])
|
||||
created, account_data = self.account.setup_account(
|
||||
contact,
|
||||
agreement=module.params.get("agreement"),
|
||||
terms_agreed=module.params.get("terms_agreed"),
|
||||
allow_creation=modify_account,
|
||||
)
|
||||
if account_data is None:
|
||||
raise ModuleFailException(msg="Account does not exist or is deactivated.")
|
||||
updated = False
|
||||
if not created and account_data and modify_account:
|
||||
updated, account_data = self.account.update_account(account_data, contact)
|
||||
self.changed = created or updated
|
||||
|
||||
if self.csr is not None and not os.path.exists(self.csr):
|
||||
raise ModuleFailException("CSR %s not found" % (self.csr))
|
||||
@@ -712,13 +697,9 @@ class ACMECertificateClient(object):
|
||||
"""
|
||||
if self.data is None:
|
||||
return True
|
||||
if self.version == 1:
|
||||
# As soon as self.data is a non-empty object, we are in the second stage.
|
||||
return not self.data
|
||||
else:
|
||||
# We are in the second stage if data.order_uri is given (which has been
|
||||
# stored in self.order_uri by the constructor).
|
||||
return self.order_uri is None
|
||||
# We are in the second stage if data.order_uri is given (which has been
|
||||
# stored in self.order_uri by the constructor).
|
||||
return self.order_uri is None
|
||||
|
||||
def _get_cert_info_or_none(self):
|
||||
if self.module.params.get("dest"):
|
||||
@@ -735,40 +716,30 @@ class ACMECertificateClient(object):
|
||||
respectively start a new order for ACME v2.
|
||||
"""
|
||||
self.authorizations = {}
|
||||
if self.version == 1:
|
||||
for identifier_type, identifier in self.identifiers:
|
||||
if identifier_type != "dns":
|
||||
raise ModuleFailException("ACME v1 only supports DNS identifiers!")
|
||||
for identifier_type, identifier in self.identifiers:
|
||||
authz = Authorization.create(self.client, identifier_type, identifier)
|
||||
self.authorizations[
|
||||
normalize_combined_identifier(authz.combined_identifier)
|
||||
] = authz
|
||||
else:
|
||||
replaces_cert_id = None
|
||||
if self.include_renewal_cert_id == "always" or (
|
||||
self.include_renewal_cert_id == "when_ari_supported"
|
||||
and self.client.directory.has_renewal_info_endpoint()
|
||||
):
|
||||
cert_info = self._get_cert_info_or_none()
|
||||
if cert_info is not None:
|
||||
replaces_cert_id = compute_cert_id(
|
||||
self.client.backend,
|
||||
cert_info=cert_info,
|
||||
none_if_required_information_is_missing=True,
|
||||
)
|
||||
self.order = Order.create_with_error_handling(
|
||||
self.client,
|
||||
self.identifiers,
|
||||
error_strategy=self.order_creation_error_strategy,
|
||||
error_max_retries=self.order_creation_max_retries,
|
||||
replaces_cert_id=replaces_cert_id,
|
||||
profile=self.profile,
|
||||
message_callback=self.module.warn,
|
||||
)
|
||||
self.order_uri = self.order.url
|
||||
self.order.load_authorizations(self.client)
|
||||
self.authorizations.update(self.order.authorizations)
|
||||
replaces_cert_id = None
|
||||
if self.include_renewal_cert_id == "always" or (
|
||||
self.include_renewal_cert_id == "when_ari_supported"
|
||||
and self.client.directory.has_renewal_info_endpoint()
|
||||
):
|
||||
cert_info = self._get_cert_info_or_none()
|
||||
if cert_info is not None:
|
||||
replaces_cert_id = compute_cert_id(
|
||||
self.client.backend,
|
||||
cert_info=cert_info,
|
||||
none_if_required_information_is_missing=True,
|
||||
)
|
||||
self.order = Order.create_with_error_handling(
|
||||
self.client,
|
||||
self.identifiers,
|
||||
error_strategy=self.order_creation_error_strategy,
|
||||
error_max_retries=self.order_creation_max_retries,
|
||||
replaces_cert_id=replaces_cert_id,
|
||||
profile=self.profile,
|
||||
message_callback=self.module.warn,
|
||||
)
|
||||
self.order_uri = self.order.url
|
||||
self.order.load_authorizations(self.client)
|
||||
self.authorizations.update(self.order.authorizations)
|
||||
self.changed = True
|
||||
|
||||
def get_challenges_data(self, first_step):
|
||||
@@ -815,20 +786,11 @@ class ACMECertificateClient(object):
|
||||
self.authorizations = {}
|
||||
|
||||
# Step 1: obtain challenge information
|
||||
if self.version == 1:
|
||||
# For ACME v1, we attempt to create new authzs. Existing ones
|
||||
# will be returned instead.
|
||||
for identifier_type, identifier in self.identifiers:
|
||||
authz = Authorization.create(self.client, identifier_type, identifier)
|
||||
self.authorizations[combine_identifier(identifier_type, identifier)] = (
|
||||
authz
|
||||
)
|
||||
else:
|
||||
# For ACME v2, we obtain the order object by fetching the
|
||||
# order URI, and extract the information from there.
|
||||
self.order = Order.from_url(self.client, self.order_uri)
|
||||
self.order.load_authorizations(self.client)
|
||||
self.authorizations.update(self.order.authorizations)
|
||||
# For ACME v2, we obtain the order object by fetching the
|
||||
# order URI, and extract the information from there.
|
||||
self.order = Order.from_url(self.client, self.order_uri)
|
||||
self.order.load_authorizations(self.client)
|
||||
self.authorizations.update(self.order.authorizations)
|
||||
|
||||
# Step 2: validate pending challenges
|
||||
authzs_to_wait_for = []
|
||||
@@ -897,33 +859,25 @@ class ACMECertificateClient(object):
|
||||
module=self.module,
|
||||
)
|
||||
|
||||
if self.version == 1:
|
||||
cert = retrieve_acme_v1_certificate(
|
||||
self.client, pem_to_der(self.csr, self.csr_content)
|
||||
)
|
||||
else:
|
||||
self.order.finalize(self.client, pem_to_der(self.csr, self.csr_content))
|
||||
cert = CertificateChain.download(self.client, self.order.certificate_uri)
|
||||
if (
|
||||
self.module.params["retrieve_all_alternates"]
|
||||
or self.select_chain_matcher
|
||||
):
|
||||
# Retrieve alternate chains
|
||||
alternate_chains = self.download_alternate_chains(cert)
|
||||
self.order.finalize(self.client, pem_to_der(self.csr, self.csr_content))
|
||||
cert = CertificateChain.download(self.client, self.order.certificate_uri)
|
||||
if self.module.params["retrieve_all_alternates"] or self.select_chain_matcher:
|
||||
# Retrieve alternate chains
|
||||
alternate_chains = self.download_alternate_chains(cert)
|
||||
|
||||
# Prepare return value for all alternate chains
|
||||
if self.module.params["retrieve_all_alternates"]:
|
||||
self.all_chains = [cert.to_json()]
|
||||
for alt_chain in alternate_chains:
|
||||
self.all_chains.append(alt_chain.to_json())
|
||||
# Prepare return value for all alternate chains
|
||||
if self.module.params["retrieve_all_alternates"]:
|
||||
self.all_chains = [cert.to_json()]
|
||||
for alt_chain in alternate_chains:
|
||||
self.all_chains.append(alt_chain.to_json())
|
||||
|
||||
# Try to select alternate chain depending on criteria
|
||||
if self.select_chain_matcher:
|
||||
matching_chain = self.find_matching_chain([cert] + alternate_chains)
|
||||
if matching_chain:
|
||||
cert = matching_chain
|
||||
else:
|
||||
self.module.debug("Found no matching alternative chain")
|
||||
# Try to select alternate chain depending on criteria
|
||||
if self.select_chain_matcher:
|
||||
matching_chain = self.find_matching_chain([cert] + alternate_chains)
|
||||
if matching_chain:
|
||||
cert = matching_chain
|
||||
else:
|
||||
self.module.debug("Found no matching alternative chain")
|
||||
|
||||
if cert.cert is not None:
|
||||
pem_cert = cert.cert
|
||||
|
||||
@@ -76,8 +76,6 @@ def main():
|
||||
order_uri=dict(type="str", required=True),
|
||||
)
|
||||
module = argument_spec.create_ansible_module(supports_check_mode=True)
|
||||
if module.params["acme_version"] == 1:
|
||||
module.fail_json("The module does not support acme_version=1")
|
||||
|
||||
backend = create_backend(module, False)
|
||||
|
||||
|
||||
@@ -215,7 +215,6 @@ EXAMPLES = r"""
|
||||
- name: Create a challenge for sample.com using a account key file.
|
||||
community.crypto.acme_certificate_order_create:
|
||||
acme_directory: https://acme-v01.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
csr: /etc/pki/cert/csr/sample.com.csr
|
||||
register: sample_com_challenge
|
||||
@@ -238,7 +237,6 @@ EXAMPLES = r"""
|
||||
- name: Let the challenge be validated
|
||||
community.crypto.acme_certificate_order_validate:
|
||||
acme_directory: https://acme-v01.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
order_uri: "{{ sample_com_challenge.order_uri }}"
|
||||
challenge: dns-01
|
||||
@@ -246,7 +244,6 @@ EXAMPLES = r"""
|
||||
- name: Retrieve the cert and intermediate certificate
|
||||
community.crypto.acme_certificate_order_finalize:
|
||||
acme_directory: https://acme-v01.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
csr: /etc/pki/cert/csr/sample.com.csr
|
||||
order_uri: "{{ sample_com_challenge.order_uri }}"
|
||||
@@ -405,8 +402,6 @@ def main():
|
||||
order_creation_max_retries=dict(type="int", default=3),
|
||||
)
|
||||
module = argument_spec.create_ansible_module()
|
||||
if module.params["acme_version"] == 1:
|
||||
module.fail_json("The module does not support acme_version=1")
|
||||
|
||||
backend = create_backend(module, False)
|
||||
|
||||
|
||||
@@ -227,7 +227,6 @@ EXAMPLES = r"""
|
||||
- name: Create a challenge for sample.com using a account key file.
|
||||
community.crypto.acme_certificate_order_create:
|
||||
acme_directory: https://acme-v01.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
csr: /etc/pki/cert/csr/sample.com.csr
|
||||
register: sample_com_challenge
|
||||
@@ -250,7 +249,6 @@ EXAMPLES = r"""
|
||||
- name: Let the challenge be validated
|
||||
community.crypto.acme_certificate_order_validate:
|
||||
acme_directory: https://acme-v01.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
order_uri: "{{ sample_com_challenge.order_uri }}"
|
||||
challenge: dns-01
|
||||
@@ -258,7 +256,6 @@ EXAMPLES = r"""
|
||||
- name: Retrieve the cert and intermediate certificate
|
||||
community.crypto.acme_certificate_order_finalize:
|
||||
acme_directory: https://acme-v01.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
csr: /etc/pki/cert/csr/sample.com.csr
|
||||
order_uri: "{{ sample_com_challenge.order_uri }}"
|
||||
@@ -365,8 +362,6 @@ def main():
|
||||
),
|
||||
)
|
||||
module = argument_spec.create_ansible_module()
|
||||
if module.params["acme_version"] == 1:
|
||||
module.fail_json("The module does not support acme_version=1")
|
||||
|
||||
backend = create_backend(module, False)
|
||||
|
||||
|
||||
@@ -380,8 +380,6 @@ def main():
|
||||
order_uri=dict(type="str", required=True),
|
||||
)
|
||||
module = argument_spec.create_ansible_module(supports_check_mode=True)
|
||||
if module.params["acme_version"] == 1:
|
||||
module.fail_json("The module does not support acme_version=1")
|
||||
|
||||
backend = create_backend(module, False)
|
||||
|
||||
|
||||
@@ -152,7 +152,6 @@ EXAMPLES = r"""
|
||||
- name: Create a challenge for sample.com using a account key file.
|
||||
community.crypto.acme_certificate_order_create:
|
||||
acme_directory: https://acme-v01.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
csr: /etc/pki/cert/csr/sample.com.csr
|
||||
register: sample_com_challenge
|
||||
@@ -175,7 +174,6 @@ EXAMPLES = r"""
|
||||
- name: Let the challenge be validated
|
||||
community.crypto.acme_certificate_order_validate:
|
||||
acme_directory: https://acme-v01.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
order_uri: "{{ sample_com_challenge.order_uri }}"
|
||||
challenge: dns-01
|
||||
@@ -183,7 +181,6 @@ EXAMPLES = r"""
|
||||
- name: Retrieve the cert and intermediate certificate
|
||||
community.crypto.acme_certificate_order_finalize:
|
||||
acme_directory: https://acme-v01.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
csr: /etc/pki/cert/csr/sample.com.csr
|
||||
order_uri: "{{ sample_com_challenge.order_uri }}"
|
||||
@@ -257,8 +254,6 @@ def main():
|
||||
deactivate_authzs=dict(type="bool", default=True),
|
||||
)
|
||||
module = argument_spec.create_ansible_module()
|
||||
if module.params["acme_version"] == 1:
|
||||
module.fail_json("The module does not support acme_version=1")
|
||||
|
||||
backend = create_backend(module, False)
|
||||
|
||||
|
||||
@@ -174,12 +174,7 @@ def main():
|
||||
payload = {"certificate": certificate}
|
||||
if module.params.get("revoke_reason") is not None:
|
||||
payload["reason"] = module.params.get("revoke_reason")
|
||||
# Determine endpoint
|
||||
if module.params.get("acme_version") == 1:
|
||||
endpoint = client.directory["revoke-cert"]
|
||||
payload["resource"] = "revoke-cert"
|
||||
else:
|
||||
endpoint = client.directory["revokeCert"]
|
||||
endpoint = client.directory["revokeCert"]
|
||||
# Get hold of private key (if available) and make sure it comes from disk
|
||||
private_key = module.params.get("private_key_src")
|
||||
private_key_content = module.params.get("private_key_content")
|
||||
@@ -227,12 +222,8 @@ def main():
|
||||
already_revoked = True
|
||||
else:
|
||||
# Hack for Boulder errors
|
||||
if module.params.get("acme_version") == 1:
|
||||
error_type = "urn:acme:error:malformed"
|
||||
else:
|
||||
error_type = "urn:ietf:params:acme:error:malformed"
|
||||
if (
|
||||
result.get("type") == error_type
|
||||
result.get("type") == "urn:ietf:params:acme:error:malformed"
|
||||
and result.get("detail") == "Certificate already revoked"
|
||||
):
|
||||
# Fallback: boulder returns this in case the certificate was already revoked.
|
||||
|
||||
@@ -27,7 +27,7 @@ notes:
|
||||
- "Using the C(ansible) tool, M(community.crypto.acme_inspect) can be used to directly execute ACME requests without the
|
||||
need of writing a playbook. For example, the following command retrieves the ACME account with ID 1 from Let's Encrypt
|
||||
(assuming C(/path/to/key) is the correct private account key): C(ansible localhost -m acme_inspect -a \"account_key_src=/path/to/key
|
||||
acme_directory=https://acme-v02.api.letsencrypt.org/directory acme_version=2 account_uri=https://acme-v02.api.letsencrypt.org/acme/acct/1
|
||||
acme_directory=https://acme-v02.api.letsencrypt.org/directory account_uri=https://acme-v02.api.letsencrypt.org/acme/acct/1
|
||||
method=get url=https://acme-v02.api.letsencrypt.org/acme/acct/1\")."
|
||||
seealso:
|
||||
- name: Automatic Certificate Management Environment (ACME)
|
||||
@@ -83,14 +83,12 @@ EXAMPLES = r"""
|
||||
- name: Get directory
|
||||
community.crypto.acme_inspect:
|
||||
acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
method: directory-only
|
||||
register: directory
|
||||
|
||||
- name: Create an account
|
||||
community.crypto.acme_inspect:
|
||||
acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
url: "{{ directory.newAccount}}"
|
||||
method: post
|
||||
@@ -102,7 +100,6 @@ EXAMPLES = r"""
|
||||
- name: Get account information
|
||||
community.crypto.acme_inspect:
|
||||
acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
account_uri: "{{ account_creation.headers.location }}"
|
||||
url: "{{ account_creation.headers.location }}"
|
||||
@@ -111,7 +108,6 @@ EXAMPLES = r"""
|
||||
- name: Update account contacts
|
||||
community.crypto.acme_inspect:
|
||||
acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
account_uri: "{{ account_creation.headers.location }}"
|
||||
url: "{{ account_creation.headers.location }}"
|
||||
@@ -127,7 +123,6 @@ EXAMPLES = r"""
|
||||
- name: Create certificate order
|
||||
community.crypto.acme_certificate:
|
||||
acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
account_uri: "{{ account_creation.headers.location }}"
|
||||
csr: /etc/pki/cert/csr/sample.com.csr
|
||||
@@ -141,7 +136,6 @@ EXAMPLES = r"""
|
||||
- name: Get order information
|
||||
community.crypto.acme_inspect:
|
||||
acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
account_uri: "{{ account_creation.headers.location }}"
|
||||
url: "{{ certificate_request.order_uri }}"
|
||||
@@ -151,7 +145,6 @@ EXAMPLES = r"""
|
||||
- name: Get first authz for order
|
||||
community.crypto.acme_inspect:
|
||||
acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
account_uri: "{{ account_creation.headers.location }}"
|
||||
url: "{{ order.output_json.authorizations[0] }}"
|
||||
@@ -161,7 +154,6 @@ EXAMPLES = r"""
|
||||
- name: Get HTTP-01 challenge for authz
|
||||
community.crypto.acme_inspect:
|
||||
acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
account_uri: "{{ account_creation.headers.location }}"
|
||||
url: "{{ authz.output_json.challenges | selectattr('type', 'equalto', 'http-01') }}"
|
||||
@@ -171,7 +163,6 @@ EXAMPLES = r"""
|
||||
- name: Activate HTTP-01 challenge manually
|
||||
community.crypto.acme_inspect:
|
||||
acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
acme_version: 2
|
||||
account_key_src: /etc/pki/cert/private/account.key
|
||||
account_uri: "{{ account_creation.headers.location }}"
|
||||
url: "{{ http01challenge.url }}"
|
||||
|
||||
@@ -100,8 +100,9 @@ options:
|
||||
- Whether to encode the ASN.1 values in the RV(extensions) return value with Base64 or not.
|
||||
- The documentation claimed for a long time that the values are Base64 encoded, but they never were. For compatibility
|
||||
this option is set to V(false).
|
||||
- The default value V(false) is B(deprecated) and will change to V(true) in community.crypto 3.0.0.
|
||||
- The default value was changed from V(false) to V(true) incommunity.crypto 3.0.0.
|
||||
type: bool
|
||||
default: true
|
||||
version_added: 2.12.0
|
||||
tls_ctx_options:
|
||||
description:
|
||||
@@ -351,7 +352,7 @@ def main():
|
||||
),
|
||||
starttls=dict(type="str", choices=["mysql"]),
|
||||
ciphers=dict(type="list", elements="str"),
|
||||
asn1_base64=dict(type="bool"),
|
||||
asn1_base64=dict(type="bool", default=True),
|
||||
tls_ctx_options=dict(type="list", elements="raw"),
|
||||
get_certificate_chain=dict(type="bool", default=False),
|
||||
),
|
||||
@@ -370,16 +371,6 @@ def main():
|
||||
tls_ctx_options = module.params["tls_ctx_options"]
|
||||
get_certificate_chain = module.params["get_certificate_chain"]
|
||||
|
||||
if asn1_base64 is None:
|
||||
module.deprecate(
|
||||
"The default value `false` for asn1_base64 is deprecated and will change to `true` in "
|
||||
"community.crypto 3.0.0. If you need this value, it is best to set the value explicitly "
|
||||
"and adjust your roles/playbooks to use `asn1_base64=true` as soon as possible",
|
||||
version="3.0.0",
|
||||
collection_name="community.crypto",
|
||||
)
|
||||
asn1_base64 = False
|
||||
|
||||
if get_certificate_chain and sys.version_info < (3, 10):
|
||||
module.fail_json(
|
||||
msg="get_certificate_chain=true can only be used with Python 3.10 (Python 3.13+ officially supports this). "
|
||||
|
||||
@@ -29,10 +29,7 @@ attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
details:
|
||||
- Currently in check mode, private keys will not be (re-)generated, only the changed status is set. This will change
|
||||
in community.crypto 3.0.0.
|
||||
- From community.crypto 3.0.0 on, the module will ignore check mode and always behave as if check mode is not active.
|
||||
If you think this breaks your use-case of this module, please create an issue in the community.crypto repository.
|
||||
- Since community.crypto 3.0.0, the module ignores check mode and always behaves as if check mode is not active.
|
||||
options:
|
||||
content:
|
||||
description:
|
||||
@@ -157,18 +154,7 @@ class CertificateSigningRequestModule(object):
|
||||
def generate(self, module):
|
||||
"""Generate the certificate signing request."""
|
||||
if self.module_backend.needs_regeneration():
|
||||
if not self.check_mode:
|
||||
self.module_backend.generate_csr()
|
||||
else:
|
||||
self.module.deprecate(
|
||||
"Check mode support for openssl_csr_pipe will change in community.crypto 3.0.0"
|
||||
" to behave the same as without check mode. You can get that behavior right now"
|
||||
" by adding `check_mode: false` to the openssl_csr_pipe task. If you think this"
|
||||
" breaks your use-case of this module, please create an issue in the"
|
||||
" community.crypto repository",
|
||||
version="3.0.0",
|
||||
collection_name="community.crypto",
|
||||
)
|
||||
self.module_backend.generate_csr()
|
||||
self.changed = True
|
||||
|
||||
def dump(self):
|
||||
|
||||
@@ -18,11 +18,9 @@ author:
|
||||
short_description: Generate OpenSSL PKCS#12 archive
|
||||
description:
|
||||
- This module allows one to (re-)generate PKCS#12.
|
||||
- The module can use the cryptography Python library, or the pyOpenSSL Python library. By default, it tries to detect which
|
||||
one is available, assuming none of the O(iter_size) and O(maciter_size) options are used. This can be overridden with
|
||||
the O(select_crypto_backend) option.
|
||||
- The module uses the cryptography Python library.
|
||||
requirements:
|
||||
- PyOpenSSL >= 0.15, < 23.3.0 or cryptography >= 3.0
|
||||
- cryptography >= 3.0
|
||||
extends_documentation_fragment:
|
||||
- ansible.builtin.files
|
||||
- community.crypto.attributes
|
||||
@@ -95,15 +93,16 @@ options:
|
||||
description:
|
||||
- Number of times to repeat the encryption step.
|
||||
- This is B(not considered during idempotency checks).
|
||||
- This is only used by the C(pyopenssl) backend, or when O(encryption_level=compatibility2022).
|
||||
- When using it, the default is V(2048) for C(pyopenssl) and V(50000) for C(cryptography).
|
||||
- This is only used when O(encryption_level=compatibility2022).
|
||||
- When using it, the default is V(50000).
|
||||
type: int
|
||||
maciter_size:
|
||||
description:
|
||||
- Number of times to repeat the MAC step.
|
||||
- This is B(not considered during idempotency checks).
|
||||
- This is only used by the C(pyopenssl) backend. When using it, the default is V(1).
|
||||
- This value is B(not used).
|
||||
type: int
|
||||
# TODO: deprecate!
|
||||
encryption_level:
|
||||
description:
|
||||
- Determines the encryption level used.
|
||||
@@ -170,15 +169,12 @@ options:
|
||||
select_crypto_backend:
|
||||
description:
|
||||
- Determines which crypto backend to use.
|
||||
- The default choice is V(auto), which tries to use C(cryptography) if available, and falls back to C(pyopenssl). If
|
||||
O(iter_size) is used together with O(encryption_level) is not V(compatibility2022), or if O(maciter_size) is used,
|
||||
V(auto) will always result in C(pyopenssl) to be chosen for backwards compatibility.
|
||||
- If set to V(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) library.
|
||||
- The default choice is V(auto), which tries to use C(cryptography) if available.
|
||||
- If set to V(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
|
||||
- B(Note) that the V(pyopenssl) backend is deprecated and will be removed from community.crypto 3.0.0.
|
||||
- The value V(pyopenssl) has been removed for community.crypto 3.0.0.
|
||||
type: str
|
||||
default: auto
|
||||
choices: [auto, cryptography, pyopenssl]
|
||||
choices: [auto, cryptography]
|
||||
version_added: 1.7.0
|
||||
seealso:
|
||||
- module: community.crypto.x509_certificate
|
||||
@@ -315,23 +311,6 @@ from ansible_collections.community.crypto.plugins.module_utils.version import (
|
||||
|
||||
|
||||
MINIMAL_CRYPTOGRAPHY_VERSION = "3.0"
|
||||
MINIMAL_PYOPENSSL_VERSION = "0.15"
|
||||
MAXIMAL_PYOPENSSL_VERSION = "23.3.0"
|
||||
|
||||
PYOPENSSL_IMP_ERR = None
|
||||
try:
|
||||
import OpenSSL
|
||||
from OpenSSL import crypto
|
||||
from OpenSSL.crypto import (
|
||||
load_pkcs12 as _load_pkcs12, # this got removed in pyOpenSSL 23.3.0
|
||||
)
|
||||
|
||||
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
|
||||
except (ImportError, AttributeError):
|
||||
PYOPENSSL_IMP_ERR = traceback.format_exc()
|
||||
PYOPENSSL_FOUND = False
|
||||
else:
|
||||
PYOPENSSL_FOUND = True
|
||||
|
||||
CRYPTOGRAPHY_IMP_ERR = None
|
||||
try:
|
||||
@@ -628,88 +607,6 @@ class Pkcs(OpenSSLObject):
|
||||
self.pkcs12_bytes = content
|
||||
|
||||
|
||||
class PkcsPyOpenSSL(Pkcs):
|
||||
def __init__(self, module):
|
||||
super(PkcsPyOpenSSL, self).__init__(module, "pyopenssl")
|
||||
if self.encryption_level != "auto":
|
||||
module.fail_json(
|
||||
msg="The PyOpenSSL backend only supports encryption_level = auto"
|
||||
)
|
||||
|
||||
def generate_bytes(self, module):
|
||||
"""Generate PKCS#12 file archive."""
|
||||
self.pkcs12 = crypto.PKCS12()
|
||||
|
||||
if self.other_certificates:
|
||||
self.pkcs12.set_ca_certificates(self.other_certificates)
|
||||
|
||||
if self.certificate_content:
|
||||
self.pkcs12.set_certificate(
|
||||
load_certificate(
|
||||
None, content=self.certificate_content, backend=self.backend
|
||||
)
|
||||
)
|
||||
|
||||
if self.friendly_name:
|
||||
self.pkcs12.set_friendlyname(to_bytes(self.friendly_name))
|
||||
|
||||
if self.privatekey_content:
|
||||
try:
|
||||
self.pkcs12.set_privatekey(
|
||||
load_privatekey(
|
||||
None,
|
||||
content=self.privatekey_content,
|
||||
passphrase=self.privatekey_passphrase,
|
||||
backend=self.backend,
|
||||
)
|
||||
)
|
||||
except OpenSSLBadPassphraseError as exc:
|
||||
raise PkcsError(exc)
|
||||
|
||||
return self.pkcs12.export(self.passphrase, self.iter_size, self.maciter_size)
|
||||
|
||||
def parse_bytes(self, pkcs12_content):
|
||||
try:
|
||||
p12 = crypto.load_pkcs12(pkcs12_content, self.passphrase)
|
||||
pkey = p12.get_privatekey()
|
||||
if pkey is not None:
|
||||
pkey = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey)
|
||||
crt = p12.get_certificate()
|
||||
if crt is not None:
|
||||
crt = crypto.dump_certificate(crypto.FILETYPE_PEM, crt)
|
||||
other_certs = []
|
||||
if p12.get_ca_certificates() is not None:
|
||||
other_certs = [
|
||||
crypto.dump_certificate(crypto.FILETYPE_PEM, other_cert)
|
||||
for other_cert in p12.get_ca_certificates()
|
||||
]
|
||||
|
||||
friendly_name = p12.get_friendlyname()
|
||||
|
||||
return (pkey, crt, other_certs, friendly_name)
|
||||
except crypto.Error as exc:
|
||||
raise PkcsError(exc)
|
||||
|
||||
def _dump_privatekey(self, pkcs12):
|
||||
pk = pkcs12.get_privatekey()
|
||||
return crypto.dump_privatekey(crypto.FILETYPE_PEM, pk) if pk else None
|
||||
|
||||
def _dump_certificate(self, pkcs12):
|
||||
cert = pkcs12.get_certificate()
|
||||
return crypto.dump_certificate(crypto.FILETYPE_PEM, cert) if cert else None
|
||||
|
||||
def _dump_other_certificates(self, pkcs12):
|
||||
if pkcs12.get_ca_certificates() is None:
|
||||
return []
|
||||
return [
|
||||
crypto.dump_certificate(crypto.FILETYPE_PEM, other_cert)
|
||||
for other_cert in pkcs12.get_ca_certificates()
|
||||
]
|
||||
|
||||
def _get_friendly_name(self, pkcs12):
|
||||
return pkcs12.get_friendlyname()
|
||||
|
||||
|
||||
class PkcsCryptography(Pkcs):
|
||||
def __init__(self, module):
|
||||
super(PkcsCryptography, self).__init__(
|
||||
@@ -839,52 +736,20 @@ def select_backend(module, backend):
|
||||
CRYPTOGRAPHY_FOUND
|
||||
and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
|
||||
)
|
||||
can_use_pyopenssl = (
|
||||
PYOPENSSL_FOUND
|
||||
and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
|
||||
and PYOPENSSL_VERSION < LooseVersion(MAXIMAL_PYOPENSSL_VERSION)
|
||||
)
|
||||
|
||||
# If no restrictions are provided, first try cryptography, then pyOpenSSL
|
||||
if (
|
||||
module.params["iter_size"] is not None
|
||||
and module.params["encryption_level"] != "compatibility2022"
|
||||
) or module.params["maciter_size"] is not None:
|
||||
# If iter_size (for encryption_level != compatibility2022) or maciter_size is specified, use pyOpenSSL backend
|
||||
backend = "pyopenssl"
|
||||
elif can_use_cryptography:
|
||||
if can_use_cryptography:
|
||||
backend = "cryptography"
|
||||
elif can_use_pyopenssl:
|
||||
backend = "pyopenssl"
|
||||
|
||||
# Success?
|
||||
if backend == "auto":
|
||||
module.fail_json(
|
||||
msg=(
|
||||
"Cannot detect any of the required Python libraries "
|
||||
"cryptography (>= {0}) or PyOpenSSL (>= {1}, < {2})"
|
||||
"Cannot detect the required Python library cryptography (>= {0})"
|
||||
).format(
|
||||
MINIMAL_CRYPTOGRAPHY_VERSION,
|
||||
MINIMAL_PYOPENSSL_VERSION,
|
||||
MAXIMAL_PYOPENSSL_VERSION,
|
||||
)
|
||||
)
|
||||
|
||||
if backend == "pyopenssl":
|
||||
if not PYOPENSSL_FOUND:
|
||||
msg = missing_required_lib(
|
||||
"pyOpenSSL >= {0}, < {1}".format(
|
||||
MINIMAL_PYOPENSSL_VERSION, MAXIMAL_PYOPENSSL_VERSION
|
||||
)
|
||||
)
|
||||
module.fail_json(msg=msg, exception=PYOPENSSL_IMP_ERR)
|
||||
module.deprecate(
|
||||
"The module is using the PyOpenSSL backend. This backend has been deprecated",
|
||||
version="3.0.0",
|
||||
collection_name="community.crypto",
|
||||
)
|
||||
return backend, PkcsPyOpenSSL(module)
|
||||
elif backend == "cryptography":
|
||||
if backend == "cryptography":
|
||||
if not CRYPTOGRAPHY_FOUND:
|
||||
module.fail_json(
|
||||
msg=missing_required_lib(
|
||||
@@ -924,7 +789,7 @@ def main():
|
||||
backup=dict(type="bool", default=False),
|
||||
return_content=dict(type="bool", default=False),
|
||||
select_crypto_backend=dict(
|
||||
type="str", default="auto", choices=["auto", "cryptography", "pyopenssl"]
|
||||
type="str", default="auto", choices=["auto", "cryptography"]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -39,10 +39,7 @@ attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
details:
|
||||
- Currently in check mode, private keys will not be (re-)generated, only the changed status is set. This will change
|
||||
in community.crypto 3.0.0.
|
||||
- From community.crypto 3.0.0 on, the module will ignore check mode and always behave as if check mode is not active.
|
||||
If you think this breaks your use-case of this module, please create an issue in the community.crypto repository.
|
||||
- Since community.crypto 3.0.0, the module ignores check mode and always behaves as if check mode is not active.
|
||||
options:
|
||||
content:
|
||||
description:
|
||||
|
||||
@@ -33,10 +33,7 @@ attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
details:
|
||||
- Currently in check mode, private keys will not be (re-)generated, only the changed status is set. This will change
|
||||
in community.crypto 3.0.0.
|
||||
- From community.crypto 3.0.0 on, the module will ignore check mode and always behave as if check mode is not active.
|
||||
If you think this breaks your use-case of this module, please create an issue in the community.crypto repository.
|
||||
- Since community.crypto 3.0.0 the module ignores check mode and always behaves as if check mode is not active.
|
||||
options:
|
||||
provider:
|
||||
description:
|
||||
@@ -162,18 +159,7 @@ class GenericCertificate(object):
|
||||
|
||||
def generate(self, module):
|
||||
if self.module_backend.needs_regeneration():
|
||||
if not self.check_mode:
|
||||
self.module_backend.generate_certificate()
|
||||
else:
|
||||
self.module.deprecate(
|
||||
"Check mode support for x509_certificate_pipe will change in community.crypto 3.0.0"
|
||||
" to behave the same as without check mode. You can get that behavior right now"
|
||||
" by adding `check_mode: false` to the x509_certificate_pipe task. If you think this"
|
||||
" breaks your use-case of this module, please create an issue in the"
|
||||
" community.crypto repository",
|
||||
version="3.0.0",
|
||||
collection_name="community.crypto",
|
||||
)
|
||||
self.module_backend.generate_certificate()
|
||||
self.changed = True
|
||||
|
||||
def dump(self, check_mode=False):
|
||||
|
||||
@@ -59,17 +59,9 @@ options:
|
||||
- This parameter was called O(mode) before community.crypto 2.13.0. It has been renamed to avoid a collision with the
|
||||
common O(mode) parameter for setting the CRL file's access mode.
|
||||
type: str
|
||||
# default: generate
|
||||
default: generate
|
||||
choices: [generate, update]
|
||||
version_added: 2.13.0
|
||||
mode:
|
||||
description:
|
||||
- This parameter has been renamed to O(crl_mode). The old name O(mode) is now deprecated and will be removed in community.crypto
|
||||
3.0.0. Replace usage of this parameter with O(crl_mode).
|
||||
- Note that from community.crypto 3.0.0 on, O(mode) will be used for the CRL file's mode.
|
||||
type: str
|
||||
# default: generate
|
||||
choices: [generate, update]
|
||||
|
||||
force:
|
||||
description:
|
||||
@@ -968,16 +960,9 @@ def main():
|
||||
state=dict(type="str", default="present", choices=["present", "absent"]),
|
||||
crl_mode=dict(
|
||||
type="str",
|
||||
# default='generate',
|
||||
default="generate",
|
||||
choices=["generate", "update"],
|
||||
),
|
||||
mode=dict(
|
||||
type="str",
|
||||
# default='generate',
|
||||
choices=["generate", "update"],
|
||||
removed_in_version="3.0.0",
|
||||
removed_from_collection="community.crypto",
|
||||
),
|
||||
force=dict(type="bool", default=False),
|
||||
backup=dict(type="bool", default=False),
|
||||
path=dict(type="path", required=True),
|
||||
@@ -1044,16 +1029,6 @@ def main():
|
||||
add_file_common_args=True,
|
||||
)
|
||||
|
||||
if module.params["mode"]:
|
||||
if module.params["crl_mode"]:
|
||||
module.fail_json(
|
||||
"You cannot use both `mode` and `crl_mode`. Use `crl_mode`."
|
||||
)
|
||||
module.params["crl_mode"] = module.params["mode"]
|
||||
# TODO: in 3.0.0, once the option `mode` has been removed, remove this:
|
||||
module.params.pop("mode", None)
|
||||
# From then on, `mode` will be the file mode of the CRL file
|
||||
|
||||
if not CRYPTOGRAPHY_FOUND:
|
||||
module.fail_json(
|
||||
msg=missing_required_lib(
|
||||
|
||||
Reference in New Issue
Block a user