From cedd9d9926c113280e40915ae844fba2b2bd9fe8 Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Fri, 15 Feb 2019 11:46:44 +0100 Subject: [PATCH] crypto: Fix known issues in modules (#52302) * crypto: Fix known issues in modules This fixes a few issues reported by 'validate-modules'. * Fix whitespace --- .../modules/crypto/openssl_certificate.py | 293 +++++++++------ lib/ansible/modules/crypto/openssl_csr.py | 350 +++++++++--------- lib/ansible/modules/crypto/openssl_dhparam.py | 83 +++-- lib/ansible/modules/crypto/openssl_pkcs12.py | 120 +++--- .../modules/crypto/openssl_privatekey.py | 160 ++++---- .../modules/crypto/openssl_publickey.py | 104 +++--- test/sanity/validate-modules/ignore.txt | 2 - 7 files changed, 590 insertions(+), 522 deletions(-) diff --git a/lib/ansible/modules/crypto/openssl_certificate.py b/lib/ansible/modules/crypto/openssl_certificate.py index 2f1e72aca1f..e890f3f8a13 100644 --- a/lib/ansible/modules/crypto/openssl_certificate.py +++ b/lib/ansible/modules/crypto/openssl_certificate.py @@ -1,8 +1,8 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# (c) 2016-2017, Yanis Guenane -# (c) 2017, Markus Teufelberger +# Copyright: (c) 2016-2017, Yanis Guenane +# Copyright: (c) 2017, Markus Teufelberger # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function @@ -14,268 +14,322 @@ ANSIBLE_METADATA = {'metadata_version': '1.1', 'supported_by': 'community'} -DOCUMENTATION = ''' +DOCUMENTATION = r''' --- module: openssl_certificate -author: - - Yanis Guenane (@Spredzy) - - Markus Teufelberger (@MarkusTeufelberger) version_added: "2.4" short_description: Generate and/or check OpenSSL certificates description: - - "This module allows one to (re)generate OpenSSL certificates. It implements a notion - of provider (ie. C(selfsigned), C(ownca), C(acme), C(assertonly)) for your certificate. - The 'assertonly' provider is intended for use cases where one is only interested in - checking properties of a supplied certificate. - The 'ownca' provider is intended for generate OpenSSL certificate signed with your own - CA (Certificate Authority) certificate (self-signed certificate). - Many properties that can be specified in this module are for validation of an - existing or newly generated certificate. The proper place to specify them, if you - want to receive a certificate with these properties is a CSR (Certificate Signing Request). - It uses the pyOpenSSL python library to interact with OpenSSL." + - This module allows one to (re)generate OpenSSL certificates. + - It implements a notion of provider (ie. C(selfsigned), C(ownca), C(acme), C(assertonly)) + for your certificate. + - The C(assertonly) provider is intended for use cases where one is only interested in + checking properties of a supplied certificate. + - The C(ownca) provider is intended for generate OpenSSL certificate signed with your own + CA (Certificate Authority) certificate (self-signed certificate). + - Many properties that can be specified in this module are for validation of an + existing or newly generated certificate. The proper place to specify them, if you + want to receive a certificate with these properties is a CSR (Certificate Signing Request). + - It uses the pyOpenSSL python library to interact with OpenSSL. requirements: - python-pyOpenSSL >= 0.15 (if using C(selfsigned) or C(assertonly) provider) - acme-tiny (if using the C(acme) provider) +author: + - Yanis Guenane (@Spredzy) + - Markus Teufelberger (@MarkusTeufelberger) options: state: - default: "present" - choices: [ present, absent ] description: - Whether the certificate should exist or not, taking action if the state is different from what is stated. + type: str + choices: [ absent, present ] + default: present path: - required: true description: - Remote absolute path where the generated certificate file should be created or is already located. + type: path + required: true provider: - required: true - choices: [ 'selfsigned', 'ownca', 'assertonly', 'acme' ] description: - Name of the provider to use to generate/retrieve the OpenSSL certificate. - The C(assertonly) provider will not generate files and fail if the certificate file is missing. + - The C(assertonly) provider will not generate files and fail if the certificate file is missing. + required: true + type: str + choices: [ acme, assertonly, ownca, selfsigned ] force: - default: False - type: bool description: - Generate the certificate, even if it already exists. + type: bool + default: no csr_path: description: - - Path to the Certificate Signing Request (CSR) used to generate this certificate. This is not required in C(assertonly) mode. + - Path to the Certificate Signing Request (CSR) used to generate this certificate. + - This is not required in C(assertonly) mode. + type: path privatekey_path: description: - Path to the private key to use when signing the certificate. + type: path privatekey_passphrase: description: - The passphrase for the I(privatekey_path). + type: str selfsigned_version: - default: 3 description: - - Version of the C(selfsigned) certificate. Nowadays it should almost always be C(3). + - Version of the C(selfsigned) certificate. + - Nowadays it should almost always be C(3). + type: int + default: 3 version_added: "2.5" selfsigned_digest: - default: "sha256" description: - - Digest algorithm to be used when self-signing the certificate + - Digest algorithm to be used when self-signing the certificate. + type: str + default: sha256 selfsigned_not_before: - default: +0s description: - - "The point in time the certificate is valid from. Time can be specified either as relative time or as absolute timestamp. - Time will always be interpreted as UTC. Valid formats are: C([+-]timespec | ASN.1 TIME) - where timespec can be an integer + C([w | d | h | m | s]) (e.g. C(+32w1d2h). - Note that if using relative time this module is NOT idempotent. - If this value is not specified, the certificate will start being valid from now." + - The point in time the certificate is valid from. + - Time can be specified either as relative time or as absolute timestamp. + - Time will always be interpreted as UTC. + - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer + + C([w | d | h | m | s]) (e.g. C(+32w1d2h). + - Note that if using relative time this module is NOT idempotent. + - If this value is not specified, the certificate will start being valid from now. + type: str + default: +0s aliases: [ selfsigned_notBefore ] selfsigned_not_after: - default: +3650d description: - - "The point in time at which the certificate stops being valid. Time can be specified either as relative time or as absolute timestamp. - Time will always be interpreted as UTC. Valid formats are: C([+-]timespec | ASN.1 TIME) - where timespec can be an integer + C([w | d | h | m | s]) (e.g. C(+32w1d2h). - Note that if using relative time this module is NOT idempotent. - If this value is not specified, the certificate will stop being valid 10 years from now." + - The point in time at which the certificate stops being valid. + - Time can be specified either as relative time or as absolute timestamp. + - Time will always be interpreted as UTC. + - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer + + C([w | d | h | m | s]) (e.g. C(+32w1d2h). + - Note that if using relative time this module is NOT idempotent. + - If this value is not specified, the certificate will stop being valid 10 years from now. + type: str + default: +3650d aliases: [ selfsigned_notAfter ] ownca_path: description: - Remote absolute path of the CA (Certificate Authority) certificate. + type: path version_added: "2.7" ownca_privatekey_path: description: - Path to the CA (Certificate Authority) private key to use when signing the certificate. + type: path version_added: "2.7" ownca_privatekey_passphrase: description: - The passphrase for the I(ownca_privatekey_path). + type: str version_added: "2.7" ownca_digest: - default: "sha256" description: - - Digest algorithm to be used for the C(ownca) certificate. + - The digest algorithm to be used for the C(ownca) certificate. + type: str + default: sha256 version_added: "2.7" ownca_version: - default: 3 description: - - Version of the C(ownca) certificate. Nowadays it should almost always be C(3). + - The version of the C(ownca) certificate. + - Nowadays it should almost always be C(3). + type: int + default: 3 version_added: "2.7" ownca_not_before: - default: +0s description: - - "The point in time the certificate is valid from. Time can be specified either as relative time or as absolute timestamp. - Time will always be interpreted as UTC. Valid formats are: C([+-]timespec | ASN.1 TIME) - where timespec can be an integer + C([w | d | h | m | s]) (e.g. C(+32w1d2h). - Note that if using relative time this module is NOT idempotent. - If this value is not specified, the certificate will start being valid from now." + - The point in time the certificate is valid from. + - Time can be specified either as relative time or as absolute timestamp. + - Time will always be interpreted as UTC. + - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer + + C([w | d | h | m | s]) (e.g. C(+32w1d2h). + - Note that if using relative time this module is NOT idempotent. + - If this value is not specified, the certificate will start being valid from now. + type: str + default: +0s version_added: "2.7" ownca_not_after: - default: +3650d description: - - "The point in time at which the certificate stops being valid. Time can be specified either as relative time or as absolute timestamp. - Time will always be interpreted as UTC. Valid formats are: C([+-]timespec | ASN.1 TIME) - where timespec can be an integer + C([w | d | h | m | s]) (e.g. C(+32w1d2h). - Note that if using relative time this module is NOT idempotent. - If this value is not specified, the certificate will stop being valid 10 years from now." + - The point in time at which the certificate stops being valid. + - Time can be specified either as relative time or as absolute timestamp. + - Time will always be interpreted as UTC. + - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer + + C([w | d | h | m | s]) (e.g. C(+32w1d2h). + - Note that if using relative time this module is NOT idempotent. + - If this value is not specified, the certificate will stop being valid 10 years from now. + type: str + default: +3650d version_added: "2.7" acme_accountkey_path: description: - - Path to the accountkey for the C(acme) provider + - The path to the accountkey for the C(acme) provider. + type: path acme_challenge_path: description: - - Path to the ACME challenge directory that is served on U(http://:80/.well-known/acme-challenge/) + - The path to the ACME challenge directory that is served on U(http://:80/.well-known/acme-challenge/) + type: path acme_chain: - default: True description: - Include the intermediate certificate to the generated certificate + type: bool + default: yes version_added: "2.5" signature_algorithms: description: - - list of algorithms that you would accept the certificate to be signed with + - A list of algorithms that you would accept the certificate to be signed with (e.g. ['sha256WithRSAEncryption', 'sha512WithRSAEncryption']). + type: str issuer: description: - - Key/value pairs that must be present in the issuer name field of the certificate. - If you need to specify more than one value with the same key, use a list as value. + - The key/value pairs that must be present in the issuer name field of the certificate. + - If you need to specify more than one value with the same key, use a list as value. + type: str issuer_strict: - default: False - type: bool description: - - If set to True, the I(issuer) field must contain only these values. + - If set to C(yes), the I(issuer) field must contain only these values. + type: bool + default: no version_added: "2.5" subject: description: - - Key/value pairs that must be present in the subject name field of the certificate. - If you need to specify more than one value with the same key, use a list as value. + - The key/value pairs that must be present in the subject name field of the certificate. + - If you need to specify more than one value with the same key, use a list as value. + type: str subject_strict: - default: False - type: bool description: - - If set to True, the I(subject) field must contain only these values. + - If set to C(yes), the I(subject) field must contain only these values. + type: bool + default: no version_added: "2.5" has_expired: - default: False - type: bool description: - Checks if the certificate is expired/not expired at the time the module is executed. + type: bool + default: no version: description: - - Version of the certificate. Nowadays it should almost always be 3. + - The version of the certificate. + - Nowadays it should almost always be 3. + type: int valid_at: description: - - The certificate must be valid at this point in time. The timestamp is formatted as an ASN.1 TIME. + - The certificate must be valid at this point in time. + - The timestamp is formatted as an ASN.1 TIME. + type: str invalid_at: description: - - The certificate must be invalid at this point in time. The timestamp is formatted as an ASN.1 TIME. + - The certificate must be invalid at this point in time. + - The timestamp is formatted as an ASN.1 TIME. + type: str not_before: description: - - The certificate must start to become valid at this point in time. The timestamp is formatted as an ASN.1 TIME. + - The certificate must start to become valid at this point in time. + - The timestamp is formatted as an ASN.1 TIME. + type: str aliases: [ notBefore ] not_after: description: - - The certificate must expire at this point in time. The timestamp is formatted as an ASN.1 TIME. + - The certificate must expire at this point in time. + - The timestamp is formatted as an ASN.1 TIME. + type: str aliases: [ notAfter ] valid_in: description: - - "The certificate must still be valid at this relative time offset from now. - Valid formats are: C([+-]timespec | number_of_seconds) - where timespec can be an integer + C([w | d | h | m | s]) (e.g. C(+32w1d2h). - Note that if using this parameter, this module is NOT idempotent." + - The certificate must still be valid at this relative time offset from now. + - Valid format is C([+-]timespec | number_of_seconds) where timespec can be an integer + + C([w | d | h | m | s]) (e.g. C(+32w1d2h). + - Note that if using this parameter, this module is NOT idempotent. + type: str key_usage: description: - The I(key_usage) extension field must contain all these values. + type: list aliases: [ keyUsage ] key_usage_strict: - default: False - type: bool description: - - If set to True, the I(key_usage) extension field must contain only these values. + - If set to C(yes), the I(key_usage) extension field must contain only these values. + type: bool + default: no aliases: [ keyUsage_strict ] extended_key_usage: description: - The I(extended_key_usage) extension field must contain all these values. + type: list aliases: [ extendedKeyUsage ] extended_key_usage_strict: - default: False - type: bool description: - - If set to True, the I(extended_key_usage) extension field must contain only these values. + - If set to C(yes), the I(extended_key_usage) extension field must contain only these values. + type: bool + default: no aliases: [ extendedKeyUsage_strict ] subject_alt_name: description: - The I(subject_alt_name) extension field must contain these values. + type: list aliases: [ subjectAltName ] subject_alt_name_strict: - default: False - type: bool description: - - If set to True, the I(subject_alt_name) extension field must contain only these values. + - If set to C(yes), the I(subject_alt_name) extension field must contain only these values. + type: bool + default: no aliases: [ subjectAltName_strict ] extends_documentation_fragment: files notes: - All ASN.1 TIME values should be specified following the YYYYMMDDHHMMSSZ pattern. - Date specified should be UTC. Minutes and seconds are mandatory. + - Date specified should be UTC. Minutes and seconds are mandatory. - For security reason, when you use C(ownca) provider, you should NOT run M(openssl_certificate) on a target machine, but on a dedicated CA machine. It is recommended not to store the CA private key on the target machine. Once signed, the certificate can be moved to the target machine. +seealso: +- module: openssl_csr +- module: openssl_dhparam +- module: openssl_pkcs12 +- module: openssl_privatekey +- module: openssl_publickey ''' - -EXAMPLES = ''' +EXAMPLES = r''' - name: Generate a Self Signed OpenSSL certificate openssl_certificate: path: /etc/ssl/crt/ansible.com.crt @@ -306,7 +360,7 @@ EXAMPLES = ''' provider: acme acme_accountkey_path: /etc/ssl/private/ansible.com.pem acme_challenge_path: /etc/ssl/challenges/ansible.com/ - force: True + force: yes # Examples for some checks one could use the assertonly provider for: @@ -315,8 +369,8 @@ EXAMPLES = ''' openssl_certificate: path: /etc/ssl/crt/example.com.crt provider: assertonly - has_expired: False - ignore_errors: True + has_expired: no + ignore_errors: yes register: validity_check - name: Run custom task(s) to get a new, valid certificate in case the initial check failed @@ -327,7 +381,7 @@ EXAMPLES = ''' openssl_certificate: path: /etc/ssl/crt/example.com.crt provider: assertonly - has_expired: False + has_expired: no when: validity_check.failed # Some other checks that assertonly could be used for: @@ -337,7 +391,7 @@ EXAMPLES = ''' provider: assertonly issuer: O: Let's Encrypt - has_expired: False + has_expired: no - name: Ensure that a certificate uses a modern signature algorithm (no SHA1, MD5 or DSA) openssl_certificate: @@ -405,8 +459,7 @@ EXAMPLES = ''' - test.example.com ''' - -RETURN = ''' +RETURN = r''' filename: description: Path to the generated Certificate returned: changed or success @@ -710,14 +763,14 @@ class AssertOnlyCertificate(Certificate): self.issuer_strict = module.params['issuer_strict'] self.has_expired = module.params['has_expired'] self.version = module.params['version'] - self.keyUsage = module.params['keyUsage'] - self.keyUsage_strict = module.params['keyUsage_strict'] - self.extendedKeyUsage = module.params['extendedKeyUsage'] - self.extendedKeyUsage_strict = module.params['extendedKeyUsage_strict'] - self.subjectAltName = module.params['subjectAltName'] - self.subjectAltName_strict = module.params['subjectAltName_strict'] - self.notBefore = module.params['notBefore'] - self.notAfter = module.params['notAfter'] + self.keyUsage = module.params['key_usage'] + self.keyUsage_strict = module.params['key_usage_strict'] + self.extendedKeyUsage = module.params['extended_key_usage'] + self.extendedKeyUsage_strict = module.params['extended_key_usage_strict'] + self.subjectAltName = module.params['subject_alt_name'] + self.subjectAltName_strict = module.params['subject_alt_name_strict'] + self.notBefore = module.params['not_before'] + self.notAfter = module.params['not_after'] self.valid_at = module.params['valid_at'] self.invalid_at = module.params['invalid_at'] self.valid_in = module.params['valid_in'] @@ -1002,7 +1055,7 @@ def main(): argument_spec=dict( state=dict(type='str', choices=['present', 'absent'], default='present'), path=dict(type='path', required=True), - provider=dict(type='str', choices=['selfsigned', 'ownca', 'assertonly', 'acme']), + provider=dict(type='str', choices=['acme', 'assertonly', 'ownca', 'selfsigned']), force=dict(type='bool', default=False,), csr_path=dict(type='path'), @@ -1016,20 +1069,20 @@ def main(): issuer_strict=dict(type='bool', default=False), has_expired=dict(type='bool', default=False), version=dict(type='int'), - keyUsage=dict(type='list', aliases=['key_usage'], elements='str'), - keyUsage_strict=dict(type='bool', default=False, aliases=['key_usage_strict']), - extendedKeyUsage=dict(type='list', aliases=['extended_key_usage'], elements='str'), - extendedKeyUsage_strict=dict(type='bool', default=False, aliases=['extended_key_usage_strict']), - subjectAltName=dict(type='list', aliases=['subject_alt_name'], elements='str'), - subjectAltName_strict=dict(type='bool', default=False, aliases=['subject_alt_name_strict']), - notBefore=dict(type='str', aliases=['not_before']), - notAfter=dict(type='str', aliases=['not_after']), + key_usage=dict(type='list', elements='str', aliases=['keyUsage']), + key_usage_strict=dict(type='bool', default=False, aliases=['keyUsage_strict']), + extended_key_usage=dict(type='list', elements='str', aliases=['extendedKeyUsage']), + extended_key_usage_strict=dict(type='bool', default=False, aliases=['extendedKeyUsage_strict']), + subject_alt_name=dict(type='list', elements='str', aliases=['subjectAltName']), + subject_alt_name_strict=dict(type='bool', default=False, aliases=['subjectAltName_strict']), + not_before=dict(type='str', aliases=['notBefore']), + not_after=dict(type='str', aliases=['notAfter']), valid_at=dict(type='str'), invalid_at=dict(type='str'), valid_in=dict(type='str'), # provider: selfsigned - selfsigned_version=dict(type='int', default='3'), + selfsigned_version=dict(type='int', default=3), selfsigned_digest=dict(type='str', default='sha256'), selfsigned_not_before=dict( type='str', default='+0s', aliases=['selfsigned_notBefore']), @@ -1041,7 +1094,7 @@ def main(): ownca_privatekey_path=dict(type='path'), ownca_privatekey_passphrase=dict(type='path', no_log=True), ownca_digest=dict(type='str', default='sha256'), - ownca_version=dict(type='int', default='3'), + ownca_version=dict(type='int', default=3), ownca_not_before=dict(type='str', default='+0s'), ownca_not_after=dict(type='str', default='+3650d'), diff --git a/lib/ansible/modules/crypto/openssl_csr.py b/lib/ansible/modules/crypto/openssl_csr.py index 07e7eef639c..b58a6d7d799 100644 --- a/lib/ansible/modules/crypto/openssl_csr.py +++ b/lib/ansible/modules/crypto/openssl_csr.py @@ -1,228 +1,225 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# -# (c) 2017, Yanis Guenane + +# Copyrigt: (c) 2017, Yanis Guenane # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type - ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} - -DOCUMENTATION = ''' +DOCUMENTATION = r''' --- module: openssl_csr -author: "Yanis Guenane (@Spredzy)" -version_added: "2.4" +version_added: '2.4' short_description: Generate OpenSSL Certificate Signing Request (CSR) description: - - "This module allows one to (re)generate OpenSSL certificate signing requests. - It uses the pyOpenSSL python library to interact with openssl. This module supports - the subjectAltName, keyUsage, extendedKeyUsage, basicConstraints and OCSP Must Staple - extensions." + - This module allows one to (re)generate OpenSSL certificate signing requests. + - It uses the pyOpenSSL python library to interact with openssl. This module supports + the subjectAltName, keyUsage, extendedKeyUsage, basicConstraints and OCSP Must Staple + extensions. requirements: - - "One of the following Python libraries:" - - "cryptography >= 1.3" - - "pyOpenSSL >= 0.15" + - Either cryptography >= 1.3 + - Or pyOpenSSL >= 0.15 +author: +- Yanis Guenane (@Spredzy) options: state: - required: false - default: "present" - choices: [ present, absent ] description: - Whether the certificate signing request should exist or not, taking action if the state is different from what is stated. + type: str + required: false + choices: [ absent, present ] + default: present digest: - required: false - default: "sha256" description: - - Digest used when signing the certificate signing request with the private key + - The digest used when signing the certificate signing request with the private key. + type: str + default: sha256 privatekey_path: - required: true description: - - Path to the privatekey to use when signing the certificate signing request + - The path to the privatekey to use when signing the certificate signing request. + type: path + required: true privatekey_passphrase: - required: false description: - The passphrase for the privatekey. + type: str version: - required: false + description: + - The version of the certificate signing request. + type: int default: 1 - description: - - Version of the certificate signing request force: - required: false - default: False + description: + - Should the certificate signing request be forced regenerated by this ansible module. type: bool - description: - - Should the certificate signing request be forced regenerated by this ansible module + default: no path: - required: true description: - - Name of the file into which the generated OpenSSL certificate signing request will be written + - The name of the file into which the generated OpenSSL certificate signing request will be written. + type: path + required: true subject: - required: false description: - Key/value pairs that will be present in the subject name field of the certificate signing request. - If you need to specify more than one value with the same key, use a list as value. + type: str version_added: '2.5' country_name: - required: false - aliases: [ 'C', 'countryName' ] description: - - countryName field of the certificate signing request subject + - The countryName field of the certificate signing request subject. + type: str + aliases: [ C, countryName ] state_or_province_name: - required: false - aliases: [ 'ST', 'stateOrProvinceName' ] description: - - stateOrProvinceName field of the certificate signing request subject + - The stateOrProvinceName field of the certificate signing request subject. + type: str + aliases: [ ST, stateOrProvinceName ] locality_name: - required: false - aliases: [ 'L', 'localityName' ] description: - - localityName field of the certificate signing request subject + - The localityName field of the certificate signing request subject. + type: str + aliases: [ L, localityName ] organization_name: - required: false - aliases: [ 'O', 'organizationName' ] description: - - organizationName field of the certificate signing request subject + - The organizationName field of the certificate signing request subject. + type: str + aliases: [ O, organizationName ] organizational_unit_name: - required: false - aliases: [ 'OU', 'organizationalUnitName' ] description: - - organizationalUnitName field of the certificate signing request subject + - The organizationalUnitName field of the certificate signing request subject. + type: str + aliases: [ OU, organizationalUnitName ] common_name: - required: false - aliases: [ 'CN', 'commonName' ] description: - - commonName field of the certificate signing request subject + - The commonName field of the certificate signing request subject. + type: str + aliases: [ CN, commonName ] email_address: - required: false - aliases: [ 'E', 'emailAddress' ] description: - - emailAddress field of the certificate signing request subject + - The emailAddress field of the certificate signing request subject. + type: str + aliases: [ E, emailAddress ] subject_alt_name: - required: false - aliases: [ 'subjectAltName' ] description: - - SAN extension to attach to the certificate signing request + - SAN extension to attach to the certificate signing request. - This can either be a 'comma separated string' or a YAML list. - Values should be prefixed by their options. (i.e., C(email), C(URI), C(DNS), C(RID), C(IP), C(dirName), C(otherName) and the ones specific to your CA) - Note that if no SAN is specified, but a common name, the common name will be added as a SAN except if C(useCommonNameForSAN) is set to I(false). - - More at U(https://tools.ietf.org/html/rfc5280#section-4.2.1.6) + - More at U(https://tools.ietf.org/html/rfc5280#section-4.2.1.6). + type: list + aliases: [ subjectAltName ] subject_alt_name_critical: - required: false - aliases: [ 'subjectAltName_critical' ] description: - - Should the subjectAltName extension be considered as critical - useCommonNameForSAN: + - Should the subjectAltName extension be considered as critical. type: bool - default: true + aliases: [ subjectAltName_critical ] + use_common_name_for_san: description: - - If set to I(true), the module will fill the common name in for + - If set to C(yes), the module will fill the common name in for C(subject_alt_name) with C(DNS:) prefix if no SAN is specified. + type: bool + default: yes + aliases: [ useCommonNameForSAN ] version_added: '2.8' key_usage: - required: false - aliases: [ 'keyUsage' ] description: - This defines the purpose (e.g. encipherment, signature, certificate signing) of the key contained in the certificate. - - This can either be a 'comma separated string' or a YAML list. + type: list + aliases: [ keyUsage ] key_usage_critical: - required: false - aliases: [ 'keyUsage_critical' ] description: - - Should the keyUsage extension be considered as critical + - Should the keyUsage extension be considered as critical. + type: bool + aliases: [ keyUsage_critical ] extended_key_usage: - required: false - aliases: [ 'extKeyUsage', 'extendedKeyUsage' ] description: - Additional restrictions (e.g. client authentication, server authentication) on the allowed purposes for which the public key may be used. - - This can either be a 'comma separated string' or a YAML list. + type: list + aliases: [ extKeyUsage, extendedKeyUsage ] extended_key_usage_critical: - required: false - aliases: [ 'extKeyUsage_critical', 'extendedKeyUsage_critical' ] description: - - Should the extkeyUsage extension be considered as critical + - Should the extkeyUsage extension be considered as critical. + type: bool + aliases: [ extKeyUsage_critical, extendedKeyUsage_critical ] basic_constraints: - required: false - aliases: ['basicConstraints'] description: - Indicates basic constraints, such as if the certificate is a CA. - version_added: 2.5 + type: list + aliases: [ basicConstraints ] + version_added: '2.5' basic_constraints_critical: - required: false - aliases: [ 'basicConstraints_critical' ] description: - - Should the basicConstraints extension be considered as critical - version_added: 2.5 + - Should the basicConstraints extension be considered as critical. + type: bool + aliases: [ basicConstraints_critical ] + version_added: '2.5' ocsp_must_staple: - required: false - aliases: ['ocspMustStaple'] description: - Indicates that the certificate should contain the OCSP Must Staple extension (U(https://tools.ietf.org/html/rfc7633)). - version_added: 2.5 + type: bool + aliases: [ ocspMustStaple ] + version_added: '2.5' ocsp_must_staple_critical: - required: false - aliases: [ 'ocspMustStaple_critical' ] description: - Should the OCSP Must Staple extension be considered as critical - - "Warning: according to the RFC, this extension should not be marked - as critical, as old clients not knowing about OCSP Must Staple - are required to reject such certificates - (see U(https://tools.ietf.org/html/rfc7633#section-4))." - version_added: 2.5 + - Note that according to the RFC, this extension should not be marked + as critical, as old clients not knowing about OCSP Must Staple + are required to reject such certificates + (see U(https://tools.ietf.org/html/rfc7633#section-4)). + type: bool + aliases: [ ocspMustStaple_critical ] + version_added: '2.5' select_crypto_backend: description: - - "Determines which crypto backend to use. The default choice is C(auto), - which tries to use C(cryptography) if available, and falls back to - C(pyopenssl)." - - "If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) - library." - - "If set to C(cryptography), will try to use the - L(cryptography,https://cryptography.io/) library." + - Determines which crypto backend to use. + - The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to C(pyopenssl). + - If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) library. + - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library. type: str - default: 'auto' - choices: - - auto - - cryptography - - pyopenssl - version_added: "2.8" -extends_documentation_fragment: files - + choices: [ auto, cryptography, pyopenssl ] + default: auto + version_added: '2.8' +extends_documentation_fragment: +- files notes: - - "If the certificate signing request already exists it will be checked whether subjectAltName, - keyUsage, extendedKeyUsage and basicConstraints only contain the requested values, whether - OCSP Must Staple is as requested, and if the request was signed by the given private key." + - If the certificate signing request already exists it will be checked whether subjectAltName, + keyUsage, extendedKeyUsage and basicConstraints only contain the requested values, whether + OCSP Must Staple is as requested, and if the request was signed by the given private key. +seealso: +- module: openssl_certificate +- module: openssl_dhparam +- module: openssl_pkcs12 +- module: openssl_privatekey +- module: openssl_publickey ''' - -EXAMPLES = ''' -# Generate an OpenSSL Certificate Signing Request -- openssl_csr: +EXAMPLES = r''' +- name: Generate an OpenSSL Certificate Signing Request + openssl_csr: path: /etc/ssl/csr/www.ansible.com.csr privatekey_path: /etc/ssl/private/ansible.com.pem common_name: www.ansible.com -# Generate an OpenSSL Certificate Signing Request with a -# passphrase protected private key -- openssl_csr: +- name: Generate an OpenSSL Certificate Signing Request with a passphrase protected private key + openssl_csr: path: /etc/ssl/csr/www.ansible.com.csr privatekey_path: /etc/ssl/private/ansible.com.pem privatekey_passphrase: ansible common_name: www.ansible.com -# Generate an OpenSSL Certificate Signing Request with Subject information -- openssl_csr: +- name: Generate an OpenSSL Certificate Signing Request with Subject information + openssl_csr: path: /etc/ssl/csr/www.ansible.com.csr privatekey_path: /etc/ssl/private/ansible.com.pem country_name: FR @@ -230,14 +227,14 @@ EXAMPLES = ''' email_address: jdoe@ansible.com common_name: www.ansible.com -# Generate an OpenSSL Certificate Signing Request with subjectAltName extension -- openssl_csr: +- name: Generate an OpenSSL Certificate Signing Request with subjectAltName extension + openssl_csr: path: /etc/ssl/csr/www.ansible.com.csr privatekey_path: /etc/ssl/private/ansible.com.pem subject_alt_name: 'DNS:www.ansible.com,DNS:m.ansible.com' -# Generate an OpenSSL CSR with subjectAltName extension with dynamic list -- openssl_csr: +- name: Generate an OpenSSL CSR with subjectAltName extension with dynamic list + openssl_csr: path: /etc/ssl/csr/www.ansible.com.csr privatekey_path: /etc/ssl/private/ansible.com.pem subject_alt_name: "{{ item.value | map('regex_replace', '^', 'DNS:') | list }}" @@ -246,15 +243,15 @@ EXAMPLES = ''' - www.ansible.com - m.ansible.com -# Force re-generate an OpenSSL Certificate Signing Request -- openssl_csr: +- name: Force re-generate an OpenSSL Certificate Signing Request + openssl_csr: path: /etc/ssl/csr/www.ansible.com.csr privatekey_path: /etc/ssl/private/ansible.com.pem - force: True + force: yes common_name: www.ansible.com -# Generate an OpenSSL Certificate Signing Request with special key usages -- openssl_csr: +- name: Generate an OpenSSL Certificate Signing Request with special key usages + openssl_csr: path: /etc/ssl/csr/www.ansible.com.csr privatekey_path: /etc/ssl/private/ansible.com.pem common_name: www.ansible.com @@ -264,16 +261,15 @@ EXAMPLES = ''' extended_key_usage: - clientAuth -# Generate an OpenSSL Certificate Signing Request with OCSP Must Staple -- openssl_csr: +- name: Generate an OpenSSL Certificate Signing Request with OCSP Must Staple + openssl_csr: path: /etc/ssl/csr/www.ansible.com.csr privatekey_path: /etc/ssl/private/ansible.com.pem common_name: www.ansible.com - ocsp_must_staple: true + ocsp_must_staple: yes ''' - -RETURN = ''' +RETURN = r''' privatekey: description: Path to the TLS/SSL private key the CSR was generated for returned: changed or success @@ -384,34 +380,34 @@ class CertificateSigningRequestBase(crypto_utils.OpenSSLObject): self.privatekey_path = module.params['privatekey_path'] self.privatekey_passphrase = module.params['privatekey_passphrase'] self.version = module.params['version'] - self.subjectAltName = module.params['subjectAltName'] - self.subjectAltName_critical = module.params['subjectAltName_critical'] - self.keyUsage = module.params['keyUsage'] - self.keyUsage_critical = module.params['keyUsage_critical'] - self.extendedKeyUsage = module.params['extendedKeyUsage'] - self.extendedKeyUsage_critical = module.params['extendedKeyUsage_critical'] - self.basicConstraints = module.params['basicConstraints'] - self.basicConstraints_critical = module.params['basicConstraints_critical'] - self.ocspMustStaple = module.params['ocspMustStaple'] - self.ocspMustStaple_critical = module.params['ocspMustStaple_critical'] + self.subjectAltName = module.params['subject_alt_name'] + self.subjectAltName_critical = module.params['subject_alt_name_critical'] + self.keyUsage = module.params['key_usage'] + self.keyUsage_critical = module.params['key_usage_critical'] + self.extendedKeyUsage = module.params['extended_key_usage'] + self.extendedKeyUsage_critical = module.params['extended_key_usage_critical'] + self.basicConstraints = module.params['basic_constraints'] + self.basicConstraints_critical = module.params['basic_constraints_critical'] + self.ocspMustStaple = module.params['ocsp_must_staple'] + self.ocspMustStaple_critical = module.params['ocsp_must_staple_critical'] self.request = None self.privatekey = None self.subject = [ - ('C', module.params['countryName']), - ('ST', module.params['stateOrProvinceName']), - ('L', module.params['localityName']), - ('O', module.params['organizationName']), - ('OU', module.params['organizationalUnitName']), - ('CN', module.params['commonName']), - ('emailAddress', module.params['emailAddress']), + ('C', module.params['country_name']), + ('ST', module.params['state_or_province_name']), + ('L', module.params['locality_name']), + ('O', module.params['organization_name']), + ('OU', module.params['organizational_unit_name']), + ('CN', module.params['common_name']), + ('emailAddress', module.params['email_address']), ] if module.params['subject']: self.subject = self.subject + crypto_utils.parse_name_field(module.params['subject']) self.subject = [(entry[0], entry[1]) for entry in self.subject if entry[1]] - if not self.subjectAltName and module.params['useCommonNameForSAN']: + if not self.subjectAltName and module.params['use_common_name_for_san']: for sub in self.subject: if sub[0] in ('commonName', 'CN'): self.subjectAltName = ['DNS:%s' % sub[1]] @@ -944,33 +940,33 @@ class CertificateSigningRequestCryptography(CertificateSigningRequestBase): def main(): module = AnsibleModule( argument_spec=dict( - state=dict(default='present', choices=['present', 'absent'], type='str'), - digest=dict(default='sha256', type='str'), - privatekey_path=dict(require=True, type='path'), + state=dict(type='str', default='present', choices=['absent', 'present']), + digest=dict(type='str', default='sha256'), + privatekey_path=dict(type='path', require=True), privatekey_passphrase=dict(type='str', no_log=True), - version=dict(default='1', type='int'), - force=dict(default=False, type='bool'), - path=dict(required=True, type='path'), + version=dict(type='int', default=1), + force=dict(type='bool', default=False), + path=dict(type='path', required=True), subject=dict(type='dict'), - countryName=dict(aliases=['C', 'country_name'], type='str'), - stateOrProvinceName=dict(aliases=['ST', 'state_or_province_name'], type='str'), - localityName=dict(aliases=['L', 'locality_name'], type='str'), - organizationName=dict(aliases=['O', 'organization_name'], type='str'), - organizationalUnitName=dict(aliases=['OU', 'organizational_unit_name'], type='str'), - commonName=dict(aliases=['CN', 'common_name'], type='str'), - emailAddress=dict(aliases=['E', 'email_address'], type='str'), - subjectAltName=dict(aliases=['subject_alt_name'], type='list', elements='str'), - subjectAltName_critical=dict(aliases=['subject_alt_name_critical'], default=False, type='bool'), - useCommonNameForSAN=dict(type='bool', default=True), - keyUsage=dict(aliases=['key_usage'], type='list', elements='str'), - keyUsage_critical=dict(aliases=['key_usage_critical'], default=False, type='bool'), - extendedKeyUsage=dict(aliases=['extKeyUsage', 'extended_key_usage'], type='list', elements='str'), - extendedKeyUsage_critical=dict(aliases=['extKeyUsage_critical', 'extended_key_usage_critical'], default=False, type='bool'), - basicConstraints=dict(aliases=['basic_constraints'], type='list', elements='str'), - basicConstraints_critical=dict(aliases=['basic_constraints_critical'], default=False, type='bool'), - ocspMustStaple=dict(aliases=['ocsp_must_staple'], default=False, type='bool'), - ocspMustStaple_critical=dict(aliases=['ocsp_must_staple_critical'], default=False, type='bool'), - select_crypto_backend=dict(required=False, choices=['auto', 'pyopenssl', 'cryptography'], default='auto', type='str'), + 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']), + select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']), ), add_file_common_args=True, supports_check_mode=True, diff --git a/lib/ansible/modules/crypto/openssl_dhparam.py b/lib/ansible/modules/crypto/openssl_dhparam.py index 91304e4d3c9..82b7a513d25 100644 --- a/lib/ansible/modules/crypto/openssl_dhparam.py +++ b/lib/ansible/modules/crypto/openssl_dhparam.py @@ -1,80 +1,85 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# (c) 2017, Thom Wiggers +# Copyright: (c) 2017, Thom Wiggers # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type - ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} - -DOCUMENTATION = ''' +DOCUMENTATION = r''' --- module: openssl_dhparam -author: "Thom Wiggers (@thomwiggers)" version_added: "2.5" short_description: Generate OpenSSL Diffie-Hellman Parameters description: - - "This module allows one to (re)generate OpenSSL DH-params. - This module uses file common arguments to specify generated file permissions." + - This module allows one to (re)generate OpenSSL DH-params. + - This module uses file common arguments to specify generated file permissions. requirements: - OpenSSL +author: +- Thom Wiggers (@thomwiggers) options: state: - required: false - default: "present" - choices: [ present, absent ] description: - Whether the parameters should exist or not, taking action if the state is different from what is stated. + type: str + choices: [ absent, present ] + default: present size: - required: false + description: + - Size (in bits) of the generated DH-params. + type: int default: 4096 - description: - - Size (in bits) of the generated DH-params force: - required: false - default: False - type: bool description: - - Should the parameters be regenerated even it it already exists + - Should the parameters be regenerated even it it already exists. + type: bool + default: no path: - required: true description: - Name of the file in which the generated parameters will be saved. -extends_documentation_fragment: files + type: path + required: true +extends_documentation_fragment: +- files +seealso: +- module: openssl_certificate +- module: openssl_csr +- module: openssl_pkcs12 +- module: openssl_privatekey +- module: openssl_publickey ''' -EXAMPLES = ''' -# Generate Diffie-Hellman parameters with the default size (4096 bits) -- openssl_dhparam: +EXAMPLES = r''' +- name: Generate Diffie-Hellman parameters with the default size (4096 bits) + openssl_dhparam: path: /etc/ssl/dhparams.pem -# Generate DH Parameters with a different size (2048 bits) -- openssl_dhparam: +- name: Generate DH Parameters with a different size (2048 bits) + openssl_dhparam: path: /etc/ssl/dhparams.pem size: 2048 -# Force regenerate an DH parameters if they already exist -- openssl_dhparam: +- name: Force regenerate an DH parameters if they already exist + openssl_dhparam: path: /etc/ssl/dhparams.pem - force: True - + force: yes ''' -RETURN = ''' +RETURN = r''' size: - description: Size (in bits) of the Diffie-Hellman parameters + description: Size (in bits) of the Diffie-Hellman parameters. returned: changed or success type: int sample: 4096 filename: - description: Path to the generated Diffie-Hellman parameters + description: Path to the generated Diffie-Hellman parameters. returned: changed or success type: str sample: /etc/ssl/dhparams.pem @@ -97,7 +102,7 @@ class DHParameter(object): def __init__(self, module): self.state = module.params['state'] self.path = module.params['path'] - self.size = int(module.params['size']) + self.size = module.params['size'] self.force = module.params['force'] self.changed = False self.openssl_bin = module.get_bin_path('openssl', True) @@ -150,8 +155,8 @@ class DHParameter(object): match = re.search(r"Parameters:\s+\((\d+) bit\).*", result) if not match: return False # No "xxxx bit" in output - else: - bits = int(match.group(1)) + + bits = int(match.group(1)) # if output contains "WARNING" we've got a problem if "WARNING" in result or "WARNING" in to_native(err): @@ -182,10 +187,10 @@ def main(): module = AnsibleModule( argument_spec=dict( - state=dict(default='present', choices=['present', 'absent'], type='str'), - size=dict(default=4096, type='int'), - force=dict(default=False, type='bool'), - path=dict(required=True, type='path'), + state=dict(type='str', default='present', choices=['absent', 'present']), + size=dict(type='int', default=4096), + force=dict(type='bool', default=False), + path=dict(type='path', required=True), ), supports_check_mode=True, add_file_common_args=True, @@ -195,7 +200,7 @@ def main(): if not os.path.isdir(base_dir): module.fail_json( name=base_dir, - msg='The directory %s does not exist or the file is not a directory' % base_dir + msg="The directory '%s' does not exist or the file is not a directory" % base_dir ) dhparam = DHParameter(module) diff --git a/lib/ansible/modules/crypto/openssl_pkcs12.py b/lib/ansible/modules/crypto/openssl_pkcs12.py index d8cf55f09f9..3cc17fe10a8 100644 --- a/lib/ansible/modules/crypto/openssl_pkcs12.py +++ b/lib/ansible/modules/crypto/openssl_pkcs12.py @@ -1,8 +1,8 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# + +# Copyright: (c) 2017, Guillaume Delpierre # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# Copyright (c) 2017 Guillaume Delpierre from __future__ import absolute_import, division, print_function __metaclass__ = type @@ -11,128 +11,145 @@ ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} -DOCUMENTATION = ''' +DOCUMENTATION = r''' --- module: openssl_pkcs12 -author: "Guillaume Delpierre (@gdelpierre)" +author: +- Guillaume Delpierre (@gdelpierre) version_added: "2.7" -short_description: Generate OpenSSL PKCS#12 archive. +short_description: Generate OpenSSL PKCS#12 archive description: - This module allows one to (re-)generate PKCS#12. requirements: - python-pyOpenSSL options: action: - default: export - choices: ['parse', 'export'] description: - C(export) or C(parse) a PKCS#12. + choices: [ export, parse ] + default: export ca_certificates: description: - List of CA certificate to include. + type: list certificate_path: description: - - The path to read certificates and private keys from. Must be in PEM format. + - The path to read certificates and private keys from. + - Must be in PEM format. + type: path force: - default: False - type: bool description: - Should the file be regenerated even if it already exists. + type: bool + default: no friendly_name: - aliases: ['name'] description: - Specifies the friendly name for the certificate and private key. + type: str + aliases: [ name ] iter_size: - default: 2048 description: - Number of times to repeat the encryption step. + type: int + default: 2048 maciter_size: - default: 1 description: - Number of times to repeat the MAC step. + type: int + default: 1 passphrase: description: - The PKCS#12 password. + type: str path: - required: True description: - Filename to write the PKCS#12 file to. + type: path + required: True privatekey_passphrase: description: - Passphrase source to decrypt any input private keys with. + type: str privatekey_path: description: - File to read private key from. + type: path state: - default: 'present' - choices: ['present', 'absent'] description: - Whether the file should exist or not. All parameters except C(path) are ignored when state is C(absent). + choices: [ absent, present ] + default: present src: description: - PKCS#12 file path to parse. - + type: path extends_documentation_fragment: - files +seealso: +- module: openssl_certificate +- module: openssl_csr +- module: openssl_dhparam +- module: openssl_privatekey +- module: openssl_publickey ''' -EXAMPLES = ''' -- name: 'Generate PKCS#12 file' +EXAMPLES = r''' +- name: Generate PKCS#12 file openssl_pkcs12: action: export - path: '/opt/certs/ansible.p12' - friendly_name: 'raclette' - privatekey_path: '/opt/certs/keys/key.pem' - certificate_path: '/opt/certs/cert.pem' - ca_certificates: '/opt/certs/ca.pem' + path: /opt/certs/ansible.p12 + friendly_name: raclette + privatekey_path: /opt/certs/keys/key.pem + certificate_path: /opt/certs/cert.pem + ca_certificates: /opt/certs/ca.pem state: present -- name: 'Change PKCS#12 file permission' +- name: Change PKCS#12 file permission openssl_pkcs12: action: export - path: '/opt/certs/ansible.p12' - friendly_name: 'raclette' - privatekey_path: '/opt/certs/keys/key.pem' - certificate_path: '/opt/certs/cert.pem' - ca_certificates: '/opt/certs/ca.pem' + path: /opt/certs/ansible.p12 + friendly_name: raclette + privatekey_path: /opt/certs/keys/key.pem + certificate_path: /opt/certs/cert.pem + ca_certificates: /opt/certs/ca.pem state: present - mode: 0600 + mode: '0600' -- name: 'Regen PKCS#12 file' +- name: Regen PKCS#12 file openssl_pkcs12: action: export - src: '/opt/certs/ansible.p12' - path: '/opt/certs/ansible.p12' - friendly_name: 'raclette' - privatekey_path: '/opt/certs/keys/key.pem' - certificate_path: '/opt/certs/cert.pem' - ca_certificates: '/opt/certs/ca.pem' + src: /opt/certs/ansible.p12 + path: /opt/certs/ansible.p12 + friendly_name: raclette + privatekey_path: /opt/certs/keys/key.pem + certificate_path: /opt/certs/cert.pem + ca_certificates: /opt/certs/ca.pem state: present - mode: 0600 - force: True + mode: '0600' + force: yes -- name: 'Dump/Parse PKCS#12 file' +- name: Dump/Parse PKCS#12 file openssl_pkcs12: action: parse - src: '/opt/certs/ansible.p12' - path: '/opt/certs/ansible.pem' + src: /opt/certs/ansible.p12 + path: /opt/certs/ansible.pem state: present -- name: 'Remove PKCS#12 file' +- name: Remove PKCS#12 file openssl_pkcs12: - path: '/opt/certs/ansible.p12' + path: /opt/certs/ansible.p12 state: absent ''' -RETURN = ''' +RETURN = r''' filename: description: Path to the generate PKCS#12 file. returned: changed or success type: str sample: /opt/certs/ansible.p12 privatekey: - description: Path to the TLS/SSL private key the public key was generated from + description: Path to the TLS/SSL private key the public key was generated from. returned: changed or success type: str sample: /etc/ssl/private/ansible.com.pem @@ -280,8 +297,7 @@ class Pkcs(crypto_utils.OpenSSLObject): def main(): argument_spec = dict( - action=dict(type='str', default='export', - choices=['parse', 'export']), + action=dict(type='str', default='export', choices=['export', 'parse']), ca_certificates=dict(type='list', elements='path'), certificate_path=dict(type='path'), force=dict(type='bool', default=False), @@ -292,8 +308,7 @@ def main(): path=dict(type='path', required=True), privatekey_passphrase=dict(type='str', no_log=True), privatekey_path=dict(type='path'), - state=dict(type='str', default='present', - choices=['present', 'absent']), + state=dict(type='str', default='present', choices=['absent', 'present']), src=dict(type='path'), ) @@ -320,8 +335,7 @@ def main(): if not os.path.isdir(base_dir): module.fail_json( name=base_dir, - msg='The directory %s does not exist or ' - 'the path is not a directory' % base_dir + msg="The directory '%s' does not exist or the path is not a directory" % base_dir ) pkcs12 = Pkcs(module) diff --git a/lib/ansible/modules/crypto/openssl_privatekey.py b/lib/ansible/modules/crypto/openssl_privatekey.py index 09fd66dad62..360f1d8c1ac 100644 --- a/lib/ansible/modules/crypto/openssl_privatekey.py +++ b/lib/ansible/modules/crypto/openssl_privatekey.py @@ -1,66 +1,65 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# (c) 2016, Yanis Guenane +# Copyright: (c) 2016, Yanis Guenane # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type - ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} - -DOCUMENTATION = ''' +DOCUMENTATION = r''' --- module: openssl_privatekey -author: - - "Yanis Guenane (@Spredzy)" - - "Felix Fontein (@felixfontein)" version_added: "2.3" -short_description: Generate OpenSSL private keys. +short_description: Generate OpenSSL private keys description: - - "This module allows one to (re)generate OpenSSL private keys. One can - generate L(RSA,https://en.wikipedia.org/wiki/RSA_(cryptosystem)), - L(DSA,https://en.wikipedia.org/wiki/Digital_Signature_Algorithm) or - L(ECC,https://en.wikipedia.org/wiki/Elliptic-curve_cryptography) - private keys. Keys are generated in PEM format." - - "The module can use the cryptography Python library, or the pyOpenSSL Python - library. By default, it tries to detect which one is available. This can be - overridden with the I(select_crypto_backend) option." + - This module allows one to (re)generate OpenSSL private keys. + - One can generate L(RSA,https://en.wikipedia.org/wiki/RSA_(cryptosystem)), + L(DSA,https://en.wikipedia.org/wiki/Digital_Signature_Algorithm) or + L(ECC,https://en.wikipedia.org/wiki/Elliptic-curve_cryptography) + private keys. + - Keys are generated in PEM format. + - The module can use the cryptography Python library, or the pyOpenSSL Python + library. By default, it tries to detect which one is available. This can be + overridden with the I(select_crypto_backend) option." requirements: - - "One of the following Python libraries:" - - "cryptography >= 1.2.3 (older versions might work as well)" - - "pyOpenSSL" + - Either cryptography >= 1.2.3 (older versions might work as well) + - Or pyOpenSSL +author: + - Yanis Guenane (@Spredzy) + - Felix Fontein (@felixfontein) options: state: - required: false - default: "present" - choices: [ present, absent ] description: - Whether the private key should exist or not, taking action if the state is different from what is stated. + type: str + choices: [ absent, present ] + default: present size: - required: false + description: + - Size (in bits) of the TLS/SSL key to generate. + type: int default: 4096 - description: - - Size (in bits) of the TLS/SSL key to generate type: - required: false - default: "RSA" - choices: - - RSA - - DSA - - ECC - # - X448 - # - X25519 description: - - The algorithm used to generate the TLS/SSL private key - - "Note that C(ECC) requires the C(cryptography) backend. Depending on the curve, you need a newer - version of the cryptography backend." + - The algorithm used to generate the TLS/SSL private key. + - Note that C(ECC) requires the C(cryptography) backend. + - Depending on the curve, you need a newer version of the cryptography backend. + type: str + #choices: [ DSA, ECC, RSA, X448, X25519 ] + choices: [ DSA, ECC, RSA ] + default: RSA curve: - required: false + description: + - Note that not all curves are supported by all versions of C(cryptography). + - For maximal interoperability, C(secp384r1) or C(secp256k1) should be used. + - We use the curve names as defined in the + L(IANA registry for TLS,https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8). + type: str choices: - secp384r1 - secp521r1 @@ -80,105 +79,100 @@ options: - sect283r1 - sect233r1 - sect163r2 - description: - - Note that not all curves are supported by all versions of C(cryptography). - - For maximal interoperability, C(secp384r1) or C(secp256k1) should be used. - - We use the curve names as defined in the - L(IANA registry for TLS,https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8). version_added: "2.8" force: - required: false - default: False - type: bool description: - - Should the key be regenerated even if it already exists + - Should the key be regenerated even if it already exists. + type: bool + default: no path: - required: true description: - Name of the file in which the generated TLS/SSL private key will be written. It will have 0600 mode. + type: path + required: true passphrase: - required: false description: - The passphrase for the private key. + type: str version_added: "2.4" cipher: - required: false description: - The cipher to encrypt the private key. (cipher can be found by running `openssl list-cipher-algorithms`) - When using the C(cryptography) backend, use C(auto). + type: str version_added: "2.4" select_crypto_backend: description: - - "Determines which crypto backend to use. The default choice is C(auto), - which tries to use C(cryptography) if available, and falls back to - C(pyopenssl)." - - "If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) - library." - - "If set to C(cryptography), will try to use the - L(cryptography,https://cryptography.io/) library." + - Determines which crypto backend to use. + - The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to C(pyopenssl). + - If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) library. + - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library. type: str - default: 'auto' - choices: - - auto - - cryptography - - pyopenssl + choices: [ auto, cryptography, pyopenssl ] + default: auto version_added: "2.8" -extends_documentation_fragment: files +extends_documentation_fragment: +- files +seealso: +- module: openssl_certificate +- module: openssl_csr +- module: openssl_dhparam +- module: openssl_pkcs12 +- module: openssl_publickey ''' -EXAMPLES = ''' -# Generate an OpenSSL private key with the default values (4096 bits, RSA) -- openssl_privatekey: +EXAMPLES = r''' +- name: Generate an OpenSSL private key with the default values (4096 bits, RSA) + openssl_privatekey: path: /etc/ssl/private/ansible.com.pem -# Generate an OpenSSL private key with the default values (4096 bits, RSA) -# and a passphrase -- openssl_privatekey: +- name: Generate an OpenSSL private key with the default values (4096 bits, RSA) and a passphrase + openssl_privatekey: path: /etc/ssl/private/ansible.com.pem passphrase: ansible cipher: aes256 -# Generate an OpenSSL private key with a different size (2048 bits) -- openssl_privatekey: +- name: Generate an OpenSSL private key with a different size (2048 bits) + openssl_privatekey: path: /etc/ssl/private/ansible.com.pem size: 2048 -# Force regenerate an OpenSSL private key if it already exists -- openssl_privatekey: +- name: Force regenerate an OpenSSL private key if it already exists + openssl_privatekey: path: /etc/ssl/private/ansible.com.pem - force: True + force: yes -# Generate an OpenSSL private key with a different algorithm (DSA) -- openssl_privatekey: +- name: Generate an OpenSSL private key with a different algorithm (DSA) + openssl_privatekey: path: /etc/ssl/private/ansible.com.pem type: DSA ''' -RETURN = ''' +RETURN = r''' size: - description: Size (in bits) of the TLS/SSL private key + description: Size (in bits) of the TLS/SSL private key. returned: changed or success type: int sample: 4096 type: - description: Algorithm used to generate the TLS/SSL private key + description: Algorithm used to generate the TLS/SSL private key. returned: changed or success type: str sample: RSA curve: - description: Elliptic curve used to generate the TLS/SSL private key + description: Elliptic curve used to generate the TLS/SSL private key. returned: changed or success, and I(type) is C(ECC) type: str sample: secp256k1 filename: - description: Path to the generated TLS/SSL private key file + description: Path to the generated TLS/SSL private key file. returned: changed or success type: str sample: /etc/ssl/private/ansible.com.pem fingerprint: - description: The fingerprint of the public key. Fingerprint will be generated for - each C(hashlib.algorithms) available. - The PyOpenSSL backend requires PyOpenSSL >= 16.0 for meaningful output. + description: + - The fingerprint of the public key. Fingerprint will be generated for each C(hashlib.algorithms) available. + - The PyOpenSSL backend requires PyOpenSSL >= 16.0 for meaningful output. returned: changed or success type: dict sample: diff --git a/lib/ansible/modules/crypto/openssl_publickey.py b/lib/ansible/modules/crypto/openssl_publickey.py index f049e3d39a3..0a616497aec 100644 --- a/lib/ansible/modules/crypto/openssl_publickey.py +++ b/lib/ansible/modules/crypto/openssl_publickey.py @@ -1,117 +1,125 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# (c) 2016, Yanis Guenane +# Copyright: (c) 2016, Yanis Guenane # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type - ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} - -DOCUMENTATION = ''' +DOCUMENTATION = r''' --- module: openssl_publickey -author: "Yanis Guenane (@Spredzy)" version_added: "2.3" short_description: Generate an OpenSSL public key from its private key. description: - - "This module allows one to (re)generate OpenSSL public keys from their private keys. - It uses the pyOpenSSL python library to interact with openssl. Keys are generated - in PEM format. This module works only if the version of PyOpenSSL is recent enough (> 16.0.0)." + - This module allows one to (re)generate OpenSSL public keys from their private keys. + - It uses the pyOpenSSL python library to interact with openssl. + - Keys are generated in PEM format. + - This module works only if the version of PyOpenSSL is recent enough (> 16.0.0). requirements: - - "python-pyOpenSSL" + - python-pyOpenSSL +author: +- Yanis Guenane (@Spredzy) options: state: - required: false - default: "present" - choices: [ present, absent ] description: - Whether the public key should exist or not, taking action if the state is different from what is stated. + type: str + choices: [ absent, present ] + default: present force: - required: false - default: False - type: bool description: - - Should the key be regenerated even it it already exists + - Should the key be regenerated even it it already exists. + type: bool + default: no format: - required: false - default: PEM - choices: [ PEM, OpenSSH ] description: - The format of the public key. + type: str + choices: [ OpenSSH, PEM ] + default: PEM version_added: "2.4" path: - required: true description: - Name of the file in which the generated TLS/SSL public key will be written. - privatekey_path: + type: path required: true + privatekey_path: description: - Path to the TLS/SSL private key from which to generate the public key. + type: path + required: true privatekey_passphrase: - required: false description: - The passphrase for the privatekey. + type: str version_added: "2.4" -extends_documentation_fragment: files +extends_documentation_fragment: +- files +seealso: +- module: openssl_certificate +- module: openssl_csr +- module: openssl_dhparam +- module: openssl_pkcs12 +- module: openssl_privatekey ''' -EXAMPLES = ''' -# Generate an OpenSSL public key in PEM format. -- openssl_publickey: +EXAMPLES = r''' +- name: Generate an OpenSSL public key in PEM format + openssl_publickey: path: /etc/ssl/public/ansible.com.pem privatekey_path: /etc/ssl/private/ansible.com.pem -# Generate an OpenSSL public key in OpenSSH v2 format. -- openssl_publickey: +- name: Generate an OpenSSL public key in OpenSSH v2 format + openssl_publickey: path: /etc/ssl/public/ansible.com.pem privatekey_path: /etc/ssl/private/ansible.com.pem format: OpenSSH -# Generate an OpenSSL public key with a passphrase protected -# private key -- openssl_publickey: +- name: Generate an OpenSSL public key with a passphrase protected private key + openssl_publickey: path: /etc/ssl/public/ansible.com.pem privatekey_path: /etc/ssl/private/ansible.com.pem privatekey_passphrase: ansible -# Force regenerate an OpenSSL public key if it already exists -- openssl_publickey: +- name: Force regenerate an OpenSSL public key if it already exists + openssl_publickey: path: /etc/ssl/public/ansible.com.pem privatekey_path: /etc/ssl/private/ansible.com.pem - force: True + force: yes -# Remove an OpenSSL public key -- openssl_publickey: +- name: Remove an OpenSSL public key + openssl_publickey: path: /etc/ssl/public/ansible.com.pem privatekey_path: /etc/ssl/private/ansible.com.pem state: absent ''' -RETURN = ''' +RETURN = r''' privatekey: - description: Path to the TLS/SSL private key the public key was generated from + description: Path to the TLS/SSL private key the public key was generated from. returned: changed or success type: str sample: /etc/ssl/private/ansible.com.pem format: - description: The format of the public key (PEM, OpenSSH, ...) + description: The format of the public key (PEM, OpenSSH, ...). returned: changed or success type: str sample: PEM filename: - description: Path to the generated TLS/SSL public key file + description: Path to the generated TLS/SSL public key file. returned: changed or success type: str sample: /etc/ssl/public/ansible.com.pem fingerprint: - description: The fingerprint of the public key. Fingerprint will be generated for each hashlib.algorithms available. - Requires PyOpenSSL >= 16.0 for meaningful output. + description: + - The fingerprint of the public key. Fingerprint will be generated for each hashlib.algorithms available. + - Requires PyOpenSSL >= 16.0 for meaningful output. returned: changed or success type: dict sample: @@ -259,16 +267,16 @@ def main(): module = AnsibleModule( argument_spec=dict( - state=dict(default='present', choices=['present', 'absent'], type='str'), - force=dict(default=False, type='bool'), - path=dict(required=True, type='path'), + state=dict(type='str', default='present', choices=['present', 'absent']), + force=dict(type='bool', default=False), + path=dict(type='path', required=True), privatekey_path=dict(type='path'), - format=dict(type='str', choices=['PEM', 'OpenSSH'], default='PEM'), + format=dict(type='str', default='PEM', choices=['OpenSSH', 'PEM']), privatekey_passphrase=dict(type='str', no_log=True), ), supports_check_mode=True, add_file_common_args=True, - required_if=[('state', 'present', ['privatekey_path'])] + required_if=[('state', 'present', ['privatekey_path'])], ) if not pyopenssl_found: @@ -278,7 +286,7 @@ def main(): if not os.path.isdir(base_dir): module.fail_json( name=base_dir, - msg='The directory %s does not exist or the file is not a directory' % base_dir + msg="The directory '%s' does not exist or the file is not a directory" % base_dir ) public_key = PublicKey(module) diff --git a/test/sanity/validate-modules/ignore.txt b/test/sanity/validate-modules/ignore.txt index 6d7b4cbcf7b..1f1e9b7f80e 100644 --- a/test/sanity/validate-modules/ignore.txt +++ b/test/sanity/validate-modules/ignore.txt @@ -360,8 +360,6 @@ lib/ansible/modules/clustering/znode.py E326 lib/ansible/modules/commands/command.py E322 lib/ansible/modules/commands/command.py E323 lib/ansible/modules/commands/command.py E325 -lib/ansible/modules/crypto/openssl_certificate.py E325 -lib/ansible/modules/crypto/openssl_csr.py E325 lib/ansible/modules/database/influxdb/influxdb_database.py E324 lib/ansible/modules/database/influxdb/influxdb_query.py E324 lib/ansible/modules/database/influxdb/influxdb_retention_policy.py E324