Remove Entrust modules and certificate providers (#900)

* Remove Entrust modules and certificate providers.

* Add more information on Entrust removal.

* Remove Entrust content from ignore.txt files.

* Work around bug in ansible-test.
This commit is contained in:
Felix Fontein
2025-05-22 21:08:48 +02:00
committed by GitHub
parent 41b71bb60c
commit 43ea6148df
25 changed files with 25 additions and 3119 deletions

View File

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

View File

@@ -106,6 +106,6 @@ This collection is primarily licensed and distributed as a whole under the GNU G
See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.crypto/blob/main/COPYING) for the full text. See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.crypto/blob/main/COPYING) for the full text.
Parts of the collection are licensed under the [Apache 2.0 license](https://github.com/ansible-collections/community.crypto/blob/main/LICENSES/Apache-2.0.txt) (`plugins/module_utils/_crypto/_obj2txt.py` and `plugins/module_utils/_crypto/_objects_data.py`), the [BSD 2-Clause license](https://github.com/ansible-collections/community.crypto/blob/main/LICENSES/BSD-2-Clause.txt) (`plugins/module_utils/_ecs/api.py`), the [BSD 3-Clause license](https://github.com/ansible-collections/community.crypto/blob/main/LICENSES/BSD-3-Clause.txt) (`plugins/module_utils/_crypto/_obj2txt.py`). This only applies to vendored files in ``plugins/module_utils/`` and to the ECS module utils. Parts of the collection are licensed under the [Apache 2.0 license](https://github.com/ansible-collections/community.crypto/blob/main/LICENSES/Apache-2.0.txt) (`plugins/module_utils/_crypto/_obj2txt.py` and `plugins/module_utils/_crypto/_objects_data.py`) and the [BSD 3-Clause license](https://github.com/ansible-collections/community.crypto/blob/main/LICENSES/BSD-3-Clause.txt) (`plugins/module_utils/_crypto/_obj2txt.py`). This only applies to vendored files in ``plugins/module_utils/``.
All files have a machine readable `SDPX-License-Identifier:` comment denoting its respective license(s) or an equivalent entry in an accompanying `.license` file. Only changelog fragments (which will not be part of a release) are covered by a blanket statement in `REUSE.toml`. This conforms to the [REUSE specification](https://reuse.software/spec/). All files have a machine readable `SDPX-License-Identifier:` comment denoting its respective license(s) or an equivalent entry in an accompanying `.license` file. Only changelog fragments (which will not be part of a release) are covered by a blanket statement in `REUSE.toml`. This conforms to the [REUSE specification](https://reuse.software/spec/).

View File

@@ -0,0 +1,6 @@
removed_features:
- "All Entrust content is being removed since the Entrust service in currently being sunsetted after the sale of Entrust's Public Certificates Business to Sectigo; see `the announcement with key dates <https://www.entrust.com/tls-certificate-information-center>`__ and `the migration brief for customers <https://www.sectigo.com/uploads/resources/EOL_Migration-Brief-End-Customer.pdf>`__ for details. Since this process will be completed in 2025, we decided to remove all Entrust content from community.general 3.0.0 (https://github.com/ansible-collections/community.crypto/issues/895, https://github.com/ansible-collections/community.crypto/pull/901)."
- "ecs_certificate - the module has been removed. Please use community.crypto 2.x.y if you need this module (https://github.com/ansible-collections/community.crypto/pull/900)."
- "ecs_domain - the module has been removed. Please use community.crypto 2.x.y if you need this module (https://github.com/ansible-collections/community.crypto/pull/900)."
- "x509_certificate - the ``entrust`` provider has been removed. Please use community.crypto 2.x.y if you need this provider (https://github.com/ansible-collections/community.crypto/pull/900)."
- "x509_certificate_pipe - the ``entrust`` provider has been removed. Please use community.crypto 2.x.y if you need this provider (https://github.com/ansible-collections/community.crypto/pull/900)."

View File

@@ -20,6 +20,14 @@ action_groups:
plugin_routing: plugin_routing:
modules: modules:
ecs_certificate:
tombstone:
removal_version: 3.0.0
warning_text: The 'community.crypto.ecs_certificate' module has been removed due to the upcoming sunsetting of the ECS service. Please use community.crypto 2.x.y to continue using this module
ecs_domain:
tombstone:
removal_version: 3.0.0
warning_text: The 'community.crypto.ecs_domain' module has been removed due to the upcoming sunsetting of the ECS service. Please use community.crypto 2.x.y to continue using this module.
acme_account_facts: acme_account_facts:
tombstone: tombstone:
removal_version: 2.0.0 removal_version: 2.0.0

View File

@@ -1,44 +0,0 @@
# Copyright (c), Entrust Datacard Corporation, 2019
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Note that this doc fragment is **PRIVATE** to the collection. It can have breaking changes at any time.
# Do not use this from other collections or standalone plugins/modules!
from __future__ import annotations
class ModuleDocFragment:
# Plugin options for Entrust Certificate Services (ECS) credentials
DOCUMENTATION = r"""
options:
entrust_api_user:
description:
- The username for authentication to the Entrust Certificate Services (ECS) API.
type: str
required: true
entrust_api_key:
description:
- The key (password) for authentication to the Entrust Certificate Services (ECS) API.
type: str
required: true
entrust_api_client_cert_path:
description:
- The path to the client certificate used to authenticate to the Entrust Certificate Services (ECS) API.
type: path
required: true
entrust_api_client_cert_key_path:
description:
- The path to the key for the client certificate used to authenticate to the Entrust Certificate Services (ECS) API.
type: path
required: true
entrust_api_specification_path:
description:
- The path to the specification file defining the Entrust Certificate Services (ECS) API configuration.
- You can use this to keep a local copy of the specification to avoid downloading it every time the module is used.
type: path
default: https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml
requirements:
- "PyYAML >= 3.11"
"""

View File

@@ -132,91 +132,6 @@ options:
default: https://acme-v02.api.letsencrypt.org/directory default: https://acme-v02.api.letsencrypt.org/directory
""" """
BACKEND_ENTRUST_DOCUMENTATION = r"""
options:
entrust_cert_type:
description:
- Specify the type of certificate requested.
- This is only used by the V(entrust) provider.
type: str
default: STANDARD_SSL
choices: [STANDARD_SSL, ADVANTAGE_SSL, UC_SSL, EV_SSL, WILDCARD_SSL, PRIVATE_SSL, PD_SSL, CDS_ENT_LITE, CDS_ENT_PRO, SMIME_ENT]
entrust_requester_email:
description:
- The email of the requester of the certificate (for tracking purposes).
- This is only used by the V(entrust) provider.
- This is required if the provider is V(entrust).
type: str
entrust_requester_name:
description:
- The name of the requester of the certificate (for tracking purposes).
- This is only used by the V(entrust) provider.
- This is required if the provider is V(entrust).
type: str
entrust_requester_phone:
description:
- The phone number of the requester of the certificate (for tracking purposes).
- This is only used by the V(entrust) provider.
- This is required if the provider is V(entrust).
type: str
entrust_api_user:
description:
- The username for authentication to the Entrust Certificate Services (ECS) API.
- This is only used by the V(entrust) provider.
- This is required if the provider is V(entrust).
type: str
entrust_api_key:
description:
- The key (password) for authentication to the Entrust Certificate Services (ECS) API.
- This is only used by the V(entrust) provider.
- This is required if the provider is V(entrust).
type: str
entrust_api_client_cert_path:
description:
- The path to the client certificate used to authenticate to the Entrust Certificate Services (ECS) API.
- This is only used by the V(entrust) provider.
- This is required if the provider is V(entrust).
type: path
entrust_api_client_cert_key_path:
description:
- The path to the private key of the client certificate used to authenticate to the Entrust Certificate Services (ECS) API.
- This is only used by the V(entrust) provider.
- This is required if the provider is V(entrust).
type: path
entrust_not_after:
description:
- The point in time at which the certificate stops being valid.
- Time can be specified either as relative time or as an absolute timestamp.
- A valid absolute time format is C(ASN.1 TIME) such as V(2019-06-18).
- A valid relative time format is V([+-]timespec) where timespec can be an integer + C([w | d | h | m | s]), such as V(+365d) or V(+32w1d2h)).
- Time will always be interpreted as UTC.
- Note that only the date (day, month, year) is supported for specifying the expiry date of the issued certificate.
- The full date-time is adjusted to EST (GMT -5:00) before issuance, which may result in a certificate with an expiration date one day
earlier than expected if a relative time is used.
- The minimum certificate lifetime is 90 days, and maximum is three years.
- If this value is not specified, the certificate will stop being valid 365 days the date of issue.
- This is only used by the V(entrust) provider.
- Please note that this value is B(not) covered by the O(ignore_timestamps) option.
type: str
default: +365d
entrust_api_specification_path:
description:
- The path to the specification file defining the Entrust Certificate Services (ECS) API configuration.
- You can use this to keep a local copy of the specification to avoid downloading it every time the module is used.
- This is only used by the V(entrust) provider.
type: path
default: https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml
"""
BACKEND_OWNCA_DOCUMENTATION = r""" BACKEND_OWNCA_DOCUMENTATION = r"""
description: description:
- The V(ownca) provider is intended for generating an OpenSSL certificate signed with your own - The V(ownca) provider is intended for generating an OpenSSL certificate signed with your own

View File

@@ -1,297 +0,0 @@
# Copyright (c) 2016-2017, Yanis Guenane <yanis+ansible@guenane.org>
# Copyright (c) 2017, Markus Teufelberger <mteufelberger+ansible@mgit.at>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Note that this module util is **PRIVATE** to the collection. It can have breaking changes at any time.
# Do not use this from other collections or standalone plugins/modules!
from __future__ import annotations
import datetime
import os
import typing as t
from ansible.module_utils.common.text.converters import to_bytes, to_text
from ansible_collections.community.crypto.plugins.module_utils._crypto.cryptography_support import (
CRYPTOGRAPHY_TIMEZONE,
get_not_valid_after,
)
from ansible_collections.community.crypto.plugins.module_utils._crypto.module_backends.certificate import (
CertificateBackend,
CertificateError,
CertificateProvider,
)
from ansible_collections.community.crypto.plugins.module_utils._crypto.support import (
load_certificate,
)
from ansible_collections.community.crypto.plugins.module_utils._ecs.api import (
ECSClient,
RestOperationException,
SessionConfigurationException,
)
from ansible_collections.community.crypto.plugins.module_utils._time import (
get_now_datetime,
get_relative_time_option,
)
if t.TYPE_CHECKING:
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.crypto.plugins.module_utils._argspec import (
ArgumentSpec,
)
try:
from cryptography.x509.oid import NameOID
except ImportError:
pass
class EntrustCertificateBackend(CertificateBackend):
def __init__(self, *, module: AnsibleModule) -> None:
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:
raise CertificateError(
"csr_path or csr_content is required for entrust provider"
)
if not os.path.exists(self.csr_path):
raise CertificateError(
f"The certificate signing request file {self.csr_path} does not exist"
)
self._ensure_csr_loaded()
if self.csr is None:
raise CertificateError("CSR not provided")
# ECS API defaults to using the validated organization tied to the account.
# We want to always force behavior of trying to use the organization provided in the CSR.
# To that end we need to parse out the organization from the CSR.
self.csr_org = None
csr_subject_orgs = self.csr.subject.get_attributes_for_oid(
NameOID.ORGANIZATION_NAME
)
if len(csr_subject_orgs) == 1:
self.csr_org = csr_subject_orgs[0].value
elif len(csr_subject_orgs) > 1:
self.module.fail_json(
msg=(
"Entrust provider does not currently support multiple validated organizations. Multiple organizations found in "
f"Subject DN: '{self.csr.subject}'. "
)
)
# If no organization in the CSR, explicitly tell ECS that it should be blank in issued cert, not defaulted to
# organization tied to the account.
if self.csr_org is None:
self.csr_org = ""
try:
self.ecs_client = ECSClient(
entrust_api_user=self.module.params["entrust_api_user"],
entrust_api_key=self.module.params["entrust_api_key"],
entrust_api_cert=self.module.params["entrust_api_client_cert_path"],
entrust_api_cert_key=self.module.params[
"entrust_api_client_cert_key_path"
],
entrust_api_specification_path=self.module.params[
"entrust_api_specification_path"
],
)
except SessionConfigurationException as e:
module.fail_json(msg=f"Failed to initialize Entrust Provider: {e}")
def generate_certificate(self) -> None:
"""(Re-)Generate certificate."""
body = {}
# Read the CSR that was generated for us
if self.csr_content is not None:
# csr_content contains bytes
body["csr"] = to_text(self.csr_content)
else:
assert self.csr_path is not None
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"]
# Handle expiration (30 days if not specified)
expiry = self.notAfter
if not expiry:
gmt_now = get_now_datetime(with_timezone=CRYPTOGRAPHY_TIMEZONE)
expiry = gmt_now + datetime.timedelta(days=365)
expiry_iso3339 = expiry.strftime("%Y-%m-%dT%H:%M:%S.00Z")
body["certExpiryDate"] = expiry_iso3339
body["org"] = self.csr_org
body["tracking"] = {
"requesterName": self.module.params["entrust_requester_name"],
"requesterEmail": self.module.params["entrust_requester_email"],
"requesterPhone": self.module.params["entrust_requester_phone"],
}
try:
result = self.ecs_client.NewCertRequest( # type: ignore[attr-defined] # pylint: disable=no-member
Body=body
)
self.trackingId = result.get("trackingId")
except RestOperationException as e:
self.module.fail_json(
msg=f"Failed to request new certificate from Entrust Certificate Services (ECS): {e.message}"
)
self.cert_bytes = to_bytes(result.get("endEntityCert"))
self.cert = load_certificate(
path=None,
content=self.cert_bytes,
)
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(
self,
*,
not_before: datetime.datetime | None = None,
not_after: datetime.datetime | None = None,
) -> bool:
parent_check = super().needs_regeneration()
try:
cert_details = self._get_cert_details()
except RestOperationException as e:
self.module.fail_json(
msg=f"Failed to get status of existing certificate from Entrust Certificate Services (ECS): {e.message}."
)
# Always issue a new certificate if the certificate is expired, suspended or revoked
status = cert_details.get("status", False)
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
if (
self.module.params["entrust_cert_type"]
and cert_details.get("certType")
and self.module.params["entrust_cert_type"] != cert_details.get("certType")
):
return True
return parent_check
def _get_cert_details(self) -> dict[str, t.Any]:
cert_details: dict[str, t.Any] = {}
try:
self._ensure_existing_certificate_loaded()
except Exception:
return cert_details
if self.existing_certificate:
serial_number = f"{self.existing_certificate.serial_number:X}"
expiry = get_not_valid_after(self.existing_certificate)
# get some information about the expiry of this certificate
expiry_iso3339 = expiry.strftime("%Y-%m-%dT%H:%M:%S.00Z")
cert_details["expiresAfter"] = expiry_iso3339
# If a trackingId is not already defined (from the result of a generate)
# use the serial number to identify the tracking Id
if self.trackingId is None and serial_number is not None:
cert_results = self.ecs_client.GetCertificates( # type: ignore[attr-defined] # pylint: disable=no-member
serialNumber=serial_number
).get(
"certificates", {}
)
# Finding 0 or more than 1 result is a very unlikely use case, it simply means we cannot perform additional checks
# on the 'state' as returned by Entrust Certificate Services (ECS). The general certificate validity is
# still checked as it is in the rest of the module.
if len(cert_results) == 1:
self.trackingId = cert_results[0].get("trackingId")
if self.trackingId is not None:
cert_details.update(
self.ecs_client.GetCertificate( # pylint: disable=no-member
trackingId=self.trackingId
)
)
return cert_details
class EntrustCertificateProvider(CertificateProvider):
def validate_module_args(self, module: AnsibleModule) -> None:
pass
def create_backend(self, module: AnsibleModule) -> EntrustCertificateBackend:
return EntrustCertificateBackend(module=module)
def add_entrust_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None:
argument_spec.argument_spec["provider"]["choices"].append("entrust")
argument_spec.argument_spec.update(
{
"entrust_cert_type": {
"type": "str",
"default": "STANDARD_SSL",
"choices": [
"STANDARD_SSL",
"ADVANTAGE_SSL",
"UC_SSL",
"EV_SSL",
"WILDCARD_SSL",
"PRIVATE_SSL",
"PD_SSL",
"CDS_ENT_LITE",
"CDS_ENT_PRO",
"SMIME_ENT",
],
},
"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(
(
"provider",
"entrust",
[
"entrust_requester_email",
"entrust_requester_name",
"entrust_requester_phone",
"entrust_api_user",
"entrust_api_key",
"entrust_api_client_cert_path",
"entrust_api_client_cert_key_path",
],
)
)
__all__ = (
"EntrustCertificateBackend",
"EntrustCertificateProvider",
"add_entrust_provider_to_argument_spec",
)

View File

@@ -1,421 +0,0 @@
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is licensed under the
# Modified BSD License. Modules you write using this snippet, which is embedded
# dynamically by Ansible, still belong to the author of the module, and may assign
# their own license to the complete work.
#
# Copyright (c), Entrust Datacard Corporation, 2019
# Simplified BSD License (see LICENSES/BSD-2-Clause.txt or https://opensource.org/licenses/BSD-2-Clause)
# SPDX-License-Identifier: BSD-2-Clause
# Note that this module util is **PRIVATE** to the collection. It can have breaking changes at any time.
# Do not use this from other collections or standalone plugins/modules!
from __future__ import annotations
import json
import os
import re
import traceback
import typing as t
from urllib.error import HTTPError
from urllib.parse import urlencode
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.common.text.converters import to_text
from ansible.module_utils.urls import Request
if t.TYPE_CHECKING:
_P = t.ParamSpec("_P")
YAML_IMP_ERR = None
try:
import yaml
except ImportError:
YAML_FOUND = False
YAML_IMP_ERR = traceback.format_exc()
else:
YAML_FOUND = True
valid_file_format = re.compile(r".*(\.)(yml|yaml|json)$")
def ecs_client_argument_spec() -> dict[str, t.Any]:
return {
"entrust_api_user": {"type": "str", "required": True},
"entrust_api_key": {"type": "str", "required": True, "no_log": True},
"entrust_api_client_cert_path": {"type": "path", "required": True},
"entrust_api_client_cert_key_path": {
"type": "path",
"required": True,
"no_log": True,
},
"entrust_api_specification_path": {
"type": "path",
"default": "https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml",
},
}
class SessionConfigurationException(Exception):
"""Raised if we cannot configure a session with the API"""
class RestOperationException(Exception):
"""Encapsulate a REST API error"""
def __init__(self, error: dict[str, t.Any]) -> None:
self.status = to_text(error.get("status", None))
self.errors = [to_text(err.get("message")) for err in error.get("errors", {})]
self.message = " ".join(self.errors)
def generate_docstring(operation_spec: dict[str, t.Any]) -> str:
"""Generate a docstring for an operation defined in operation_spec (swagger)"""
# Description of the operation
docs = operation_spec.get("description", "No Description")
docs += "\n\n"
# Parameters of the operation
parameters = operation_spec.get("parameters", [])
if len(parameters) != 0:
docs += "\tArguments:\n\n"
for parameter in parameters:
req = "Required" if parameter.get("required", False) else "Not Required"
docs += f"{parameter.get('name')} ({parameter.get('type', 'No Type')}:{req}): {parameter.get('description')}\n"
return docs
_T = t.TypeVar("_T")
_R = t.TypeVar("_R")
def bind(
instance: _T,
method: t.Callable[t.Concatenate[_T, _P], _R],
operation_spec: dict[str, str],
) -> t.Callable[_P, _R]:
def binding_scope_fn(*args, **kwargs) -> _R:
return method(instance, *args, **kwargs)
# Make sure we do not confuse users; add the proper name and documentation to the function.
# Users can use !help(<function>) to get help on the function from interactive python or pdb
operation_name = operation_spec["operationId"].split("Using")[0]
binding_scope_fn.__name__ = str(operation_name)
binding_scope_fn.__doc__ = generate_docstring(operation_spec)
return binding_scope_fn
class RestOperation:
def __init__(
self,
session: "ECSSession",
uri: str,
method: str,
parameters: dict | None = None,
) -> None:
self.session = session
self.method = method
if parameters is None:
self.parameters = {}
else:
self.parameters = parameters
self.url = (
f"https://{session._spec.get('host')}{session._spec.get('basePath')}{uri}"
)
def restmethod(self, *args, **kwargs) -> t.Any:
"""Do the hard work of making the request here"""
# gather named path parameters and do substitution on the URL
body_parameters: dict[str, t.Any] | None
if self.parameters:
path_parameters = {}
body_parameters = {}
query_parameters = {}
for x in self.parameters:
expected_location = x.get("in")
key_name = x.get("name", None)
key_value = kwargs.get(key_name, None)
if expected_location == "path" and key_name and key_value:
path_parameters.update({key_name: key_value})
elif expected_location == "body" and key_name and key_value:
body_parameters.update({key_name: key_value})
elif expected_location == "query" and key_name and key_value:
query_parameters.update({key_name: key_value})
if len(body_parameters.keys()) >= 1:
body_parameters = body_parameters.get(list(body_parameters.keys())[0])
else:
body_parameters = None
else:
path_parameters = {}
query_parameters = {}
body_parameters = None
# This will fail if we have not set path parameters with a KeyError
url = self.url.format(**path_parameters)
if query_parameters:
# modify the URL to add path parameters
url = url + "?" + urlencode(query_parameters)
try:
if body_parameters:
body_parameters_json = json.dumps(body_parameters)
response = self.session.request.open(
method=self.method, url=url, data=body_parameters_json
)
else:
response = self.session.request.open(method=self.method, url=url)
except HTTPError as e:
# An HTTPError has the same methods available as a valid response from request.open
response = e
# Return the result if JSON and success ({} for empty responses)
# Raise an exception if there was a failure.
try:
result_code = response.getcode()
result = json.loads(response.read())
except ValueError:
result = {}
if result or result == {}:
if result_code and result_code < 400:
return result
raise RestOperationException(result)
# Raise a generic RestOperationException if this fails
raise RestOperationException(
{"status": result_code, "errors": [{"message": "REST Operation Failed"}]}
)
class Resource:
"""Implement basic CRUD operations against a path."""
def __init__(self, session: "ECSSession") -> None:
self.session = session
self.parameters: dict[str, t.Any] = {}
for url in session._spec.get("paths").keys():
methods = session._spec.get("paths").get(url)
for method in methods.keys():
operation_spec = methods.get(method)
operation_name = operation_spec.get("operationId", None)
parameters = operation_spec.get("parameters")
if not operation_name:
if method.lower() == "post":
operation_name = "Create"
elif method.lower() == "get":
operation_name = "Get"
elif method.lower() == "put":
operation_name = "Update"
elif method.lower() == "delete":
operation_name = "Delete"
elif method.lower() == "patch":
operation_name = "Patch"
else:
raise SessionConfigurationException(
f"Invalid REST method type {method}"
)
# Get the non-parameter parts of the URL and append to the operation name
# e.g /application/version -> GetApplicationVersion
# e.g. /application/{id} -> GetApplication
# This may lead to duplicates, which we must prevent.
operation_name += (
re.sub(r"{(.*)}", "", url)
.replace("/", " ")
.title()
.replace(" ", "")
)
operation_spec["operationId"] = operation_name
op = RestOperation(session, url, method, parameters)
setattr(self, operation_name, bind(self, op.restmethod, operation_spec))
# Session to encapsulate the connection parameters of the module_utils Request object, the api spec, etc
class ECSSession:
def __init__(self, name: str, **kwargs) -> None:
"""
Initialize our session
"""
self._set_config(name, **kwargs)
def client(self) -> Resource:
resource = Resource(self)
return resource
def _set_config(self, name: str, **kwargs) -> None:
headers = {
"Content-Type": "application/json",
"Connection": "keep-alive",
}
self.request = Request(headers=headers, timeout=60)
configurators = [self._read_config_vars]
for configurator in configurators:
self._config = configurator(name, **kwargs)
if self._config:
break
if self._config is None:
raise SessionConfigurationException("No Configuration Found.")
# set up auth if passed
entrust_api_user: str | None = self.get_config("entrust_api_user")
entrust_api_key: str | None = self.get_config("entrust_api_key")
if entrust_api_user and entrust_api_key:
self.request.url_username = entrust_api_user
self.request.url_password = entrust_api_key
else:
raise SessionConfigurationException("User and key must be provided.")
# set up client certificate if passed (support all-in one or cert + key)
entrust_api_cert: str | None = self.get_config("entrust_api_cert")
entrust_api_cert_key: str | None = self.get_config("entrust_api_cert_key")
if entrust_api_cert:
self.request.client_cert = entrust_api_cert
if entrust_api_cert_key:
self.request.client_key = entrust_api_cert_key
else:
raise SessionConfigurationException(
"Client certificate for authentication to the API must be provided."
)
# set up the spec
entrust_api_specification_path = self.get_config(
"entrust_api_specification_path"
)
if not isinstance(entrust_api_specification_path, str):
raise SessionConfigurationException(
"entrust_api_specification_path must be a string."
)
if not entrust_api_specification_path.startswith("http") and not os.path.isfile(
entrust_api_specification_path
):
raise SessionConfigurationException(
f"OpenAPI specification was not found at location {entrust_api_specification_path}."
)
if not valid_file_format.match(entrust_api_specification_path):
raise SessionConfigurationException(
"OpenAPI specification filename must end in .json, .yml or .yaml"
)
self.verify = True
if entrust_api_specification_path.startswith("http"):
try:
http_response = Request().open(
method="GET", url=entrust_api_specification_path
)
http_response_contents = http_response.read()
if entrust_api_specification_path.endswith(".json"):
self._spec = json.load(http_response_contents)
elif entrust_api_specification_path.endswith(
".yml"
) or entrust_api_specification_path.endswith(".yaml"):
self._spec = yaml.safe_load(http_response_contents)
except HTTPError as e:
raise SessionConfigurationException(
f"Error downloading specification from address '{entrust_api_specification_path}', received error code '{e.getcode()}'"
) from e
else:
with open(entrust_api_specification_path, "rb") as f:
if ".json" in entrust_api_specification_path:
self._spec = json.load(f)
elif (
".yml" in entrust_api_specification_path
or ".yaml" in entrust_api_specification_path
):
self._spec = yaml.safe_load(f)
def get_config(self, item: str) -> t.Any | None:
return self._config.get(item, None)
def _read_config_vars(self, name: str, **kwargs) -> dict[str, t.Any]:
"""Read configuration from variables passed to the module."""
config = {}
entrust_api_specification_path = kwargs.get("entrust_api_specification_path")
if not entrust_api_specification_path or (
not entrust_api_specification_path.startswith("http")
and not os.path.isfile(entrust_api_specification_path)
):
raise SessionConfigurationException(
f"Parameter provided for entrust_api_specification_path of value '{entrust_api_specification_path}'"
" was not a valid file path or HTTPS address."
)
for required_file in ["entrust_api_cert", "entrust_api_cert_key"]:
file_path = kwargs.get(required_file)
if not file_path or not os.path.isfile(file_path):
raise SessionConfigurationException(
f"Parameter provided for {required_file} of value '{file_path}' was not a valid file path."
)
for required_var in ["entrust_api_user", "entrust_api_key"]:
if not kwargs.get(required_var):
raise SessionConfigurationException(
f"Parameter provided for {required_var} was missing."
)
config["entrust_api_cert"] = kwargs.get("entrust_api_cert")
config["entrust_api_cert_key"] = kwargs.get("entrust_api_cert_key")
config["entrust_api_specification_path"] = kwargs.get(
"entrust_api_specification_path"
)
config["entrust_api_user"] = kwargs.get("entrust_api_user")
config["entrust_api_key"] = kwargs.get("entrust_api_key")
return config
def ECSClient(
entrust_api_user: str | None = None,
entrust_api_key: str | None = None,
entrust_api_cert: str | None = None,
entrust_api_cert_key: str | None = None,
entrust_api_specification_path: str | None = None,
) -> Resource:
"""Create an ECS client"""
if not YAML_FOUND:
raise SessionConfigurationException(
missing_required_lib("PyYAML") # TODO: pass `exception=YAML_IMP_ERR`
)
if entrust_api_specification_path is None:
entrust_api_specification_path = (
"https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml"
)
# Not functionally necessary with current uses of this module_util, but better to be explicit for future use cases
entrust_api_user = to_text(entrust_api_user)
entrust_api_key = to_text(entrust_api_key)
entrust_api_cert_key = to_text(entrust_api_cert_key)
entrust_api_specification_path = to_text(entrust_api_specification_path)
return ECSSession(
"ecs",
entrust_api_user=entrust_api_user,
entrust_api_key=entrust_api_key,
entrust_api_cert=entrust_api_cert,
entrust_api_cert_key=entrust_api_cert_key,
entrust_api_specification_path=entrust_api_specification_path,
).client()
__all__ = (
"ecs_client_argument_spec",
"SessionConfigurationException",
"RestOperationException",
"ECSClient",
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,481 +0,0 @@
#!/usr/bin/python
# Copyright 2019 Entrust Datacard Corporation.
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
DOCUMENTATION = r"""
module: ecs_domain
author:
- Chris Trufan (@ctrufan)
version_added: '1.0.0'
short_description: Request validation of a domain with the Entrust Certificate Services (ECS) API
description:
- Request validation or re-validation of a domain with the Entrust Certificate Services (ECS) API.
- Requires credentials for the L(Entrust Certificate Services,https://www.entrustdatacard.com/products/categories/ssl-certificates)
(ECS) API.
- If the domain is already in the validation process, no new validation will be requested, but the validation data (if applicable)
will be returned.
- If the domain is already in the validation process but the O(verification_method) specified is different than the current
O(verification_method), the O(verification_method) will be updated and validation data (if applicable) will be returned.
- If the domain is an active, validated domain, the return value of C(changed) will be false, unless RV(domain_status=EXPIRED),
in which case a re-validation will be performed.
- If O(verification_method=dns), details about the required DNS entry will be specified in the return parameters RV(dns_contents),
RV(dns_location), and RV(dns_resource_type).
- If O(verification_method=web_server), details about the required file details will be specified in the return parameters
RV(file_contents) and RV(file_location).
- If O(verification_method=email), the email address(es) that the validation email(s) were sent to will be in the return
parameter RV(emails). This is purely informational. For domains requested using this module, this will always be a list
of size 1.
notes:
- There is a small delay (typically about 5 seconds, but can be as long as 60 seconds) before obtaining the random values
when requesting a validation while O(verification_method=dns) or O(verification_method=web_server). Be aware of that if
doing many domain validation requests.
extends_documentation_fragment:
- community.crypto._attributes
- community.crypto._ecs_credential
attributes:
check_mode:
support: none
diff_mode:
support: none
idempotent:
support: partial
details:
- Under which conditions the module is idempotent still needs to be determined.
If you are using this module and have more information, please contribute to
the documentation!
options:
client_id:
description:
- The client ID to request the domain be associated with.
- If no client ID is specified, the domain will be added under the primary client with ID of 1.
type: int
default: 1
domain_name:
description:
- The domain name to be verified or reverified.
type: str
required: true
verification_method:
description:
- The verification method to be used to prove control of the domain.
- If O(verification_method=email) and the value O(verification_email) is specified, that value is used for the email
validation. If O(verification_email) is not provided, the first value present in WHOIS data will be used. An email
will be sent to the address in O(verification_email) with instructions on how to verify control of the domain.
- If O(verification_method=dns), the value RV(dns_contents) must be stored in location RV(dns_location), with a DNS
record type of RV(dns_resource_type). To prove domain ownership, update your DNS records so the text string returned
by RV(dns_contents) is available at RV(dns_location).
- If O(verification_method=web_server), the contents of return value RV(file_contents) must be made available on a web
server accessible at location RV(file_location).
- If O(verification_method=manual), the domain will be validated with a manual process. This is not recommended.
type: str
choices: ['dns', 'email', 'manual', 'web_server']
required: true
verification_email:
description:
- Email address to be used to verify domain ownership.
- 'Email address must be either an email address present in the WHOIS data for O(domain_name), or one of the following
constructed emails: admin@O(domain_name), administrator@O(domain_name), webmaster@O(domain_name), hostmaster@O(domain_name),
postmaster@O(domain_name).'
- Note that if O(domain_name) includes subdomains, the top level domain should be used. For example, if requesting validation
of example1.ansible.com, or test.example2.ansible.com, and you want to use the "admin" preconstructed name, the email
address should be admin@ansible.com.
- If using the email values from the WHOIS data for the domain or its top level namespace, they must be exact matches.
- If O(verification_method=email) but O(verification_email) is not provided, the first email address found in WHOIS
data for the domain will be used.
- To verify domain ownership, domain owner must follow the instructions in the email they receive.
- Only allowed if O(verification_method=email).
type: str
seealso:
- module: community.crypto.x509_certificate
description: Can be used to request certificates from ECS, with O(community.crypto.x509_certificate#module:provider=entrust).
- module: community.crypto.ecs_certificate
description: Can be used to request a Certificate from ECS using a verified domain.
"""
EXAMPLES = r"""
---
- name: Request domain validation using email validation for client ID of 2.
community.crypto.ecs_domain:
domain_name: ansible.com
client_id: 2
verification_method: email
verification_email: admin@ansible.com
entrust_api_user: apiusername
entrust_api_key: a^lv*32!cd9LnT
entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
- name: Request domain validation using DNS. If domain is already valid, request revalidation if expires within 90 days
community.crypto.ecs_domain:
domain_name: ansible.com
verification_method: dns
entrust_api_user: apiusername
entrust_api_key: a^lv*32!cd9LnT
entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
- name: Request domain validation using web server validation, and revalidate if fewer than 60 days remaining of EV eligibility.
community.crypto.ecs_domain:
domain_name: ansible.com
verification_method: web_server
entrust_api_user: apiusername
entrust_api_key: a^lv*32!cd9LnT
entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
- name: Request domain validation using manual validation.
community.crypto.ecs_domain:
domain_name: ansible.com
verification_method: manual
entrust_api_user: apiusername
entrust_api_key: a^lv*32!cd9LnT
entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
"""
RETURN = r"""
domain_status:
description: Status of the current domain. Will be one of V(APPROVED), V(DECLINED), V(CANCELLED), V(INITIAL_VERIFICATION),
V(DECLINED), V(CANCELLED), V(RE_VERIFICATION), V(EXPIRED), V(EXPIRING).
returned: changed or success
type: str
sample: APPROVED
verification_method:
description: Verification method used to request the domain validation. If C(changed) will be the same as O(verification_method)
input parameter.
returned: changed or success
type: str
sample: dns
file_location:
description: The location that ECS will be expecting to be able to find the file for domain verification, containing the
contents of RV(file_contents).
returned: O(verification_method) is V(web_server)
type: str
sample: http://ansible.com/.well-known/pki-validation/abcd.txt
file_contents:
description: The contents of the file that ECS will be expecting to find at RV(file_location).
returned: O(verification_method) is V(web_server)
type: str
sample: AB23CD41432522FF2526920393982FAB
emails:
description:
- The list of emails used to request validation of this domain.
- Domains requested using this module will only have a list of size 1.
returned: O(verification_method) is V(email)
type: list
sample: [admin@ansible.com, administrator@ansible.com]
dns_location:
description: The location that ECS will be expecting to be able to find the DNS entry for domain verification, containing
the contents of RV(dns_contents).
returned: changed and if O(verification_method) is V(dns)
type: str
sample: _pki-validation.ansible.com
dns_contents:
description: The value that ECS will be expecting to find in the DNS record located at RV(dns_location).
returned: changed and if O(verification_method) is V(dns)
type: str
sample: AB23CD41432522FF2526920393982FAB
dns_resource_type:
description: The type of resource record that ECS will be expecting for the DNS record located at RV(dns_location).
returned: changed and if O(verification_method) is V(dns)
type: str
sample: TXT
client_id:
description: Client ID that the domain belongs to. If the input value O(client_id) is specified, this will always be the
same as O(client_id).
returned: changed or success
type: int
sample: 1
ov_eligible:
description: Whether the domain is eligible for submission of "OV" certificates. Will never be V(false) if RV(ev_eligible)
is V(true).
returned: success and RV(domain_status) is V(APPROVED), V(RE_VERIFICATION), V(EXPIRING), or V(EXPIRED).
type: bool
sample: true
ov_days_remaining:
description: The number of days the domain remains eligible for submission of "OV" certificates. Will never be less than
the value of RV(ev_days_remaining).
returned: success and RV(ov_eligible) is V(true) and RV(domain_status) is V(APPROVED), V(RE_VERIFICATION) or V(EXPIRING).
type: int
sample: 129
ev_eligible:
description: Whether the domain is eligible for submission of "EV" certificates. Will never be V(true) if RV(ov_eligible)
is V(false).
returned: success and RV(domain_status) is V(APPROVED), V(RE_VERIFICATION) or V(EXPIRING), or V(EXPIRED).
type: bool
sample: true
ev_days_remaining:
description: The number of days the domain remains eligible for submission of "EV" certificates. Will never be greater than
the value of RV(ov_days_remaining).
returned: success and RV(ev_eligible) is V(true) and RV(domain_status) is V(APPROVED), V(RE_VERIFICATION) or V(EXPIRING).
type: int
sample: 94
"""
import datetime
import time
import typing as t
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.crypto.plugins.module_utils._ecs.api import (
ECSClient,
RestOperationException,
SessionConfigurationException,
ecs_client_argument_spec,
)
@t.overload
def calculate_days_remaining(expiry_date: str) -> int: ...
@t.overload
def calculate_days_remaining(expiry_date: str | None) -> int | None: ...
def calculate_days_remaining(expiry_date: str | None) -> int | None:
days_remaining = None
if expiry_date is not None:
expiry_datetime = datetime.datetime.strptime(expiry_date, "%Y-%m-%dT%H:%M:%SZ")
days_remaining = (expiry_datetime - datetime.datetime.now()).days
return days_remaining
class EcsDomain:
"""
Entrust Certificate Services domain class.
"""
def __init__(self, module: AnsibleModule) -> None:
self.changed = False
self.domain_status = None
self.verification_method = None
self.file_location = None
self.file_contents = None
self.dns_location = None
self.dns_contents = None
self.dns_resource_type = None
self.emails = None
self.ov_eligible: bool | None = None
self.ov_days_remaining: int | None = None
self.ev_eligible: bool | None = None
self.ev_days_remaining: int | None = None
# Note that verification_method is the 'current' verification
# method of the domain, we'll use module.params when requesting a new
# one, in case the verification method has changed.
self.verification_method = None
self.client_id: str | None = None
# Instantiate the ECS client and then try a no-op connection to verify credentials are valid
try:
self.ecs_client = ECSClient(
entrust_api_user=module.params["entrust_api_user"],
entrust_api_key=module.params["entrust_api_key"],
entrust_api_cert=module.params["entrust_api_client_cert_path"],
entrust_api_cert_key=module.params["entrust_api_client_cert_key_path"],
entrust_api_specification_path=module.params[
"entrust_api_specification_path"
],
)
except SessionConfigurationException as e:
module.fail_json(msg=f"Failed to initialize Entrust Provider: {e}")
try:
self.ecs_client.GetAppVersion() # type: ignore[attr-defined] # pylint: disable=no-member
except RestOperationException as e:
module.fail_json(
msg=f"Please verify credential information. Received exception when testing ECS connection: {e.message}"
)
def set_domain_details(self, domain_details: dict[str, t.Any]) -> None:
if domain_details.get("verificationMethod"):
self.verification_method = domain_details["verificationMethod"].lower()
self.domain_status = domain_details["verificationStatus"]
self.ov_eligible = domain_details.get("ovEligible")
self.ov_days_remaining = calculate_days_remaining(
domain_details.get("ovExpiry")
)
self.ev_eligible = domain_details.get("evEligible")
self.ev_days_remaining = calculate_days_remaining(
domain_details.get("evExpiry")
)
self.client_id = domain_details["clientId"]
if self.verification_method == "dns" and domain_details.get("dnsMethod"):
self.dns_location = domain_details["dnsMethod"]["recordDomain"]
self.dns_resource_type = domain_details["dnsMethod"]["recordType"]
self.dns_contents = domain_details["dnsMethod"]["recordValue"]
elif self.verification_method == "web_server" and domain_details.get(
"webServerMethod"
):
self.file_location = domain_details["webServerMethod"]["fileLocation"]
self.file_contents = domain_details["webServerMethod"]["fileContents"]
elif self.verification_method == "email" and domain_details.get("emailMethod"):
self.emails = domain_details["emailMethod"]
def check(self, module: AnsibleModule) -> bool:
try:
domain_details = self.ecs_client.GetDomain( # type: ignore[attr-defined] # pylint: disable=no-member
clientId=module.params["client_id"], domain=module.params["domain_name"]
)
self.set_domain_details(domain_details)
if self.domain_status not in (
"APPROVED",
"INITIAL_VERIFICATION",
"RE_VERIFICATION",
):
return False
# If domain verification is in process, we want to return the random values and treat it as a valid.
if self.domain_status in ("INITIAL_VERIFICATION", "RE_VERIFICATION"):
# Unless the verification method has changed, in which case we need to do a reverify request.
if self.verification_method != module.params["verification_method"]:
return False
if self.domain_status == "EXPIRING":
return False
return True
except RestOperationException:
return False
def request_domain(self, module: AnsibleModule) -> None:
if not self.check(module):
body = {}
body["verificationMethod"] = module.params["verification_method"].upper()
if module.params["verification_method"] == "email":
emailMethod = {}
if module.params["verification_email"]:
emailMethod["emailSource"] = "SPECIFIED"
emailMethod["email"] = module.params["verification_email"]
else:
emailMethod["emailSource"] = "INCLUDE_WHOIS"
body["emailMethod"] = emailMethod
# Only populate domain name in body if it is not an existing domain
if not self.domain_status:
body["domainName"] = module.params["domain_name"]
try:
if not self.domain_status:
self.ecs_client.AddDomain( # type: ignore[attr-defined] # pylint: disable=no-member
clientId=module.params["client_id"], Body=body
)
else:
self.ecs_client.ReverifyDomain( # type: ignore[attr-defined] # pylint: disable=no-member
clientId=module.params["client_id"],
domain=module.params["domain_name"],
Body=body,
)
time.sleep(5)
result = self.ecs_client.GetDomain( # type: ignore[attr-defined] # pylint: disable=no-member
clientId=module.params["client_id"],
domain=module.params["domain_name"],
)
# It takes a bit of time before the random values are available
if (
module.params["verification_method"] == "dns"
or module.params["verification_method"] == "web_server"
):
for _i in range(4):
# Check both that random values are now available, and that they're different than were populated by previous 'check'
if module.params["verification_method"] == "dns":
if (
result.get("dnsMethod")
and result["dnsMethod"]["recordValue"]
!= self.dns_contents
):
break
elif module.params["verification_method"] == "web_server":
if (
result.get("webServerMethod")
and result["webServerMethod"]["fileContents"]
!= self.file_contents
):
break
time.sleep(10)
result = self.ecs_client.GetDomain( # type: ignore[attr-defined] # pylint: disable=no-member
clientId=module.params["client_id"],
domain=module.params["domain_name"],
)
self.changed = True
self.set_domain_details(result)
except RestOperationException as e:
module.fail_json(
msg=f"Failed to request domain validation from Entrust (ECS) {e.message}"
)
def dump(self) -> dict[str, t.Any]:
result: dict[str, t.Any] = {
"changed": self.changed,
"client_id": self.client_id,
"domain_status": self.domain_status,
}
if self.verification_method:
result["verification_method"] = self.verification_method
if self.ov_eligible is not None:
result["ov_eligible"] = self.ov_eligible
if self.ov_days_remaining:
result["ov_days_remaining"] = self.ov_days_remaining
if self.ev_eligible is not None:
result["ev_eligible"] = self.ev_eligible
if self.ev_days_remaining:
result["ev_days_remaining"] = self.ev_days_remaining
if self.emails:
result["emails"] = self.emails
if self.verification_method == "dns":
result["dns_location"] = self.dns_location
result["dns_contents"] = self.dns_contents
result["dns_resource_type"] = self.dns_resource_type
elif self.verification_method == "web_server":
result["file_location"] = self.file_location
result["file_contents"] = self.file_contents
elif self.verification_method == "email":
result["emails"] = self.emails
return result
def ecs_domain_argument_spec() -> dict[str, dict[str, t.Any]]:
return {
"client_id": {"type": "int", "default": 1},
"domain_name": {"type": "str", "required": True},
"verification_method": {
"type": "str",
"required": True,
"choices": ["dns", "email", "manual", "web_server"],
},
"verification_email": {"type": "str"},
}
def main() -> t.NoReturn:
ecs_argument_spec = ecs_client_argument_spec()
ecs_argument_spec.update(ecs_domain_argument_spec())
module = AnsibleModule(
argument_spec=ecs_argument_spec,
supports_check_mode=False,
)
if (
module.params["verification_email"]
and module.params["verification_method"] != "email"
):
module.fail_json(
msg=f'The verification_email field is invalid when verification_method="{module.params["verification_method"]}".'
)
domain = EcsDomain(module)
domain.request_domain(module)
result = domain.dump()
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -11,7 +11,7 @@ DOCUMENTATION = r"""
module: x509_certificate module: x509_certificate
short_description: Generate and/or check OpenSSL certificates short_description: Generate and/or check OpenSSL certificates
description: description:
- It implements a notion of provider (one of V(selfsigned), V(ownca), V(acme), and V(entrust)) for your certificate. - It implements a notion of provider (one of V(selfsigned), V(ownca), and V(acme)) for your certificate.
- Please note that the module regenerates existing certificate if it does not match the module's options, or if it seems - Please note that the module regenerates existing certificate if it does not match the module's options, or if it seems
to be corrupt. If you are concerned that this could overwrite your existing certificate, consider using the O(backup) to be corrupt. If you are concerned that this could overwrite your existing certificate, consider using the O(backup)
option. option.
@@ -29,7 +29,6 @@ extends_documentation_fragment:
- community.crypto._attributes.files - community.crypto._attributes.files
- community.crypto._module_certificate - community.crypto._module_certificate
- community.crypto._module_certificate.backend_acme_documentation - community.crypto._module_certificate.backend_acme_documentation
- community.crypto._module_certificate.backend_entrust_documentation
- community.crypto._module_certificate.backend_ownca_documentation - community.crypto._module_certificate.backend_ownca_documentation
- community.crypto._module_certificate.backend_selfsigned_documentation - community.crypto._module_certificate.backend_selfsigned_documentation
attributes: attributes:
@@ -56,11 +55,10 @@ options:
- Name of the provider to use to generate/retrieve the OpenSSL certificate. Please see the examples on how to emulate - Name of the provider to use to generate/retrieve the OpenSSL certificate. Please see the examples on how to emulate
it with M(community.crypto.x509_certificate_info), M(community.crypto.openssl_csr_info), M(community.crypto.openssl_privatekey_info) it with M(community.crypto.x509_certificate_info), M(community.crypto.openssl_csr_info), M(community.crypto.openssl_privatekey_info)
and M(ansible.builtin.assert). and M(ansible.builtin.assert).
- The V(entrust) provider was added for Ansible 2.9 and requires credentials for the
L(Entrust Certificate Services,https://www.entrustdatacard.com/products/categories/ssl-certificates) (ECS) API.
- Required if O(state) is V(present). - Required if O(state) is V(present).
- The V(entrust) provider has been removed from community.crypto 3.0.0 due to sunsetting of the ECS API.
type: str type: str
choices: [acme, entrust, ownca, selfsigned] choices: [acme, ownca, selfsigned]
return_content: return_content:
description: description:
@@ -125,21 +123,6 @@ EXAMPLES = r"""
acme_challenge_path: /etc/ssl/challenges/ansible.com/ acme_challenge_path: /etc/ssl/challenges/ansible.com/
force: true force: true
- name: Generate an Entrust certificate via the Entrust Certificate Services (ECS) API
community.crypto.x509_certificate:
path: /etc/ssl/crt/ansible.com.crt
csr_path: /etc/ssl/csr/ansible.com.csr
provider: entrust
entrust_requester_name: Jo Doe
entrust_requester_email: jdoe@ansible.com
entrust_requester_phone: 555-555-5555
entrust_cert_type: STANDARD_SSL
entrust_api_user: apiusername
entrust_api_key: a^lv*32!cd9LnT
entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-key.crt
entrust_api_specification_path: /etc/ssl/entrust/api-docs/cms-api-2.1.0.yaml
# The following example shows how to emulate the behavior of the removed # The following example shows how to emulate the behavior of the removed
# "assertonly" provider with the x509_certificate_info, openssl_csr_info, # "assertonly" provider with the x509_certificate_info, openssl_csr_info,
# openssl_privatekey_info and assert modules: # openssl_privatekey_info and assert modules:
@@ -237,10 +220,6 @@ from ansible_collections.community.crypto.plugins.module_utils._crypto.module_ba
AcmeCertificateProvider, AcmeCertificateProvider,
add_acme_provider_to_argument_spec, add_acme_provider_to_argument_spec,
) )
from ansible_collections.community.crypto.plugins.module_utils._crypto.module_backends.certificate_entrust import (
EntrustCertificateProvider,
add_entrust_provider_to_argument_spec,
)
from ansible_collections.community.crypto.plugins.module_utils._crypto.module_backends.certificate_ownca import ( from ansible_collections.community.crypto.plugins.module_utils._crypto.module_backends.certificate_ownca import (
OwnCACertificateProvider, OwnCACertificateProvider,
add_ownca_provider_to_argument_spec, add_ownca_provider_to_argument_spec,
@@ -362,7 +341,6 @@ class GenericCertificate(OpenSSLObject):
def main() -> t.NoReturn: def main() -> t.NoReturn:
argument_spec = get_certificate_argument_spec() argument_spec = get_certificate_argument_spec()
add_acme_provider_to_argument_spec(argument_spec) add_acme_provider_to_argument_spec(argument_spec)
add_entrust_provider_to_argument_spec(argument_spec)
add_ownca_provider_to_argument_spec(argument_spec) add_ownca_provider_to_argument_spec(argument_spec)
add_selfsigned_provider_to_argument_spec(argument_spec) add_selfsigned_provider_to_argument_spec(argument_spec)
argument_spec.argument_spec.update( argument_spec.argument_spec.update(
@@ -407,12 +385,10 @@ def main() -> t.NoReturn:
provider_map: dict[ provider_map: dict[
str, str,
type[AcmeCertificateProvider] type[AcmeCertificateProvider]
| type[EntrustCertificateProvider]
| type[OwnCACertificateProvider] | type[OwnCACertificateProvider]
| type[SelfSignedCertificateProvider], | type[SelfSignedCertificateProvider],
] = { ] = {
"acme": AcmeCertificateProvider, "acme": AcmeCertificateProvider,
"entrust": EntrustCertificateProvider,
"ownca": OwnCACertificateProvider, "ownca": OwnCACertificateProvider,
"selfsigned": SelfSignedCertificateProvider, "selfsigned": SelfSignedCertificateProvider,
} }

View File

@@ -13,7 +13,7 @@ module: x509_certificate_pipe
short_description: Generate and/or check OpenSSL certificates short_description: Generate and/or check OpenSSL certificates
version_added: 1.3.0 version_added: 1.3.0
description: description:
- It implements a notion of provider (one of V(selfsigned), V(ownca), V(entrust)) for your certificate. - It implements a notion of provider (one of V(selfsigned) and V(ownca)) for your certificate.
author: author:
- Yanis Guenane (@Spredzy) - Yanis Guenane (@Spredzy)
- Markus Teufelberger (@MarkusTeufelberger) - Markus Teufelberger (@MarkusTeufelberger)
@@ -21,7 +21,6 @@ author:
extends_documentation_fragment: extends_documentation_fragment:
- community.crypto._attributes - community.crypto._attributes
- community.crypto._module_certificate - community.crypto._module_certificate
- community.crypto._module_certificate.backend_entrust_documentation
- community.crypto._module_certificate.backend_ownca_documentation - community.crypto._module_certificate.backend_ownca_documentation
- community.crypto._module_certificate.backend_selfsigned_documentation - community.crypto._module_certificate.backend_selfsigned_documentation
attributes: attributes:
@@ -33,10 +32,9 @@ options:
provider: provider:
description: description:
- Name of the provider to use to generate/retrieve the OpenSSL certificate. - Name of the provider to use to generate/retrieve the OpenSSL certificate.
- The V(entrust) provider requires credentials for the L(Entrust Certificate Services, - The V(entrust) provider has been removed from community.crypto 3.0.0 due to sunsetting of the ECS API.
https://www.entrustdatacard.com/products/categories/ssl-certificates) (ECS) API.
type: str type: str
choices: [entrust, ownca, selfsigned] choices: [ownca, selfsigned]
required: true required: true
content: content:
@@ -127,10 +125,6 @@ from ansible_collections.community.crypto.plugins.module_utils._crypto.module_ba
get_certificate_argument_spec, get_certificate_argument_spec,
select_backend, select_backend,
) )
from ansible_collections.community.crypto.plugins.module_utils._crypto.module_backends.certificate_entrust import (
EntrustCertificateProvider,
add_entrust_provider_to_argument_spec,
)
from ansible_collections.community.crypto.plugins.module_utils._crypto.module_backends.certificate_ownca import ( from ansible_collections.community.crypto.plugins.module_utils._crypto.module_backends.certificate_ownca import (
OwnCACertificateProvider, OwnCACertificateProvider,
add_ownca_provider_to_argument_spec, add_ownca_provider_to_argument_spec,
@@ -178,7 +172,6 @@ class GenericCertificate:
def main() -> t.NoReturn: def main() -> t.NoReturn:
argument_spec = get_certificate_argument_spec() argument_spec = get_certificate_argument_spec()
argument_spec.argument_spec["provider"]["required"] = True argument_spec.argument_spec["provider"]["required"] = True
add_entrust_provider_to_argument_spec(argument_spec)
add_ownca_provider_to_argument_spec(argument_spec) add_ownca_provider_to_argument_spec(argument_spec)
add_selfsigned_provider_to_argument_spec(argument_spec) add_selfsigned_provider_to_argument_spec(argument_spec)
argument_spec.argument_spec.update( argument_spec.argument_spec.update(
@@ -194,11 +187,8 @@ def main() -> t.NoReturn:
provider = module.params["provider"] provider = module.params["provider"]
provider_map: dict[ provider_map: dict[
str, str,
type[EntrustCertificateProvider] type[OwnCACertificateProvider] | type[SelfSignedCertificateProvider],
| type[OwnCACertificateProvider]
| type[SelfSignedCertificateProvider],
] = { ] = {
"entrust": EntrustCertificateProvider,
"ownca": OwnCACertificateProvider, "ownca": OwnCACertificateProvider,
"selfsigned": SelfSignedCertificateProvider, "selfsigned": SelfSignedCertificateProvider,
} }

View File

@@ -1,19 +0,0 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Not enabled due to lack of access to test environments. May be enabled using custom integration_config.yml
# Example integation_config.yml
# ---
# entrust_api_user:
# entrust_api_key:
# entrust_api_client_cert_path: /var/integration-testing/publicCert.pem
# entrust_api_client_cert_key_path: /var/integration-testing/privateKey.pem
# entrust_api_ip_address: 127.0.0.1
# entrust_cloud_ip_address: 127.0.0.1
# # Used for certificate path validation of QA environments - we chose not to support disabling path validation ever.
# cacerts_bundle_path_local: /var/integration-testing/cacerts
### WARNING: This test will update HOSTS file and CERTIFICATE STORE of target host, in order to be able to validate
# to a QA environment. ###
unsupported

View File

@@ -1,6 +0,0 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# defaults file for test_ecs_certificate

View File

@@ -1,8 +0,0 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
dependencies:
- prepare_tests
- setup_openssl

View File

@@ -1,222 +0,0 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
## Verify that integration_config was specified
- assert:
that:
- entrust_api_user is defined
- entrust_api_key is defined
- entrust_api_ip_address is defined
- entrust_cloud_ip_address is defined
- entrust_api_client_cert_path is defined or entrust_api_client_cert_contents is defined
- entrust_api_client_cert_key_path is defined or entrust_api_client_cert_key_contents
- cacerts_bundle_path_local is defined
## SET UP TEST ENVIRONMENT ########################################################################
- name: copy the files needed for verifying test server certificate to the host
copy:
src: '{{ cacerts_bundle_path_local }}/'
dest: '{{ cacerts_bundle_path }}'
- name: Update the CA certificates for our QA certs (collection may need updating if new QA environments used)
command: c_rehash {{ cacerts_bundle_path }}
- name: Update hosts file
lineinfile:
path: /etc/hosts
state: present
regexp: 'api.entrust.net$'
line: '{{ entrust_api_ip_address }} api.entrust.net'
- name: Update hosts file
lineinfile:
path: /etc/hosts
state: present
regexp: 'cloud.entrust.net$'
line: '{{ entrust_cloud_ip_address }} cloud.entrust.net'
- name: Clear out the temporary directory for storing the API connection information
file:
path: '{{ tmpdir_path }}'
state: absent
- name: Create a directory for storing the API connection Information
file:
path: '{{ tmpdir_path }}'
state: directory
- name: Copy the files needed for the connection to entrust API to the host
copy:
src: '{{ entrust_api_client_cert_path }}'
dest: '{{ entrust_api_cert }}'
- name: Copy the files needed for the connection to entrust API to the host
copy:
src: '{{ entrust_api_client_cert_key_path }}'
dest: '{{ entrust_api_cert_key }}'
## SETUP CSR TO REQUEST
- name: Generate a 2048 bit RSA private key
openssl_privatekey:
path: '{{ privatekey_path }}'
passphrase: '{{ privatekey_passphrase }}'
type: RSA
size: 2048
- name: Generate a certificate signing request using the generated key
openssl_csr:
path: '{{ csr_path }}'
privatekey_path: '{{ privatekey_path }}'
privatekey_passphrase: '{{ privatekey_passphrase }}'
common_name: '{{ common_name }}'
organization_name: '{{ organization_name | default(omit) }}'
organizational_unit_name: '{{ organizational_unit_name | default(omit) }}'
country_name: '{{ country_name | default(omit) }}'
state_or_province_name: '{{ state_or_province_name | default(omit) }}'
digest: sha256
- block:
- name: Have ECS generate a signed certificate
ecs_certificate:
backup: true
path: '{{ example1_cert_path }}'
full_chain_path: '{{ example1_chain_path }}'
csr: '{{ csr_path }}'
cert_type: '{{ example1_cert_type }}'
requester_name: '{{ entrust_requester_name }}'
requester_email: '{{ entrust_requester_email }}'
requester_phone: '{{ entrust_requester_phone }}'
entrust_api_user: '{{ entrust_api_user }}'
entrust_api_key: '{{ entrust_api_key }}'
entrust_api_client_cert_path: '{{ entrust_api_cert }}'
entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
register: example1_result
- assert:
that:
- example1_result is not failed
- example1_result.changed
- example1_result.tracking_id > 0
- example1_result.serial_number is string
# Internal CA refuses to issue certificates with the same DN in a short time frame
- name: Sleep for 5 seconds so we don't run into duplicate-request errors
pause:
seconds: 5
- name: Attempt to have ECS generate a signed certificate, but existing one is valid
ecs_certificate:
backup: true
path: '{{ example1_cert_path }}'
full_chain_path: '{{ example1_chain_path }}'
csr: '{{ csr_path }}'
cert_type: '{{ example1_cert_type }}'
requester_name: '{{ entrust_requester_name }}'
requester_email: '{{ entrust_requester_email }}'
requester_phone: '{{ entrust_requester_phone }}'
entrust_api_user: '{{ entrust_api_user }}'
entrust_api_key: '{{ entrust_api_key }}'
entrust_api_client_cert_path: '{{ entrust_api_cert }}'
entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
register: example2_result
- assert:
that:
- example2_result is not failed
- not example2_result.changed
- example2_result.backup_file is undefined
- example2_result.backup_full_chain_file is undefined
- example2_result.serial_number == example1_result.serial_number
- example2_result.tracking_id == example1_result.tracking_id
# Internal CA refuses to issue certificates with the same DN in a short time frame
- name: Sleep for 5 seconds so we don't run into duplicate-request errors
pause:
seconds: 5
- name: Force a reissue with no CSR, verify that contents changed
ecs_certificate:
backup: true
force: true
path: '{{ example1_cert_path }}'
full_chain_path: '{{ example1_chain_path }}'
cert_type: '{{ example1_cert_type }}'
request_type: reissue
requester_name: '{{ entrust_requester_name }}'
requester_email: '{{ entrust_requester_email }}'
requester_phone: '{{ entrust_requester_phone }}'
entrust_api_user: '{{ entrust_api_user }}'
entrust_api_key: '{{ entrust_api_key }}'
entrust_api_client_cert_path: '{{ entrust_api_cert }}'
entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
register: example3_result
- assert:
that:
- example3_result is not failed
- example3_result.changed
- example3_result.backup_file is string
- example3_result.backup_full_chain_file is string
- example3_result.tracking_id > 0
- example3_result.tracking_id != example1_result.tracking_id
- example3_result.serial_number != example1_result.serial_number
# Internal CA refuses to issue certificates with the same DN in a short time frame
- name: Sleep for 5 seconds so we don't run into duplicate-request errors
pause:
seconds: 5
- name: Test a request with all of the various optional possible fields populated
ecs_certificate:
path: '{{ example4_cert_path }}'
full_chain_path: '{{ example4_full_chain_path }}'
csr: '{{ csr_path }}'
subject_alt_name: '{{ example4_subject_alt_name }}'
eku: '{{ example4_eku }}'
ct_log: true
cert_type: '{{ example4_cert_type }}'
org: '{{ example4_org }}'
ou: '{{ example4_ou }}'
tracking_info: '{{ example4_tracking_info }}'
additional_emails: '{{ example4_additional_emails }}'
custom_fields: '{{ example4_custom_fields }}'
cert_expiry: '{{ example4_cert_expiry }}'
requester_name: '{{ entrust_requester_name }}'
requester_email: '{{ entrust_requester_email }}'
requester_phone: '{{ entrust_requester_phone }}'
entrust_api_user: '{{ entrust_api_user }}'
entrust_api_key: '{{ entrust_api_key }}'
entrust_api_client_cert_path: '{{ entrust_api_cert }}'
entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
register: example4_result
- assert:
that:
- example4_result is not failed
- example4_result.changed
- example4_result.backup_file is undefined
- example4_result.backup_full_chain_file is undefined
- example4_result.tracking_id > 0
- example4_result.serial_number is string
# For bug 61738, verify that the full chain is valid
- name: Verify that the full chain path can be successfully imported
command: '{{ openssl_binary }} verify "{{ example4_full_chain_path }}"'
register: openssl_result
- assert:
that:
- "' OK' in openssl_result.stdout_lines[0]"
always:
- name: clean-up temporary folder
file:
path: '{{ tmpdir_path }}'
state: absent

View File

@@ -1,56 +0,0 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# vars file for test_ecs_certificate
# Path on various hosts that cacerts need to be put as a prerequisite to API server cert validation.
# May need to be customized for some environments based on SSL implementations
# that ansible "urls" module utility is using as a backing.
cacerts_bundle_path: /etc/pki/tls/certs
common_name: '{{ ansible_date_time.epoch }}.ansint.testcertificates.com'
organization_name: CMS API, Inc.
organizational_unit_name: RSA
country_name: US
state_or_province_name: MA
privatekey_passphrase: Passphrase452!
tmpdir_path: /tmp/ecs_cert_test/{{ ansible_date_time.epoch }}
privatekey_path: '{{ tmpdir_path }}/testcertificates.key'
entrust_api_cert: '{{ tmpdir_path }}/authcert.cer'
entrust_api_cert_key: '{{ tmpdir_path }}/authkey.cer'
csr_path: '{{ tmpdir_path }}/request.csr'
entrust_requester_name: C Trufan
entrust_requester_email: CTIntegrationTests@entrustdatacard.com
entrust_requester_phone: 1-555-555-5555 # e.g. 15555555555
# TEST 1
example1_cert_path: '{{ tmpdir_path }}/issuedcert_1.pem'
example1_chain_path: '{{ tmpdir_path }}/issuedcert_1_chain.pem'
example1_cert_type: EV_SSL
example4_cert_path: '{{ tmpdir_path }}/issuedcert_2.pem'
example4_subject_alt_name:
- ansible.testcertificates.com
- www.testcertificates.com
example4_eku: SERVER_AND_CLIENT_AUTH
example4_cert_type: UC_SSL
# Test a secondary org and special characters
example4_org: Cañon City, Inc.
example4_ou:
- StringrsaString
example4_tracking_info: Submitted via Ansible Integration
example4_additional_emails:
- itsupport@testcertificates.com
- jsmith@ansible.com
example4_custom_fields:
text1: Admin
text2: Invoice 25
number1: 342
date3: '2018-01-01'
email2: sales@ansible.testcertificates.com
dropdown2: Dropdown 2 Value 1
example4_cert_expiry: 2020-08-15
example4_full_chain_path: '{{ tmpdir_path }}/issuedcert_2_chain.pem'

View File

@@ -1,19 +0,0 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Not enabled due to lack of access to test environments. May be enabled using custom integration_config.yml
# Example integation_config.yml
# ---
# entrust_api_user:
# entrust_api_key:
# entrust_api_client_cert_path: /var/integration-testing/publicCert.pem
# entrust_api_client_cert_key_path: /var/integration-testing/privateKey.pem
# entrust_api_ip_address: 127.0.0.1
# entrust_cloud_ip_address: 127.0.0.1
# # Used for certificate path validation of QA environments - we chose not to support disabling path validation ever.
# cacerts_bundle_path_local: /var/integration-testing/cacerts
### WARNING: This test will update HOSTS file and CERTIFICATE STORE of target host, in order to be able to validate
# to a QA environment. ###
unsupported

View File

@@ -1,6 +0,0 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# defaults file for test_ecs_domain

View File

@@ -1,7 +0,0 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
dependencies:
- prepare_tests

View File

@@ -1,277 +0,0 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
## Verify that integration_config was specified
- assert:
that:
- entrust_api_user is defined
- entrust_api_key is defined
- entrust_api_ip_address is defined
- entrust_cloud_ip_address is defined
- entrust_api_client_cert_path is defined or entrust_api_client_cert_contents is defined
- entrust_api_client_cert_key_path is defined or entrust_api_client_cert_key_contents
- cacerts_bundle_path_local is defined
## SET UP TEST ENVIRONMENT ########################################################################
- name: copy the files needed for verifying test server certificate to the host
copy:
src: '{{ cacerts_bundle_path_local }}/'
dest: '{{ cacerts_bundle_path }}'
- name: Update the CA certificates for our QA certs (collection may need updating if new QA environments used)
command: c_rehash {{ cacerts_bundle_path }}
- name: Update hosts file
lineinfile:
path: /etc/hosts
state: present
regexp: 'api.entrust.net$'
line: '{{ entrust_api_ip_address }} api.entrust.net'
- name: Update hosts file
lineinfile:
path: /etc/hosts
state: present
regexp: 'cloud.entrust.net$'
line: '{{ entrust_cloud_ip_address }} cloud.entrust.net'
- name: Clear out the temporary directory for storing the API connection information
file:
path: '{{ tmpdir_path }}'
state: absent
- name: Create a directory for storing the API connection Information
file:
path: '{{ tmpdir_path }}'
state: directory
- name: Copy the files needed for the connection to entrust API to the host
copy:
src: '{{ entrust_api_client_cert_path }}'
dest: '{{ entrust_api_cert }}'
- name: Copy the files needed for the connection to entrust API to the host
copy:
src: '{{ entrust_api_client_cert_key_path }}'
dest: '{{ entrust_api_cert_key }}'
- block:
- name: Have ECS request a domain validation via dns
ecs_domain:
domain_name: dns.{{ common_name }}
verification_method: dns
entrust_api_user: '{{ entrust_api_user }}'
entrust_api_key: '{{ entrust_api_key }}'
entrust_api_client_cert_path: '{{ entrust_api_cert }}'
entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
register: dns_result
- assert:
that:
- dns_result is not failed
- dns_result.changed
- dns_result.domain_status == 'INITIAL_VERIFICATION'
- dns_result.verification_method == 'dns'
- dns_result.dns_location is string
- dns_result.dns_contents is string
- dns_result.dns_resource_type is string
- dns_result.file_location is undefined
- dns_result.file_contents is undefined
- dns_result.emails is undefined
- name: Have ECS request a domain validation via web_server
ecs_domain:
domain_name: FILE.{{ common_name }}
verification_method: web_server
entrust_api_user: '{{ entrust_api_user }}'
entrust_api_key: '{{ entrust_api_key }}'
entrust_api_client_cert_path: '{{ entrust_api_cert }}'
entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
register: file_result
- assert:
that:
- file_result is not failed
- file_result.changed
- file_result.domain_status == 'INITIAL_VERIFICATION'
- file_result.verification_method == 'web_server'
- file_result.dns_location is undefined
- file_result.dns_contents is undefined
- file_result.dns_resource_type is undefined
- file_result.file_location is string
- file_result.file_contents is string
- file_result.emails is undefined
- name: Have ECS request a domain validation via email
ecs_domain:
domain_name: email.{{ common_name }}
verification_method: email
verification_email: admin@testcertificates.com
entrust_api_user: '{{ entrust_api_user }}'
entrust_api_key: '{{ entrust_api_key }}'
entrust_api_client_cert_path: '{{ entrust_api_cert }}'
entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
register: email_result
- assert:
that:
- email_result is not failed
- email_result.changed
- email_result.domain_status == 'INITIAL_VERIFICATION'
- email_result.verification_method == 'email'
- email_result.dns_location is undefined
- email_result.dns_contents is undefined
- email_result.dns_resource_type is undefined
- email_result.file_location is undefined
- email_result.file_contents is undefined
- email_result.emails[0] == 'admin@testcertificates.com'
- name: Have ECS request a domain validation via email with no address provided
ecs_domain:
domain_name: email2.{{ common_name }}
verification_method: email
entrust_api_user: '{{ entrust_api_user }}'
entrust_api_key: '{{ entrust_api_key }}'
entrust_api_client_cert_path: '{{ entrust_api_cert }}'
entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
register: email_result2
- assert:
that:
- email_result2 is not failed
- email_result2.changed
- email_result2.domain_status == 'INITIAL_VERIFICATION'
- email_result2.verification_method == 'email'
- email_result2.dns_location is undefined
- email_result2.dns_contents is undefined
- email_result2.dns_resource_type is undefined
- email_result2.file_location is undefined
- email_result2.file_contents is undefined
- email_result2.emails is defined
- name: Have ECS request a domain validation via manual
ecs_domain:
domain_name: manual.{{ common_name }}
verification_method: manual
entrust_api_user: '{{ entrust_api_user }}'
entrust_api_key: '{{ entrust_api_key }}'
entrust_api_client_cert_path: '{{ entrust_api_cert }}'
entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
register: manual_result
- assert:
that:
- manual_result is not failed
- manual_result.changed
- manual_result.domain_status == 'INITIAL_VERIFICATION'
- manual_result.verification_method == 'manual'
- manual_result.dns_location is undefined
- manual_result.dns_contents is undefined
- manual_result.dns_resource_type is undefined
- manual_result.file_location is undefined
- manual_result.file_contents is undefined
- manual_result.emails is undefined
- name: Have ECS request a domain validation via dns that remains unchanged
ecs_domain:
domain_name: dns.{{ common_name }}
verification_method: dns
entrust_api_user: '{{ entrust_api_user }}'
entrust_api_key: '{{ entrust_api_key }}'
entrust_api_client_cert_path: '{{ entrust_api_cert }}'
entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
register: dns_result2
- assert:
that:
- dns_result2 is not failed
- not dns_result2.changed
- dns_result2.domain_status == 'INITIAL_VERIFICATION'
- dns_result2.verification_method == 'dns'
- dns_result2.dns_location is string
- dns_result2.dns_contents is string
- dns_result2.dns_resource_type is string
- dns_result2.file_location is undefined
- dns_result2.file_contents is undefined
- dns_result2.emails is undefined
- name: Have ECS request a domain validation via FILE for dns, to change verification method
ecs_domain:
domain_name: dns.{{ common_name }}
verification_method: web_server
entrust_api_user: '{{ entrust_api_user }}'
entrust_api_key: '{{ entrust_api_key }}'
entrust_api_client_cert_path: '{{ entrust_api_cert }}'
entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
register: dns_result_now_file
- assert:
that:
- dns_result_now_file is not failed
- dns_result_now_file.changed
- dns_result_now_file.domain_status == 'INITIAL_VERIFICATION'
- dns_result_now_file.verification_method == 'web_server'
- dns_result_now_file.dns_location is undefined
- dns_result_now_file.dns_contents is undefined
- dns_result_now_file.dns_resource_type is undefined
- dns_result_now_file.file_location is string
- dns_result_now_file.file_contents is string
- dns_result_now_file.emails is undefined
- name: Request revalidation of an approved domain
ecs_domain:
domain_name: '{{ existing_domain_common_name }}'
verification_method: manual
entrust_api_user: '{{ entrust_api_user }}'
entrust_api_key: '{{ entrust_api_key }}'
entrust_api_client_cert_path: '{{ entrust_api_cert }}'
entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
register: manual_existing_domain
- assert:
that:
- manual_existing_domain is not failed
- not manual_existing_domain.changed
- manual_existing_domain.domain_status == 'RE_VERIFICATION'
- manual_existing_domain.dns_location is undefined
- manual_existing_domain.dns_contents is undefined
- manual_existing_domain.dns_resource_type is undefined
- manual_existing_domain.file_location is undefined
- manual_existing_domain.file_contents is undefined
- manual_existing_domain.emails is undefined
- name: Request revalidation of an approved domain
ecs_domain:
domain_name: '{{ existing_domain_common_name }}'
verification_method: web_server
entrust_api_user: '{{ entrust_api_user }}'
entrust_api_key: '{{ entrust_api_key }}'
entrust_api_client_cert_path: '{{ entrust_api_cert }}'
entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
register: file_existing_domain_revalidate
- assert:
that:
- file_existing_domain_revalidate is not failed
- file_existing_domain_revalidate.changed
- file_existing_domain_revalidate.domain_status == 'RE_VERIFICATION'
- file_existing_domain_revalidate.verification_method == 'web_server'
- file_existing_domain_revalidate.dns_location is undefined
- file_existing_domain_revalidate.dns_contents is undefined
- file_existing_domain_revalidate.dns_resource_type is undefined
- file_existing_domain_revalidate.file_location is string
- file_existing_domain_revalidate.file_contents is string
- file_existing_domain_revalidate.emails is undefined
always:
- name: clean-up temporary folder
file:
path: '{{ tmpdir_path }}'
state: absent

View File

@@ -1,19 +0,0 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# vars file for test_ecs_certificate
# Path on various hosts that cacerts need to be put as a prerequisite to API server cert validation.
# May need to be customized for some environments based on SSL implementations
# that ansible "urls" module utility is using as a backing.
cacerts_bundle_path: /etc/pki/tls/certs
common_name: '{{ ansible_date_time.epoch }}.testcertificates.com'
existing_domain_common_name: 'testcertificates.com'
tmpdir_path: /tmp/ecs_cert_test/{{ ansible_date_time.epoch }}
entrust_api_cert: '{{ tmpdir_path }}/authcert.cer'
entrust_api_cert_key: '{{ tmpdir_path }}/authkey.cer'

View File

@@ -1,3 +1,4 @@
meta/runtime.yml runtime-metadata # Bug in ansible-test: https://github.com/ansible/ansible/pull/85198
plugins/module_utils/_acme/account.py pep8:E704 plugins/module_utils/_acme/account.py pep8:E704
plugins/module_utils/_acme/acme.py pep8:E704 plugins/module_utils/_acme/acme.py pep8:E704
plugins/module_utils/_acme/acme.py pylint:unpacking-non-sequence plugins/module_utils/_acme/acme.py pylint:unpacking-non-sequence
@@ -5,7 +6,6 @@ plugins/module_utils/_acme/backend_openssl_cli.py pep8:E704
plugins/module_utils/_acme/certificate.py pep8:E704 plugins/module_utils/_acme/certificate.py pep8:E704
plugins/module_utils/_crypto/cryptography_support.py pep8:E704 plugins/module_utils/_crypto/cryptography_support.py pep8:E704
plugins/module_utils/_crypto/module_backends/certificate.py no-assert plugins/module_utils/_crypto/module_backends/certificate.py no-assert
plugins/module_utils/_crypto/module_backends/certificate_entrust.py no-assert
plugins/module_utils/_crypto/module_backends/certificate_ownca.py no-assert plugins/module_utils/_crypto/module_backends/certificate_ownca.py no-assert
plugins/module_utils/_crypto/module_backends/certificate_selfsigned.py no-assert plugins/module_utils/_crypto/module_backends/certificate_selfsigned.py no-assert
plugins/module_utils/_crypto/module_backends/csr.py no-assert plugins/module_utils/_crypto/module_backends/csr.py no-assert
@@ -21,8 +21,6 @@ plugins/modules/acme_certificate_deactivate_authz.py pylint:unpacking-non-sequen
plugins/modules/acme_certificate_order_finalize.py pylint:unpacking-non-sequence plugins/modules/acme_certificate_order_finalize.py pylint:unpacking-non-sequence
plugins/modules/acme_certificate_revoke.py pylint:unpacking-non-sequence plugins/modules/acme_certificate_revoke.py pylint:unpacking-non-sequence
plugins/modules/acme_inspect.py pylint:unpacking-non-sequence plugins/modules/acme_inspect.py pylint:unpacking-non-sequence
plugins/modules/ecs_certificate.py no-assert
plugins/modules/ecs_domain.py pep8:E704
plugins/modules/get_certificate.py pylint:unknown-option-value plugins/modules/get_certificate.py pylint:unknown-option-value
plugins/modules/luks_device.py no-assert plugins/modules/luks_device.py no-assert
plugins/modules/openssl_pkcs12.py no-assert plugins/modules/openssl_pkcs12.py no-assert

View File

@@ -1,10 +1,10 @@
meta/runtime.yml runtime-metadata # Bug in ansible-test: https://github.com/ansible/ansible/pull/85198
plugins/module_utils/_acme/account.py pep8:E704 plugins/module_utils/_acme/account.py pep8:E704
plugins/module_utils/_acme/acme.py pep8:E704 plugins/module_utils/_acme/acme.py pep8:E704
plugins/module_utils/_acme/backend_openssl_cli.py pep8:E704 plugins/module_utils/_acme/backend_openssl_cli.py pep8:E704
plugins/module_utils/_acme/certificate.py pep8:E704 plugins/module_utils/_acme/certificate.py pep8:E704
plugins/module_utils/_crypto/cryptography_support.py pep8:E704 plugins/module_utils/_crypto/cryptography_support.py pep8:E704
plugins/module_utils/_crypto/module_backends/certificate.py no-assert plugins/module_utils/_crypto/module_backends/certificate.py no-assert
plugins/module_utils/_crypto/module_backends/certificate_entrust.py no-assert
plugins/module_utils/_crypto/module_backends/certificate_ownca.py no-assert plugins/module_utils/_crypto/module_backends/certificate_ownca.py no-assert
plugins/module_utils/_crypto/module_backends/certificate_selfsigned.py no-assert plugins/module_utils/_crypto/module_backends/certificate_selfsigned.py no-assert
plugins/module_utils/_crypto/module_backends/csr.py no-assert plugins/module_utils/_crypto/module_backends/csr.py no-assert
@@ -13,8 +13,6 @@ plugins/module_utils/_crypto/support.py pep8:E704
plugins/module_utils/_openssh/backends/keypair_backend.py no-assert plugins/module_utils/_openssh/backends/keypair_backend.py no-assert
plugins/module_utils/_openssh/certificate.py pep8:E704 plugins/module_utils/_openssh/certificate.py pep8:E704
plugins/modules/acme_certificate.py no-assert plugins/modules/acme_certificate.py no-assert
plugins/modules/ecs_certificate.py no-assert
plugins/modules/ecs_domain.py pep8:E704
plugins/modules/luks_device.py no-assert plugins/modules/luks_device.py no-assert
plugins/modules/openssl_pkcs12.py no-assert plugins/modules/openssl_pkcs12.py no-assert
tests/ee/roles/smoke/library/smoke_ipaddress.py shebang tests/ee/roles/smoke/library/smoke_ipaddress.py shebang

View File

@@ -1,12 +1,11 @@
meta/runtime.yml runtime-metadata # Bug in ansible-test: https://github.com/ansible/ansible/pull/85198
plugins/module_utils/_crypto/module_backends/certificate.py no-assert plugins/module_utils/_crypto/module_backends/certificate.py no-assert
plugins/module_utils/_crypto/module_backends/certificate_entrust.py no-assert
plugins/module_utils/_crypto/module_backends/certificate_ownca.py no-assert plugins/module_utils/_crypto/module_backends/certificate_ownca.py no-assert
plugins/module_utils/_crypto/module_backends/certificate_selfsigned.py no-assert plugins/module_utils/_crypto/module_backends/certificate_selfsigned.py no-assert
plugins/module_utils/_crypto/module_backends/csr.py no-assert plugins/module_utils/_crypto/module_backends/csr.py no-assert
plugins/module_utils/_crypto/module_backends/privatekey_convert.py no-assert plugins/module_utils/_crypto/module_backends/privatekey_convert.py no-assert
plugins/module_utils/_openssh/backends/keypair_backend.py no-assert plugins/module_utils/_openssh/backends/keypair_backend.py no-assert
plugins/modules/acme_certificate.py no-assert plugins/modules/acme_certificate.py no-assert
plugins/modules/ecs_certificate.py no-assert
plugins/modules/luks_device.py no-assert plugins/modules/luks_device.py no-assert
plugins/modules/openssl_pkcs12.py no-assert plugins/modules/openssl_pkcs12.py no-assert
tests/ee/roles/smoke/library/smoke_ipaddress.py shebang tests/ee/roles/smoke/library/smoke_ipaddress.py shebang