mirror of
https://github.com/ansible-collections/community.crypto.git
synced 2026-05-06 13:22:58 +00:00
Work on issues found by pylint (#896)
* Look at possibly-used-before-assignment. * Use latest beta releases of ansible-core 2.19 for mypy and pylint. * Look at unsupported-*. * Look at unknown-option-value. * Look at redefined-builtin. * Look at superfluous-parens. * Look at unspecified-encoding. * Adjust to new cryptography version and to ansible-core 2.17's pylint. * Look at super-with-arguments. * Look at no-else-*. * Look at try-except-raise. * Look at inconsistent-return-statements. * Look at redefined-outer-name. * Look at redefined-argument-from-local. * Look at attribute-defined-outside-init. * Look at unused-variable. * Look at protected-access. * Look at raise-missing-from. * Look at arguments-differ. * Look at useless-suppression and use-symbolic-message-instead. * Look at consider-using-dict-items. * Look at consider-using-in. * Look at consider-using-set-comprehension. * Look at consider-using-with. * Look at use-dict-literal.
This commit is contained in:
@@ -109,7 +109,7 @@ class CertificateBackend(metaclass=abc.ABCMeta):
|
||||
result["can_parse_certificate"] = True
|
||||
return result
|
||||
except Exception:
|
||||
return dict(can_parse_certificate=False)
|
||||
return {"can_parse_certificate": False}
|
||||
|
||||
@abc.abstractmethod
|
||||
def generate_certificate(self) -> None:
|
||||
@@ -143,7 +143,7 @@ class CertificateBackend(metaclass=abc.ABCMeta):
|
||||
passphrase=self.privatekey_passphrase,
|
||||
)
|
||||
except OpenSSLBadPassphraseError as exc:
|
||||
raise CertificateError(exc)
|
||||
raise CertificateError(exc) from exc
|
||||
|
||||
def _ensure_csr_loaded(self) -> None:
|
||||
"""Load the CSR into self.csr."""
|
||||
@@ -380,25 +380,28 @@ def select_backend(
|
||||
|
||||
def get_certificate_argument_spec() -> ArgumentSpec:
|
||||
return ArgumentSpec(
|
||||
argument_spec=dict(
|
||||
provider=dict(
|
||||
type="str", choices=[]
|
||||
), # choices will be filled by add_XXX_provider_to_argument_spec() in certificate_xxx.py
|
||||
force=dict(
|
||||
type="bool",
|
||||
default=False,
|
||||
),
|
||||
csr_path=dict(type="path"),
|
||||
csr_content=dict(type="str"),
|
||||
ignore_timestamps=dict(type="bool", default=True),
|
||||
select_crypto_backend=dict(
|
||||
type="str", default="auto", choices=["auto", "cryptography"]
|
||||
),
|
||||
argument_spec={
|
||||
"provider": {
|
||||
"type": "str",
|
||||
"choices": [],
|
||||
}, # choices will be filled by add_XXX_provider_to_argument_spec() in certificate_xxx.py
|
||||
"force": {
|
||||
"type": "bool",
|
||||
"default": False,
|
||||
},
|
||||
"csr_path": {"type": "path"},
|
||||
"csr_content": {"type": "str"},
|
||||
"ignore_timestamps": {"type": "bool", "default": True},
|
||||
"select_crypto_backend": {
|
||||
"type": "str",
|
||||
"default": "auto",
|
||||
"choices": ["auto", "cryptography"],
|
||||
},
|
||||
# General properties of a certificate
|
||||
privatekey_path=dict(type="path"),
|
||||
privatekey_content=dict(type="str", no_log=True),
|
||||
privatekey_passphrase=dict(type="str", no_log=True),
|
||||
),
|
||||
"privatekey_path": {"type": "path"},
|
||||
"privatekey_content": {"type": "str", "no_log": True},
|
||||
"privatekey_passphrase": {"type": "str", "no_log": True},
|
||||
},
|
||||
mutually_exclusive=[
|
||||
["csr_path", "csr_content"],
|
||||
["privatekey_path", "privatekey_content"],
|
||||
|
||||
@@ -30,7 +30,7 @@ if t.TYPE_CHECKING:
|
||||
|
||||
class AcmeCertificateBackend(CertificateBackend):
|
||||
def __init__(self, *, module: AnsibleModule) -> None:
|
||||
super(AcmeCertificateBackend, self).__init__(module=module)
|
||||
super().__init__(module=module)
|
||||
self.accountkey_path: str = module.params["acme_accountkey_path"]
|
||||
self.challenge_path: str = module.params["acme_challenge_path"]
|
||||
self.use_chain: bool = module.params["acme_chain"]
|
||||
@@ -94,7 +94,7 @@ class AcmeCertificateBackend(CertificateBackend):
|
||||
self.module.run_command(command, check_rc=True)[1]
|
||||
)
|
||||
except OSError as exc:
|
||||
raise CertificateError(exc)
|
||||
raise CertificateError(exc) from exc
|
||||
|
||||
def get_certificate_data(self) -> bytes:
|
||||
"""Return bytes for self.cert."""
|
||||
@@ -103,9 +103,7 @@ class AcmeCertificateBackend(CertificateBackend):
|
||||
return self.cert_bytes
|
||||
|
||||
def dump(self, *, include_certificate: bool) -> dict[str, t.Any]:
|
||||
result = super(AcmeCertificateBackend, self).dump(
|
||||
include_certificate=include_certificate
|
||||
)
|
||||
result = super().dump(include_certificate=include_certificate)
|
||||
result["accountkey"] = self.accountkey_path
|
||||
return result
|
||||
|
||||
@@ -128,14 +126,15 @@ class AcmeCertificateProvider(CertificateProvider):
|
||||
def add_acme_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None:
|
||||
argument_spec.argument_spec["provider"]["choices"].append("acme")
|
||||
argument_spec.argument_spec.update(
|
||||
dict(
|
||||
acme_accountkey_path=dict(type="path"),
|
||||
acme_challenge_path=dict(type="path"),
|
||||
acme_chain=dict(type="bool", default=False),
|
||||
acme_directory=dict(
|
||||
type="str", default="https://acme-v02.api.letsencrypt.org/directory"
|
||||
),
|
||||
)
|
||||
{
|
||||
"acme_accountkey_path": {"type": "path"},
|
||||
"acme_challenge_path": {"type": "path"},
|
||||
"acme_chain": {"type": "bool", "default": False},
|
||||
"acme_directory": {
|
||||
"type": "str",
|
||||
"default": "https://acme-v02.api.letsencrypt.org/directory",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -51,13 +51,14 @@ except ImportError:
|
||||
|
||||
class EntrustCertificateBackend(CertificateBackend):
|
||||
def __init__(self, *, module: AnsibleModule) -> None:
|
||||
super(EntrustCertificateBackend, self).__init__(module=module)
|
||||
super().__init__(module=module)
|
||||
self.trackingId = None
|
||||
self.notAfter = get_relative_time_option(
|
||||
module.params["entrust_not_after"],
|
||||
input_name="entrust_not_after",
|
||||
with_timezone=CRYPTOGRAPHY_TIMEZONE,
|
||||
)
|
||||
self.cert_bytes: bytes | None = None
|
||||
|
||||
if self.csr_content is None:
|
||||
if self.csr_path is None:
|
||||
@@ -119,7 +120,7 @@ class EntrustCertificateBackend(CertificateBackend):
|
||||
body["csr"] = to_native(self.csr_content)
|
||||
else:
|
||||
assert self.csr_path is not None
|
||||
with open(self.csr_path, "r") as csr_file:
|
||||
with open(self.csr_path, "r", encoding="utf-8") as csr_file:
|
||||
body["csr"] = csr_file.read()
|
||||
|
||||
body["certType"] = self.module.params["entrust_cert_type"]
|
||||
@@ -157,6 +158,8 @@ class EntrustCertificateBackend(CertificateBackend):
|
||||
|
||||
def get_certificate_data(self) -> bytes:
|
||||
"""Return bytes for self.cert."""
|
||||
if self.cert_bytes is None:
|
||||
raise AssertionError("Contract violation: cert_bytes not set")
|
||||
return self.cert_bytes
|
||||
|
||||
def needs_regeneration(
|
||||
@@ -165,7 +168,7 @@ class EntrustCertificateBackend(CertificateBackend):
|
||||
not_before: datetime.datetime | None = None,
|
||||
not_after: datetime.datetime | None = None,
|
||||
) -> bool:
|
||||
parent_check = super(EntrustCertificateBackend, self).needs_regeneration()
|
||||
parent_check = super().needs_regeneration()
|
||||
|
||||
try:
|
||||
cert_details = self._get_cert_details()
|
||||
@@ -176,7 +179,7 @@ class EntrustCertificateBackend(CertificateBackend):
|
||||
|
||||
# Always issue a new certificate if the certificate is expired, suspended or revoked
|
||||
status = cert_details.get("status", False)
|
||||
if status == "EXPIRED" or status == "SUSPENDED" or status == "REVOKED":
|
||||
if status in ("EXPIRED", "SUSPENDED", "REVOKED"):
|
||||
return True
|
||||
|
||||
# If the requested cert type was specified and it is for a different certificate type than the initial certificate, a new one is needed
|
||||
@@ -239,11 +242,11 @@ class EntrustCertificateProvider(CertificateProvider):
|
||||
def add_entrust_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None:
|
||||
argument_spec.argument_spec["provider"]["choices"].append("entrust")
|
||||
argument_spec.argument_spec.update(
|
||||
dict(
|
||||
entrust_cert_type=dict(
|
||||
type="str",
|
||||
default="STANDARD_SSL",
|
||||
choices=[
|
||||
{
|
||||
"entrust_cert_type": {
|
||||
"type": "str",
|
||||
"default": "STANDARD_SSL",
|
||||
"choices": [
|
||||
"STANDARD_SSL",
|
||||
"ADVANTAGE_SSL",
|
||||
"UC_SSL",
|
||||
@@ -255,20 +258,20 @@ def add_entrust_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None:
|
||||
"CDS_ENT_PRO",
|
||||
"SMIME_ENT",
|
||||
],
|
||||
),
|
||||
entrust_requester_email=dict(type="str"),
|
||||
entrust_requester_name=dict(type="str"),
|
||||
entrust_requester_phone=dict(type="str"),
|
||||
entrust_api_user=dict(type="str"),
|
||||
entrust_api_key=dict(type="str", no_log=True),
|
||||
entrust_api_client_cert_path=dict(type="path"),
|
||||
entrust_api_client_cert_key_path=dict(type="path", no_log=True),
|
||||
entrust_api_specification_path=dict(
|
||||
type="path",
|
||||
default="https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml",
|
||||
),
|
||||
entrust_not_after=dict(type="str", default="+365d"),
|
||||
)
|
||||
},
|
||||
"entrust_requester_email": {"type": "str"},
|
||||
"entrust_requester_name": {"type": "str"},
|
||||
"entrust_requester_phone": {"type": "str"},
|
||||
"entrust_api_user": {"type": "str"},
|
||||
"entrust_api_key": {"type": "str", "no_log": True},
|
||||
"entrust_api_client_cert_path": {"type": "path"},
|
||||
"entrust_api_client_cert_key_path": {"type": "path", "no_log": True},
|
||||
"entrust_api_specification_path": {
|
||||
"type": "path",
|
||||
"default": "https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml",
|
||||
},
|
||||
"entrust_not_after": {"type": "str", "default": "+365d"},
|
||||
}
|
||||
)
|
||||
argument_spec.required_if.append(
|
||||
(
|
||||
|
||||
@@ -70,6 +70,8 @@ TIMESTAMP_FORMAT = "%Y%m%d%H%M%SZ"
|
||||
|
||||
|
||||
class CertificateInfoRetrieval(metaclass=abc.ABCMeta):
|
||||
cert: x509.Certificate
|
||||
|
||||
def __init__(self, *, module: GeneralAnsibleModule, content: bytes) -> None:
|
||||
# content must be a bytes string
|
||||
self.module = module
|
||||
@@ -169,11 +171,11 @@ class CertificateInfoRetrieval(metaclass=abc.ABCMeta):
|
||||
result["signature_algorithm"] = self._get_signature_algorithm()
|
||||
subject = self._get_subject_ordered()
|
||||
issuer = self._get_issuer_ordered()
|
||||
result["subject"] = dict()
|
||||
result["subject"] = {}
|
||||
for k, v in subject:
|
||||
result["subject"][k] = v
|
||||
result["subject_ordered"] = subject
|
||||
result["issuer"] = dict()
|
||||
result["issuer"] = {}
|
||||
for k, v in issuer:
|
||||
result["issuer"][k] = v
|
||||
result["issuer_ordered"] = issuer
|
||||
@@ -249,9 +251,7 @@ class CertificateInfoRetrievalCryptography(CertificateInfoRetrieval):
|
||||
"""Validate the supplied cert, using the cryptography backend"""
|
||||
|
||||
def __init__(self, *, module: GeneralAnsibleModule, content: bytes) -> None:
|
||||
super(CertificateInfoRetrievalCryptography, self).__init__(
|
||||
module=module, content=content
|
||||
)
|
||||
super().__init__(module=module, content=content)
|
||||
self.name_encoding = module.params.get("name_encoding", "ignore")
|
||||
|
||||
def _get_der_bytes(self) -> bytes:
|
||||
@@ -289,36 +289,36 @@ class CertificateInfoRetrievalCryptography(CertificateInfoRetrieval):
|
||||
x509.KeyUsage
|
||||
)
|
||||
current_key_usage = current_key_ext.value
|
||||
key_usage = dict(
|
||||
digital_signature=current_key_usage.digital_signature,
|
||||
content_commitment=current_key_usage.content_commitment,
|
||||
key_encipherment=current_key_usage.key_encipherment,
|
||||
data_encipherment=current_key_usage.data_encipherment,
|
||||
key_agreement=current_key_usage.key_agreement,
|
||||
key_cert_sign=current_key_usage.key_cert_sign,
|
||||
crl_sign=current_key_usage.crl_sign,
|
||||
encipher_only=False,
|
||||
decipher_only=False,
|
||||
)
|
||||
key_usage = {
|
||||
"digital_signature": current_key_usage.digital_signature,
|
||||
"content_commitment": current_key_usage.content_commitment,
|
||||
"key_encipherment": current_key_usage.key_encipherment,
|
||||
"data_encipherment": current_key_usage.data_encipherment,
|
||||
"key_agreement": current_key_usage.key_agreement,
|
||||
"key_cert_sign": current_key_usage.key_cert_sign,
|
||||
"crl_sign": current_key_usage.crl_sign,
|
||||
"encipher_only": False,
|
||||
"decipher_only": False,
|
||||
}
|
||||
if key_usage["key_agreement"]:
|
||||
key_usage.update(
|
||||
dict(
|
||||
encipher_only=current_key_usage.encipher_only,
|
||||
decipher_only=current_key_usage.decipher_only,
|
||||
)
|
||||
{
|
||||
"encipher_only": current_key_usage.encipher_only,
|
||||
"decipher_only": current_key_usage.decipher_only,
|
||||
}
|
||||
)
|
||||
|
||||
key_usage_names = dict(
|
||||
digital_signature="Digital Signature",
|
||||
content_commitment="Non Repudiation",
|
||||
key_encipherment="Key Encipherment",
|
||||
data_encipherment="Data Encipherment",
|
||||
key_agreement="Key Agreement",
|
||||
key_cert_sign="Certificate Sign",
|
||||
crl_sign="CRL Sign",
|
||||
encipher_only="Encipher Only",
|
||||
decipher_only="Decipher Only",
|
||||
)
|
||||
key_usage_names = {
|
||||
"digital_signature": "Digital Signature",
|
||||
"content_commitment": "Non Repudiation",
|
||||
"key_encipherment": "Key Encipherment",
|
||||
"data_encipherment": "Data Encipherment",
|
||||
"key_agreement": "Key Agreement",
|
||||
"key_cert_sign": "Certificate Sign",
|
||||
"crl_sign": "CRL Sign",
|
||||
"encipher_only": "Encipher Only",
|
||||
"decipher_only": "Decipher Only",
|
||||
}
|
||||
return (
|
||||
sorted(
|
||||
[
|
||||
|
||||
@@ -63,7 +63,7 @@ except ImportError:
|
||||
|
||||
class OwnCACertificateBackendCryptography(CertificateBackend):
|
||||
def __init__(self, *, module: AnsibleModule) -> None:
|
||||
super(OwnCACertificateBackendCryptography, self).__init__(module=module)
|
||||
super().__init__(module=module)
|
||||
|
||||
self.create_subject_key_identifier: t.Literal[
|
||||
"create_if_not_provided", "always_create", "never_create"
|
||||
@@ -223,7 +223,7 @@ class OwnCACertificateBackendCryptography(CertificateBackend):
|
||||
not_before: datetime.datetime | None = None,
|
||||
not_after: datetime.datetime | None = None,
|
||||
) -> bool:
|
||||
if super(OwnCACertificateBackendCryptography, self).needs_regeneration(
|
||||
if super().needs_regeneration(
|
||||
not_before=self.notBefore, not_after=self.notAfter
|
||||
):
|
||||
return True
|
||||
@@ -272,9 +272,7 @@ class OwnCACertificateBackendCryptography(CertificateBackend):
|
||||
return False
|
||||
|
||||
def dump(self, *, include_certificate: bool) -> dict[str, t.Any]:
|
||||
result = super(OwnCACertificateBackendCryptography, self).dump(
|
||||
include_certificate=include_certificate
|
||||
)
|
||||
result = super().dump(include_certificate=include_certificate)
|
||||
result.update(
|
||||
{
|
||||
"ca_cert": self.ca_cert_path,
|
||||
@@ -343,23 +341,23 @@ class OwnCACertificateProvider(CertificateProvider):
|
||||
def add_ownca_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None:
|
||||
argument_spec.argument_spec["provider"]["choices"].append("ownca")
|
||||
argument_spec.argument_spec.update(
|
||||
dict(
|
||||
ownca_path=dict(type="path"),
|
||||
ownca_content=dict(type="str"),
|
||||
ownca_privatekey_path=dict(type="path"),
|
||||
ownca_privatekey_content=dict(type="str", no_log=True),
|
||||
ownca_privatekey_passphrase=dict(type="str", no_log=True),
|
||||
ownca_digest=dict(type="str", default="sha256"),
|
||||
ownca_version=dict(type="int", default=3, choices=[3]), # not used
|
||||
ownca_not_before=dict(type="str", default="+0s"),
|
||||
ownca_not_after=dict(type="str", default="+3650d"),
|
||||
ownca_create_subject_key_identifier=dict(
|
||||
type="str",
|
||||
default="create_if_not_provided",
|
||||
choices=["create_if_not_provided", "always_create", "never_create"],
|
||||
),
|
||||
ownca_create_authority_key_identifier=dict(type="bool", default=True),
|
||||
)
|
||||
{
|
||||
"ownca_path": {"type": "path"},
|
||||
"ownca_content": {"type": "str"},
|
||||
"ownca_privatekey_path": {"type": "path"},
|
||||
"ownca_privatekey_content": {"type": "str", "no_log": True},
|
||||
"ownca_privatekey_passphrase": {"type": "str", "no_log": True},
|
||||
"ownca_digest": {"type": "str", "default": "sha256"},
|
||||
"ownca_version": {"type": "int", "default": 3, "choices": [3]}, # not used
|
||||
"ownca_not_before": {"type": "str", "default": "+0s"},
|
||||
"ownca_not_after": {"type": "str", "default": "+3650d"},
|
||||
"ownca_create_subject_key_identifier": {
|
||||
"type": "str",
|
||||
"default": "create_if_not_provided",
|
||||
"choices": ["create_if_not_provided", "always_create", "never_create"],
|
||||
},
|
||||
"ownca_create_authority_key_identifier": {"type": "bool", "default": True},
|
||||
}
|
||||
)
|
||||
argument_spec.mutually_exclusive.extend(
|
||||
[
|
||||
|
||||
@@ -59,7 +59,7 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend):
|
||||
privatekey: CertificateIssuerPrivateKeyTypes
|
||||
|
||||
def __init__(self, *, module: AnsibleModule) -> None:
|
||||
super(SelfSignedCertificateBackendCryptography, self).__init__(module=module)
|
||||
super().__init__(module=module)
|
||||
|
||||
self.create_subject_key_identifier: t.Literal[
|
||||
"create_if_not_provided", "always_create", "never_create"
|
||||
@@ -144,7 +144,7 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend):
|
||||
critical=False,
|
||||
)
|
||||
except ValueError as e:
|
||||
raise CertificateError(str(e))
|
||||
raise CertificateError(str(e)) from e
|
||||
|
||||
certificate = cert_builder.sign(
|
||||
private_key=self.privatekey,
|
||||
@@ -167,7 +167,7 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend):
|
||||
) -> bool:
|
||||
assert self.privatekey is not None
|
||||
|
||||
if super(SelfSignedCertificateBackendCryptography, self).needs_regeneration(
|
||||
if super().needs_regeneration(
|
||||
not_before=self.notBefore, not_after=self.notAfter
|
||||
):
|
||||
return True
|
||||
@@ -185,9 +185,7 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend):
|
||||
return False
|
||||
|
||||
def dump(self, *, include_certificate: bool) -> dict[str, t.Any]:
|
||||
result = super(SelfSignedCertificateBackendCryptography, self).dump(
|
||||
include_certificate=include_certificate
|
||||
)
|
||||
result = super().dump(include_certificate=include_certificate)
|
||||
|
||||
if self.module.check_mode:
|
||||
result.update(
|
||||
@@ -243,21 +241,29 @@ class SelfSignedCertificateProvider(CertificateProvider):
|
||||
def add_selfsigned_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None:
|
||||
argument_spec.argument_spec["provider"]["choices"].append("selfsigned")
|
||||
argument_spec.argument_spec.update(
|
||||
dict(
|
||||
selfsigned_version=dict(type="int", default=3, choices=[3]), # not used
|
||||
selfsigned_digest=dict(type="str", default="sha256"),
|
||||
selfsigned_not_before=dict(
|
||||
type="str", default="+0s", aliases=["selfsigned_notBefore"]
|
||||
),
|
||||
selfsigned_not_after=dict(
|
||||
type="str", default="+3650d", aliases=["selfsigned_notAfter"]
|
||||
),
|
||||
selfsigned_create_subject_key_identifier=dict(
|
||||
type="str",
|
||||
default="create_if_not_provided",
|
||||
choices=["create_if_not_provided", "always_create", "never_create"],
|
||||
),
|
||||
)
|
||||
{
|
||||
"selfsigned_version": {
|
||||
"type": "int",
|
||||
"default": 3,
|
||||
"choices": [3],
|
||||
}, # not used
|
||||
"selfsigned_digest": {"type": "str", "default": "sha256"},
|
||||
"selfsigned_not_before": {
|
||||
"type": "str",
|
||||
"default": "+0s",
|
||||
"aliases": ["selfsigned_notBefore"],
|
||||
},
|
||||
"selfsigned_not_after": {
|
||||
"type": "str",
|
||||
"default": "+3650d",
|
||||
"aliases": ["selfsigned_notAfter"],
|
||||
},
|
||||
"selfsigned_create_subject_key_identifier": {
|
||||
"type": "str",
|
||||
"default": "create_if_not_provided",
|
||||
"choices": ["create_if_not_provided", "always_create", "never_create"],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -67,18 +67,18 @@ class CRLInfoRetrieval:
|
||||
self.name_encoding = module.params.get("name_encoding", "ignore")
|
||||
|
||||
def get_info(self) -> dict[str, t.Any]:
|
||||
self.crl_pem = identify_pem_format(self.content)
|
||||
crl_pem = identify_pem_format(self.content)
|
||||
try:
|
||||
if self.crl_pem:
|
||||
self.crl = x509.load_pem_x509_crl(self.content)
|
||||
if crl_pem:
|
||||
crl = x509.load_pem_x509_crl(self.content)
|
||||
else:
|
||||
self.crl = x509.load_der_x509_crl(self.content)
|
||||
crl = x509.load_der_x509_crl(self.content)
|
||||
except ValueError as e:
|
||||
self.module.fail_json(msg=f"Error while decoding CRL: {e}")
|
||||
|
||||
result: dict[str, t.Any] = {
|
||||
"changed": False,
|
||||
"format": "pem" if self.crl_pem else "der",
|
||||
"format": "pem" if crl_pem else "der",
|
||||
"last_update": None,
|
||||
"next_update": None,
|
||||
"digest": None,
|
||||
@@ -86,25 +86,24 @@ class CRLInfoRetrieval:
|
||||
"issuer": None,
|
||||
}
|
||||
|
||||
result["last_update"] = self.crl.last_update.strftime(TIMESTAMP_FORMAT)
|
||||
result["last_update"] = crl.last_update.strftime(TIMESTAMP_FORMAT)
|
||||
result["next_update"] = (
|
||||
self.crl.next_update.strftime(TIMESTAMP_FORMAT)
|
||||
if self.crl.next_update
|
||||
else None
|
||||
crl.next_update.strftime(TIMESTAMP_FORMAT) if crl.next_update else None
|
||||
)
|
||||
result["digest"] = cryptography_oid_to_name(
|
||||
cryptography_get_signature_algorithm_oid_from_crl(self.crl)
|
||||
cryptography_get_signature_algorithm_oid_from_crl(crl)
|
||||
)
|
||||
issuer = []
|
||||
for attribute in self.crl.issuer:
|
||||
for attribute in crl.issuer:
|
||||
issuer.append([cryptography_oid_to_name(attribute.oid), attribute.value])
|
||||
result["issuer_ordered"] = issuer
|
||||
result["issuer"] = {}
|
||||
issuer_dict = {}
|
||||
for k, v in issuer:
|
||||
result["issuer"][k] = v
|
||||
issuer_dict[k] = v
|
||||
result["issuer"] = issuer_dict
|
||||
if self.list_revoked_certificates:
|
||||
result["revoked_certificates"] = []
|
||||
for cert in self.crl:
|
||||
for cert in crl:
|
||||
entry = cryptography_decode_revoked_certificate(cert)
|
||||
result["revoked_certificates"].append(
|
||||
cryptography_dump_revoked(entry, idn_rewrite=self.name_encoding)
|
||||
|
||||
@@ -170,7 +170,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta):
|
||||
)
|
||||
self.ordered_subject = True
|
||||
except ValueError as exc:
|
||||
raise CertificateSigningRequestError(str(exc))
|
||||
raise CertificateSigningRequestError(str(exc)) from exc
|
||||
|
||||
self.using_common_name_for_san = False
|
||||
if not self.subjectAltName and module.params["use_common_name_for_san"]:
|
||||
@@ -189,7 +189,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta):
|
||||
except Exception as e:
|
||||
raise CertificateSigningRequestError(
|
||||
f"Cannot parse subject_key_identifier: {e}"
|
||||
)
|
||||
) from e
|
||||
|
||||
self.authority_key_identifier: bytes | None = None
|
||||
if authority_key_identifier is not None:
|
||||
@@ -200,7 +200,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta):
|
||||
except Exception as e:
|
||||
raise CertificateSigningRequestError(
|
||||
f"Cannot parse authority_key_identifier: {e}"
|
||||
)
|
||||
) from e
|
||||
|
||||
self.existing_csr: cryptography.x509.CertificateSigningRequest | None = None
|
||||
self.existing_csr_bytes: bytes | None = None
|
||||
@@ -221,7 +221,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta):
|
||||
result["can_parse_csr"] = True
|
||||
return result
|
||||
except Exception:
|
||||
return dict(can_parse_csr=False)
|
||||
return {"can_parse_csr": False}
|
||||
|
||||
@abc.abstractmethod
|
||||
def generate_csr(self) -> None:
|
||||
@@ -253,7 +253,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta):
|
||||
passphrase=self.privatekey_passphrase,
|
||||
)
|
||||
except OpenSSLBadPassphraseError as exc:
|
||||
raise CertificateSigningRequestError(exc)
|
||||
raise CertificateSigningRequestError(exc) from exc
|
||||
|
||||
@abc.abstractmethod
|
||||
def _check_csr(self) -> bool:
|
||||
@@ -294,10 +294,10 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta):
|
||||
# Store result
|
||||
result["csr"] = csr_bytes.decode("utf-8") if csr_bytes else None
|
||||
|
||||
result["diff"] = dict(
|
||||
before=self.diff_before,
|
||||
after=self.diff_after,
|
||||
)
|
||||
result["diff"] = {
|
||||
"before": self.diff_before,
|
||||
"after": self.diff_after,
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
@@ -347,16 +347,14 @@ def parse_crl_distribution_points(
|
||||
except (OpenSSLObjectError, ValueError) as e:
|
||||
raise OpenSSLObjectError(
|
||||
f"Error while parsing CRL distribution point #{index}: {e}"
|
||||
)
|
||||
) from e
|
||||
return result
|
||||
|
||||
|
||||
# Implementation with using cryptography
|
||||
class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBackend):
|
||||
def __init__(self, *, module: AnsibleModule) -> None:
|
||||
super(CertificateSigningRequestCryptographyBackend, self).__init__(
|
||||
module=module
|
||||
)
|
||||
super().__init__(module=module)
|
||||
if self.version != 1:
|
||||
module.warn(
|
||||
"The cryptography backend only supports version 1. (The only valid value according to RFC 2986.)"
|
||||
@@ -388,7 +386,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
|
||||
)
|
||||
)
|
||||
except ValueError as e:
|
||||
raise CertificateSigningRequestError(e)
|
||||
raise CertificateSigningRequestError(e) from e
|
||||
|
||||
if self.subjectAltName:
|
||||
csr = csr.add_extension(
|
||||
@@ -451,7 +449,9 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
|
||||
critical=self.name_constraints_critical,
|
||||
)
|
||||
except TypeError as e:
|
||||
raise OpenSSLObjectError(f"Error while parsing name constraint: {e}")
|
||||
raise OpenSSLObjectError(
|
||||
f"Error while parsing name constraint: {e}"
|
||||
) from e
|
||||
|
||||
if self.create_subject_key_identifier:
|
||||
if not is_potential_certificate_issuer_public_key(
|
||||
@@ -556,8 +556,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
|
||||
current_subject = [(sub.oid, sub.value) for sub in csr.subject]
|
||||
if self.ordered_subject:
|
||||
return subject == current_subject
|
||||
else:
|
||||
return set(subject) == set(current_subject)
|
||||
return set(subject) == set(current_subject)
|
||||
|
||||
def _find_extension(
|
||||
extensions: cryptography.x509.Extensions, exttype: type[_ET]
|
||||
@@ -596,15 +595,14 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
|
||||
)
|
||||
if not self.keyUsage:
|
||||
return current_keyusage_ext is None
|
||||
elif current_keyusage_ext is None:
|
||||
if current_keyusage_ext is None:
|
||||
return False
|
||||
params = cryptography_parse_key_usage_params(self.keyUsage)
|
||||
for param in params:
|
||||
if getattr(current_keyusage_ext.value, "_" + param) != params[param]:
|
||||
for param, value in params.items():
|
||||
# TODO: check whether getattr() with '_' prepended is really needed
|
||||
if getattr(current_keyusage_ext.value, "_" + param) != value:
|
||||
return False
|
||||
if current_keyusage_ext.critical != self.keyUsage_critical:
|
||||
return False
|
||||
return True
|
||||
return current_keyusage_ext.critical == self.keyUsage_critical
|
||||
|
||||
def _check_extenededKeyUsage(extensions: cryptography.x509.Extensions) -> bool:
|
||||
current_usages_ext = _find_extension(
|
||||
@@ -647,8 +645,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
|
||||
bc_ext is not None
|
||||
and bc_ext.critical == self.basicConstraints_critical
|
||||
)
|
||||
else:
|
||||
return bc_ext is None
|
||||
return bc_ext is None
|
||||
|
||||
def _check_ocspMustStaple(extensions: cryptography.x509.Extensions) -> bool:
|
||||
tlsfeature_ext = _find_extension(extensions, cryptography.x509.TLSFeature)
|
||||
@@ -662,8 +659,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
|
||||
cryptography.x509.TLSFeatureType.status_request
|
||||
in tlsfeature_ext.value
|
||||
)
|
||||
else:
|
||||
return tlsfeature_ext is None
|
||||
return tlsfeature_ext is None
|
||||
|
||||
def _check_nameConstraints(extensions: cryptography.x509.Extensions) -> bool:
|
||||
current_nc_ext = _find_extension(
|
||||
@@ -722,10 +718,8 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
|
||||
self.privatekey.public_key()
|
||||
).digest
|
||||
return ext.value.digest == digest
|
||||
else:
|
||||
return ext.value.digest == self.subject_key_identifier
|
||||
else:
|
||||
return ext is None
|
||||
return ext.value.digest == self.subject_key_identifier
|
||||
return ext is None
|
||||
|
||||
def _check_authority_key_identifier(
|
||||
extensions: cryptography.x509.Extensions,
|
||||
@@ -753,8 +747,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
|
||||
and ext.value.authority_cert_serial_number
|
||||
== self.authority_cert_serial_number
|
||||
)
|
||||
else:
|
||||
return ext is None
|
||||
return ext is None
|
||||
|
||||
def _check_crl_distribution_points(
|
||||
extensions: cryptography.x509.Extensions,
|
||||
@@ -814,77 +807,97 @@ def select_backend(
|
||||
|
||||
def get_csr_argument_spec() -> ArgumentSpec:
|
||||
return ArgumentSpec(
|
||||
argument_spec=dict(
|
||||
digest=dict(type="str", default="sha256"),
|
||||
privatekey_path=dict(type="path"),
|
||||
privatekey_content=dict(type="str", no_log=True),
|
||||
privatekey_passphrase=dict(type="str", no_log=True),
|
||||
version=dict(type="int", default=1, choices=[1]),
|
||||
subject=dict(type="dict"),
|
||||
subject_ordered=dict(type="list", elements="dict"),
|
||||
country_name=dict(type="str", aliases=["C", "countryName"]),
|
||||
state_or_province_name=dict(
|
||||
type="str", aliases=["ST", "stateOrProvinceName"]
|
||||
),
|
||||
locality_name=dict(type="str", aliases=["L", "localityName"]),
|
||||
organization_name=dict(type="str", aliases=["O", "organizationName"]),
|
||||
organizational_unit_name=dict(
|
||||
type="str", aliases=["OU", "organizationalUnitName"]
|
||||
),
|
||||
common_name=dict(type="str", aliases=["CN", "commonName"]),
|
||||
email_address=dict(type="str", aliases=["E", "emailAddress"]),
|
||||
subject_alt_name=dict(
|
||||
type="list", elements="str", aliases=["subjectAltName"]
|
||||
),
|
||||
subject_alt_name_critical=dict(
|
||||
type="bool", default=False, aliases=["subjectAltName_critical"]
|
||||
),
|
||||
use_common_name_for_san=dict(
|
||||
type="bool", default=True, aliases=["useCommonNameForSAN"]
|
||||
),
|
||||
key_usage=dict(type="list", elements="str", aliases=["keyUsage"]),
|
||||
key_usage_critical=dict(
|
||||
type="bool", default=False, aliases=["keyUsage_critical"]
|
||||
),
|
||||
extended_key_usage=dict(
|
||||
type="list", elements="str", aliases=["extKeyUsage", "extendedKeyUsage"]
|
||||
),
|
||||
extended_key_usage_critical=dict(
|
||||
type="bool",
|
||||
default=False,
|
||||
aliases=["extKeyUsage_critical", "extendedKeyUsage_critical"],
|
||||
),
|
||||
basic_constraints=dict(
|
||||
type="list", elements="str", aliases=["basicConstraints"]
|
||||
),
|
||||
basic_constraints_critical=dict(
|
||||
type="bool", default=False, aliases=["basicConstraints_critical"]
|
||||
),
|
||||
ocsp_must_staple=dict(
|
||||
type="bool", default=False, aliases=["ocspMustStaple"]
|
||||
),
|
||||
ocsp_must_staple_critical=dict(
|
||||
type="bool", default=False, aliases=["ocspMustStaple_critical"]
|
||||
),
|
||||
name_constraints_permitted=dict(type="list", elements="str"),
|
||||
name_constraints_excluded=dict(type="list", elements="str"),
|
||||
name_constraints_critical=dict(type="bool", default=False),
|
||||
create_subject_key_identifier=dict(type="bool", default=False),
|
||||
subject_key_identifier=dict(type="str"),
|
||||
authority_key_identifier=dict(type="str"),
|
||||
authority_cert_issuer=dict(type="list", elements="str"),
|
||||
authority_cert_serial_number=dict(type="int"),
|
||||
crl_distribution_points=dict(
|
||||
type="list",
|
||||
elements="dict",
|
||||
options=dict(
|
||||
full_name=dict(type="list", elements="str"),
|
||||
relative_name=dict(type="list", elements="str"),
|
||||
crl_issuer=dict(type="list", elements="str"),
|
||||
reasons=dict(
|
||||
type="list",
|
||||
elements="str",
|
||||
choices=[
|
||||
argument_spec={
|
||||
"digest": {"type": "str", "default": "sha256"},
|
||||
"privatekey_path": {"type": "path"},
|
||||
"privatekey_content": {"type": "str", "no_log": True},
|
||||
"privatekey_passphrase": {"type": "str", "no_log": True},
|
||||
"version": {"type": "int", "default": 1, "choices": [1]},
|
||||
"subject": {"type": "dict"},
|
||||
"subject_ordered": {"type": "list", "elements": "dict"},
|
||||
"country_name": {"type": "str", "aliases": ["C", "countryName"]},
|
||||
"state_or_province_name": {
|
||||
"type": "str",
|
||||
"aliases": ["ST", "stateOrProvinceName"],
|
||||
},
|
||||
"locality_name": {"type": "str", "aliases": ["L", "localityName"]},
|
||||
"organization_name": {"type": "str", "aliases": ["O", "organizationName"]},
|
||||
"organizational_unit_name": {
|
||||
"type": "str",
|
||||
"aliases": ["OU", "organizationalUnitName"],
|
||||
},
|
||||
"common_name": {"type": "str", "aliases": ["CN", "commonName"]},
|
||||
"email_address": {"type": "str", "aliases": ["E", "emailAddress"]},
|
||||
"subject_alt_name": {
|
||||
"type": "list",
|
||||
"elements": "str",
|
||||
"aliases": ["subjectAltName"],
|
||||
},
|
||||
"subject_alt_name_critical": {
|
||||
"type": "bool",
|
||||
"default": False,
|
||||
"aliases": ["subjectAltName_critical"],
|
||||
},
|
||||
"use_common_name_for_san": {
|
||||
"type": "bool",
|
||||
"default": True,
|
||||
"aliases": ["useCommonNameForSAN"],
|
||||
},
|
||||
"key_usage": {"type": "list", "elements": "str", "aliases": ["keyUsage"]},
|
||||
"key_usage_critical": {
|
||||
"type": "bool",
|
||||
"default": False,
|
||||
"aliases": ["keyUsage_critical"],
|
||||
},
|
||||
"extended_key_usage": {
|
||||
"type": "list",
|
||||
"elements": "str",
|
||||
"aliases": ["extKeyUsage", "extendedKeyUsage"],
|
||||
},
|
||||
"extended_key_usage_critical": {
|
||||
"type": "bool",
|
||||
"default": False,
|
||||
"aliases": ["extKeyUsage_critical", "extendedKeyUsage_critical"],
|
||||
},
|
||||
"basic_constraints": {
|
||||
"type": "list",
|
||||
"elements": "str",
|
||||
"aliases": ["basicConstraints"],
|
||||
},
|
||||
"basic_constraints_critical": {
|
||||
"type": "bool",
|
||||
"default": False,
|
||||
"aliases": ["basicConstraints_critical"],
|
||||
},
|
||||
"ocsp_must_staple": {
|
||||
"type": "bool",
|
||||
"default": False,
|
||||
"aliases": ["ocspMustStaple"],
|
||||
},
|
||||
"ocsp_must_staple_critical": {
|
||||
"type": "bool",
|
||||
"default": False,
|
||||
"aliases": ["ocspMustStaple_critical"],
|
||||
},
|
||||
"name_constraints_permitted": {"type": "list", "elements": "str"},
|
||||
"name_constraints_excluded": {"type": "list", "elements": "str"},
|
||||
"name_constraints_critical": {"type": "bool", "default": False},
|
||||
"create_subject_key_identifier": {"type": "bool", "default": False},
|
||||
"subject_key_identifier": {"type": "str"},
|
||||
"authority_key_identifier": {"type": "str"},
|
||||
"authority_cert_issuer": {"type": "list", "elements": "str"},
|
||||
"authority_cert_serial_number": {"type": "int"},
|
||||
"crl_distribution_points": {
|
||||
"type": "list",
|
||||
"elements": "dict",
|
||||
"options": {
|
||||
"full_name": {"type": "list", "elements": "str"},
|
||||
"relative_name": {"type": "list", "elements": "str"},
|
||||
"crl_issuer": {"type": "list", "elements": "str"},
|
||||
"reasons": {
|
||||
"type": "list",
|
||||
"elements": "str",
|
||||
"choices": [
|
||||
"key_compromise",
|
||||
"ca_compromise",
|
||||
"affiliation_changed",
|
||||
@@ -894,15 +907,17 @@ def get_csr_argument_spec() -> ArgumentSpec:
|
||||
"privilege_withdrawn",
|
||||
"aa_compromise",
|
||||
],
|
||||
),
|
||||
),
|
||||
mutually_exclusive=[("full_name", "relative_name")],
|
||||
required_one_of=[("full_name", "relative_name", "crl_issuer")],
|
||||
),
|
||||
select_crypto_backend=dict(
|
||||
type="str", default="auto", choices=["auto", "cryptography"]
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
"mutually_exclusive": [("full_name", "relative_name")],
|
||||
"required_one_of": [("full_name", "relative_name", "crl_issuer")],
|
||||
},
|
||||
"select_crypto_backend": {
|
||||
"type": "str",
|
||||
"default": "auto",
|
||||
"choices": ["auto", "cryptography"],
|
||||
},
|
||||
},
|
||||
required_together=[
|
||||
["authority_cert_issuer", "authority_cert_serial_number"],
|
||||
],
|
||||
|
||||
@@ -61,6 +61,8 @@ TIMESTAMP_FORMAT = "%Y%m%d%H%M%SZ"
|
||||
|
||||
|
||||
class CSRInfoRetrieval(metaclass=abc.ABCMeta):
|
||||
csr: x509.CertificateSigningRequest
|
||||
|
||||
def __init__(
|
||||
self, *, module: GeneralAnsibleModule, content: bytes, validate_signature: bool
|
||||
) -> None:
|
||||
@@ -129,7 +131,7 @@ class CSRInfoRetrieval(metaclass=abc.ABCMeta):
|
||||
)
|
||||
|
||||
subject = self._get_subject_ordered()
|
||||
result["subject"] = dict()
|
||||
result["subject"] = {}
|
||||
for k, v in subject:
|
||||
result["subject"][k] = v
|
||||
result["subject_ordered"] = subject
|
||||
@@ -197,7 +199,7 @@ class CSRInfoRetrievalCryptography(CSRInfoRetrieval):
|
||||
def __init__(
|
||||
self, *, module: GeneralAnsibleModule, content: bytes, validate_signature: bool
|
||||
) -> None:
|
||||
super(CSRInfoRetrievalCryptography, self).__init__(
|
||||
super().__init__(
|
||||
module=module, content=content, validate_signature=validate_signature
|
||||
)
|
||||
self.name_encoding: t.Literal["ignore", "idna", "unicode"] = module.params.get(
|
||||
@@ -216,36 +218,36 @@ class CSRInfoRetrievalCryptography(CSRInfoRetrieval):
|
||||
try:
|
||||
current_key_ext = self.csr.extensions.get_extension_for_class(x509.KeyUsage)
|
||||
current_key_usage = current_key_ext.value
|
||||
key_usage = dict(
|
||||
digital_signature=current_key_usage.digital_signature,
|
||||
content_commitment=current_key_usage.content_commitment,
|
||||
key_encipherment=current_key_usage.key_encipherment,
|
||||
data_encipherment=current_key_usage.data_encipherment,
|
||||
key_agreement=current_key_usage.key_agreement,
|
||||
key_cert_sign=current_key_usage.key_cert_sign,
|
||||
crl_sign=current_key_usage.crl_sign,
|
||||
encipher_only=False,
|
||||
decipher_only=False,
|
||||
)
|
||||
key_usage = {
|
||||
"digital_signature": current_key_usage.digital_signature,
|
||||
"content_commitment": current_key_usage.content_commitment,
|
||||
"key_encipherment": current_key_usage.key_encipherment,
|
||||
"data_encipherment": current_key_usage.data_encipherment,
|
||||
"key_agreement": current_key_usage.key_agreement,
|
||||
"key_cert_sign": current_key_usage.key_cert_sign,
|
||||
"crl_sign": current_key_usage.crl_sign,
|
||||
"encipher_only": False,
|
||||
"decipher_only": False,
|
||||
}
|
||||
if key_usage["key_agreement"]:
|
||||
key_usage.update(
|
||||
dict(
|
||||
encipher_only=current_key_usage.encipher_only,
|
||||
decipher_only=current_key_usage.decipher_only,
|
||||
)
|
||||
{
|
||||
"encipher_only": current_key_usage.encipher_only,
|
||||
"decipher_only": current_key_usage.decipher_only,
|
||||
}
|
||||
)
|
||||
|
||||
key_usage_names = dict(
|
||||
digital_signature="Digital Signature",
|
||||
content_commitment="Non Repudiation",
|
||||
key_encipherment="Key Encipherment",
|
||||
data_encipherment="Data Encipherment",
|
||||
key_agreement="Key Agreement",
|
||||
key_cert_sign="Certificate Sign",
|
||||
crl_sign="CRL Sign",
|
||||
encipher_only="Encipher Only",
|
||||
decipher_only="Decipher Only",
|
||||
)
|
||||
key_usage_names = {
|
||||
"digital_signature": "Digital Signature",
|
||||
"content_commitment": "Non Repudiation",
|
||||
"key_encipherment": "Key Encipherment",
|
||||
"data_encipherment": "Data Encipherment",
|
||||
"key_agreement": "Key Agreement",
|
||||
"key_cert_sign": "Certificate Sign",
|
||||
"crl_sign": "CRL Sign",
|
||||
"encipher_only": "Encipher Only",
|
||||
"decipher_only": "Decipher Only",
|
||||
}
|
||||
return (
|
||||
sorted(
|
||||
[
|
||||
|
||||
@@ -265,10 +265,10 @@ class PrivateKeyBackend(metaclass=abc.ABCMeta):
|
||||
else:
|
||||
result["privatekey"] = None
|
||||
|
||||
result["diff"] = dict(
|
||||
before=self.diff_before,
|
||||
after=self.diff_after,
|
||||
)
|
||||
result["diff"] = {
|
||||
"before": self.diff_before,
|
||||
"after": self.diff_after,
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
@@ -323,7 +323,7 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
|
||||
self.curves[name] = _Curve(name=name, ectype=ectype, deprecated=deprecated)
|
||||
|
||||
def __init__(self, module: GeneralAnsibleModule) -> None:
|
||||
super(PrivateKeyCryptographyBackend, self).__init__(module=module)
|
||||
super().__init__(module=module)
|
||||
|
||||
self.curves: dict[str, _Curve] = {}
|
||||
self._add_curve("secp224r1", "SECP224R1")
|
||||
@@ -351,8 +351,7 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
|
||||
return self.format # type: ignore
|
||||
if self.type in ("X25519", "X448", "Ed25519", "Ed448"):
|
||||
return "pkcs8"
|
||||
else:
|
||||
return "pkcs1"
|
||||
return "pkcs1"
|
||||
|
||||
def generate_private_key(self) -> None:
|
||||
"""(Re-)Generate private key."""
|
||||
@@ -427,6 +426,9 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
|
||||
export_encoding = (
|
||||
cryptography.hazmat.primitives.serialization.Encoding.Raw
|
||||
)
|
||||
else:
|
||||
# pylint does not notice that all possible values for export_format_txt have been covered.
|
||||
raise AssertionError("Can never be reached") # pragma: no cover
|
||||
except AttributeError:
|
||||
self.module.fail_json(
|
||||
msg=f'Cryptography backend does not support the selected output format "{self.format}"'
|
||||
@@ -469,8 +471,8 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
|
||||
raise AssertionError("existing_private_key_bytes not set")
|
||||
try:
|
||||
# Interpret bytes depending on format.
|
||||
format = identify_private_key_format(data)
|
||||
if format == "raw":
|
||||
key_format = identify_private_key_format(data)
|
||||
if key_format == "raw":
|
||||
if len(data) == 56:
|
||||
return cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey.from_private_bytes(
|
||||
data
|
||||
@@ -497,15 +499,13 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
|
||||
data
|
||||
)
|
||||
raise PrivateKeyError("Cannot load raw key")
|
||||
else:
|
||||
return (
|
||||
cryptography.hazmat.primitives.serialization.load_pem_private_key(
|
||||
data,
|
||||
None if self.passphrase is None else to_bytes(self.passphrase),
|
||||
)
|
||||
)
|
||||
|
||||
return cryptography.hazmat.primitives.serialization.load_pem_private_key(
|
||||
data,
|
||||
None if self.passphrase is None else to_bytes(self.passphrase),
|
||||
)
|
||||
except Exception as e:
|
||||
raise PrivateKeyError(e)
|
||||
raise PrivateKeyError(e) from e
|
||||
|
||||
def _ensure_existing_private_key_loaded(self) -> None:
|
||||
if self.existing_private_key is None and self.has_existing():
|
||||
@@ -515,21 +515,20 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
|
||||
if self.existing_private_key_bytes is None:
|
||||
raise AssertionError("existing_private_key_bytes not set")
|
||||
try:
|
||||
format = identify_private_key_format(self.existing_private_key_bytes)
|
||||
if format == "raw":
|
||||
key_format = identify_private_key_format(self.existing_private_key_bytes)
|
||||
if key_format == "raw":
|
||||
# Raw keys cannot be encrypted. To avoid incompatibilities, we try to
|
||||
# actually load the key (and return False when this fails).
|
||||
self._load_privatekey()
|
||||
# Loading the key succeeded. Only return True when no passphrase was
|
||||
# provided.
|
||||
return self.passphrase is None
|
||||
else:
|
||||
return bool(
|
||||
cryptography.hazmat.primitives.serialization.load_pem_private_key(
|
||||
self.existing_private_key_bytes,
|
||||
None if self.passphrase is None else to_bytes(self.passphrase),
|
||||
)
|
||||
return bool(
|
||||
cryptography.hazmat.primitives.serialization.load_pem_private_key(
|
||||
self.existing_private_key_bytes,
|
||||
None if self.passphrase is None else to_bytes(self.passphrase),
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@@ -588,8 +587,8 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
|
||||
if self.format == "auto_ignore":
|
||||
return True
|
||||
try:
|
||||
format = identify_private_key_format(self.existing_private_key_bytes)
|
||||
return format == self._get_wanted_format()
|
||||
key_format = identify_private_key_format(self.existing_private_key_bytes)
|
||||
return key_format == self._get_wanted_format()
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@@ -603,16 +602,16 @@ def select_backend(module: GeneralAnsibleModule) -> PrivateKeyBackend:
|
||||
|
||||
def get_privatekey_argument_spec() -> ArgumentSpec:
|
||||
return ArgumentSpec(
|
||||
argument_spec=dict(
|
||||
size=dict(type="int", default=4096),
|
||||
type=dict(
|
||||
type="str",
|
||||
default="RSA",
|
||||
choices=["DSA", "ECC", "Ed25519", "Ed448", "RSA", "X25519", "X448"],
|
||||
),
|
||||
curve=dict(
|
||||
type="str",
|
||||
choices=[
|
||||
argument_spec={
|
||||
"size": {"type": "int", "default": 4096},
|
||||
"type": {
|
||||
"type": "str",
|
||||
"default": "RSA",
|
||||
"choices": ["DSA", "ECC", "Ed25519", "Ed448", "RSA", "X25519", "X448"],
|
||||
},
|
||||
"curve": {
|
||||
"type": "str",
|
||||
"choices": [
|
||||
"secp224r1",
|
||||
"secp256k1",
|
||||
"secp256r1",
|
||||
@@ -633,32 +632,36 @@ def get_privatekey_argument_spec() -> ArgumentSpec:
|
||||
"sect571k1",
|
||||
"sect571r1",
|
||||
],
|
||||
),
|
||||
passphrase=dict(type="str", no_log=True),
|
||||
cipher=dict(type="str", default="auto"),
|
||||
format=dict(
|
||||
type="str",
|
||||
default="auto_ignore",
|
||||
choices=["pkcs1", "pkcs8", "raw", "auto", "auto_ignore"],
|
||||
),
|
||||
format_mismatch=dict(
|
||||
type="str", default="regenerate", choices=["regenerate", "convert"]
|
||||
),
|
||||
select_crypto_backend=dict(
|
||||
type="str", choices=["auto", "cryptography"], default="auto"
|
||||
),
|
||||
regenerate=dict(
|
||||
type="str",
|
||||
default="full_idempotence",
|
||||
choices=[
|
||||
},
|
||||
"passphrase": {"type": "str", "no_log": True},
|
||||
"cipher": {"type": "str", "default": "auto"},
|
||||
"format": {
|
||||
"type": "str",
|
||||
"default": "auto_ignore",
|
||||
"choices": ["pkcs1", "pkcs8", "raw", "auto", "auto_ignore"],
|
||||
},
|
||||
"format_mismatch": {
|
||||
"type": "str",
|
||||
"default": "regenerate",
|
||||
"choices": ["regenerate", "convert"],
|
||||
},
|
||||
"select_crypto_backend": {
|
||||
"type": "str",
|
||||
"choices": ["auto", "cryptography"],
|
||||
"default": "auto",
|
||||
},
|
||||
"regenerate": {
|
||||
"type": "str",
|
||||
"default": "full_idempotence",
|
||||
"choices": [
|
||||
"never",
|
||||
"fail",
|
||||
"partial_idempotence",
|
||||
"full_idempotence",
|
||||
"always",
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
required_if=[
|
||||
("type", "ECC", ["curve"]),
|
||||
],
|
||||
|
||||
@@ -121,7 +121,7 @@ class PrivateKeyConvertBackend(metaclass=abc.ABCMeta):
|
||||
assert self.dest_private_key_bytes is not None
|
||||
|
||||
try:
|
||||
format, self.dest_private_key = self._load_private_key(
|
||||
key_format, self.dest_private_key = self._load_private_key(
|
||||
data=self.dest_private_key_bytes,
|
||||
passphrase=self.dest_passphrase,
|
||||
current_hint=self.src_private_key,
|
||||
@@ -129,7 +129,7 @@ class PrivateKeyConvertBackend(metaclass=abc.ABCMeta):
|
||||
except Exception:
|
||||
return True
|
||||
|
||||
return format != self.format or not cryptography_compare_private_keys(
|
||||
return key_format != self.format or not cryptography_compare_private_keys(
|
||||
self.dest_private_key, self.src_private_key
|
||||
)
|
||||
|
||||
@@ -141,7 +141,7 @@ class PrivateKeyConvertBackend(metaclass=abc.ABCMeta):
|
||||
# Implementation with using cryptography
|
||||
class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend):
|
||||
def __init__(self, *, module: AnsibleModule) -> None:
|
||||
super(PrivateKeyConvertCryptographyBackend, self).__init__(module=module)
|
||||
super().__init__(module=module)
|
||||
|
||||
def get_private_key_data(self) -> bytes:
|
||||
"""Return bytes for self.src_private_key in output format"""
|
||||
@@ -166,6 +166,9 @@ class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend):
|
||||
export_encoding = (
|
||||
cryptography.hazmat.primitives.serialization.Encoding.Raw
|
||||
)
|
||||
else:
|
||||
# pylint does not notice that all possible values for self.format have been covered.
|
||||
raise AssertionError("Can never be reached") # pragma: no cover
|
||||
except AttributeError:
|
||||
self.module.fail_json(
|
||||
msg=f'Cryptography backend does not support the selected output format "{self.format}"'
|
||||
@@ -208,20 +211,20 @@ class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend):
|
||||
) -> tuple[str, PrivateKeyTypes]:
|
||||
try:
|
||||
# Interpret bytes depending on format.
|
||||
format = identify_private_key_format(data)
|
||||
if format == "raw":
|
||||
key_format = identify_private_key_format(data)
|
||||
if key_format == "raw":
|
||||
if passphrase is not None:
|
||||
raise PrivateKeyError("Cannot load raw key with passphrase")
|
||||
if len(data) == 56:
|
||||
return (
|
||||
format,
|
||||
key_format,
|
||||
cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey.from_private_bytes(
|
||||
data
|
||||
),
|
||||
)
|
||||
if len(data) == 57:
|
||||
return (
|
||||
format,
|
||||
key_format,
|
||||
cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey.from_private_bytes(
|
||||
data
|
||||
),
|
||||
@@ -233,14 +236,14 @@ class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend):
|
||||
):
|
||||
try:
|
||||
return (
|
||||
format,
|
||||
key_format,
|
||||
cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.from_private_bytes(
|
||||
data
|
||||
),
|
||||
)
|
||||
except Exception:
|
||||
return (
|
||||
format,
|
||||
key_format,
|
||||
cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey.from_private_bytes(
|
||||
data
|
||||
),
|
||||
@@ -248,29 +251,29 @@ class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend):
|
||||
else:
|
||||
try:
|
||||
return (
|
||||
format,
|
||||
key_format,
|
||||
cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey.from_private_bytes(
|
||||
data
|
||||
),
|
||||
)
|
||||
except Exception:
|
||||
return (
|
||||
format,
|
||||
key_format,
|
||||
cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.from_private_bytes(
|
||||
data
|
||||
),
|
||||
)
|
||||
raise PrivateKeyError("Cannot load raw key")
|
||||
else:
|
||||
return (
|
||||
format,
|
||||
cryptography.hazmat.primitives.serialization.load_pem_private_key(
|
||||
data,
|
||||
None if passphrase is None else to_bytes(passphrase),
|
||||
),
|
||||
)
|
||||
|
||||
return (
|
||||
key_format,
|
||||
cryptography.hazmat.primitives.serialization.load_pem_private_key(
|
||||
data,
|
||||
None if passphrase is None else to_bytes(passphrase),
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
raise PrivateKeyError(e)
|
||||
raise PrivateKeyError(e) from e
|
||||
|
||||
|
||||
def select_backend(module: AnsibleModule) -> PrivateKeyConvertBackend:
|
||||
@@ -282,13 +285,17 @@ def select_backend(module: AnsibleModule) -> PrivateKeyConvertBackend:
|
||||
|
||||
def get_privatekey_argument_spec() -> ArgumentSpec:
|
||||
return ArgumentSpec(
|
||||
argument_spec=dict(
|
||||
src_path=dict(type="path"),
|
||||
src_content=dict(type="str"),
|
||||
src_passphrase=dict(type="str", no_log=True),
|
||||
dest_passphrase=dict(type="str", no_log=True),
|
||||
format=dict(type="str", required=True, choices=["pkcs1", "pkcs8", "raw"]),
|
||||
),
|
||||
argument_spec={
|
||||
"src_path": {"type": "path"},
|
||||
"src_content": {"type": "str"},
|
||||
"src_passphrase": {"type": "str", "no_log": True},
|
||||
"dest_passphrase": {"type": "str", "no_log": True},
|
||||
"format": {
|
||||
"type": "str",
|
||||
"required": True,
|
||||
"choices": ["pkcs1", "pkcs8", "raw"],
|
||||
},
|
||||
},
|
||||
mutually_exclusive=[
|
||||
["src_path", "src_content"],
|
||||
],
|
||||
|
||||
@@ -134,7 +134,7 @@ def _is_cryptography_key_consistent(
|
||||
# key._backend was removed in cryptography 42.0.0
|
||||
backend = getattr(key, "_backend", None)
|
||||
if backend is not None:
|
||||
return bool(backend._lib.RSA_check_key(key._rsa_cdata)) # type: ignore
|
||||
return bool(backend._lib.RSA_check_key(key._rsa_cdata)) # type: ignore # pylint: disable=protected-access
|
||||
if isinstance(key, cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey):
|
||||
result = _check_dsa_consistency(
|
||||
key_public_data=key_public_data, key_private_data=key_private_data
|
||||
@@ -195,19 +195,21 @@ def _is_cryptography_key_consistent(
|
||||
|
||||
class PrivateKeyConsistencyError(OpenSSLObjectError):
|
||||
def __init__(self, msg: str, *, result: dict[str, t.Any]) -> None:
|
||||
super(PrivateKeyConsistencyError, self).__init__(msg)
|
||||
super().__init__(msg)
|
||||
self.error_message = msg
|
||||
self.result = result
|
||||
|
||||
|
||||
class PrivateKeyParseError(OpenSSLObjectError):
|
||||
def __init__(self, msg: str, *, result: dict[str, t.Any]) -> None:
|
||||
super(PrivateKeyParseError, self).__init__(msg)
|
||||
super().__init__(msg)
|
||||
self.error_message = msg
|
||||
self.result = result
|
||||
|
||||
|
||||
class PrivateKeyInfoRetrieval(metaclass=abc.ABCMeta):
|
||||
key: PrivateKeyTypes
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
@@ -257,14 +259,14 @@ class PrivateKeyInfoRetrieval(metaclass=abc.ABCMeta):
|
||||
)
|
||||
result["can_parse_key"] = True
|
||||
except OpenSSLObjectError as exc:
|
||||
raise PrivateKeyParseError(str(exc), result=result)
|
||||
raise PrivateKeyParseError(str(exc), result=result) from exc
|
||||
|
||||
result["public_key"] = to_native(self._get_public_key(binary=False))
|
||||
pk = self._get_public_key(binary=True)
|
||||
result["public_key_fingerprints"] = (
|
||||
get_fingerprint_of_bytes(pk, prefer_one=prefer_one_fingerprint)
|
||||
if pk is not None
|
||||
else dict()
|
||||
else {}
|
||||
)
|
||||
|
||||
key_type, key_public_data, key_private_data = self._get_key_info(
|
||||
@@ -295,9 +297,7 @@ class PrivateKeyInfoRetrievalCryptography(PrivateKeyInfoRetrieval):
|
||||
def __init__(
|
||||
self, *, module: GeneralAnsibleModule, content: bytes, **kwargs
|
||||
) -> None:
|
||||
super(PrivateKeyInfoRetrievalCryptography, self).__init__(
|
||||
module=module, content=content, **kwargs
|
||||
)
|
||||
super().__init__(module=module, content=content, **kwargs)
|
||||
|
||||
def _get_public_key(self, *, binary: bool) -> bytes:
|
||||
return self.key.public_key().public_bytes(
|
||||
|
||||
@@ -100,7 +100,7 @@ def _get_cryptography_public_key_info(
|
||||
|
||||
class PublicKeyParseError(OpenSSLObjectError):
|
||||
def __init__(self, msg: str, *, result: dict[str, t.Any]) -> None:
|
||||
super(PublicKeyParseError, self).__init__(msg)
|
||||
super().__init__(msg)
|
||||
self.error_message = msg
|
||||
self.result = result
|
||||
|
||||
@@ -132,13 +132,13 @@ class PublicKeyInfoRetrieval(metaclass=abc.ABCMeta):
|
||||
try:
|
||||
self.key = load_publickey(content=self.content)
|
||||
except OpenSSLObjectError as e:
|
||||
raise PublicKeyParseError(str(e), result={})
|
||||
raise PublicKeyParseError(str(e), result={}) from e
|
||||
|
||||
pk = self._get_public_key(binary=True)
|
||||
result["fingerprints"] = (
|
||||
get_fingerprint_of_bytes(pk, prefer_one=prefer_one_fingerprint)
|
||||
if pk is not None
|
||||
else dict()
|
||||
else {}
|
||||
)
|
||||
|
||||
key_type, key_public_data = self._get_key_info()
|
||||
@@ -157,9 +157,7 @@ class PublicKeyInfoRetrievalCryptography(PublicKeyInfoRetrieval):
|
||||
content: bytes | None = None,
|
||||
key: PublicKeyTypes | None = None,
|
||||
) -> None:
|
||||
super(PublicKeyInfoRetrievalCryptography, self).__init__(
|
||||
module=module, content=content, key=key
|
||||
)
|
||||
super().__init__(module=module, content=content, key=key)
|
||||
|
||||
def _get_public_key(self, binary: bool) -> bytes:
|
||||
if self.key is None:
|
||||
|
||||
Reference in New Issue
Block a user