Skip to content

Commit

Permalink
Remove PyOpenSSL backends (except for openssl_pkcs12) (#273)
Browse files Browse the repository at this point in the history
* Remove Ubuntu 16.04 (Xenial Xerus) from CI.

* Removing PyOpenSSL backend from everywhere but openssl_pkcs12.

* Remove PyOpenSSL support from module_utils that's not needed for openssl_pkcs12.

* Add changelog fragment.
  • Loading branch information
felixfontein authored Sep 28, 2021
1 parent 24e7d07 commit f644db3
Show file tree
Hide file tree
Showing 72 changed files with 234 additions and 2,645 deletions.
6 changes: 2 additions & 4 deletions .azure-pipelines/azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,6 @@ stages:
test: centos6
- name: Fedora 31
test: fedora31
- name: Ubuntu 16.04
test: ubuntu1604
- stage: Docker_2_9
displayName: Docker 2.9
dependsOn: []
Expand All @@ -193,8 +191,8 @@ stages:
test: centos7
- name: Fedora 31
test: fedora31
- name: Ubuntu 16.04
test: ubuntu1604
- name: openSUSE 15 py3
test: opensuse15
- name: Ubuntu 18.04
test: ubuntu1804

Expand Down
17 changes: 17 additions & 0 deletions changelogs/fragments/273-pyopenssl-removal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
removed_features:
- "get_certificate - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
- "openssl_csr - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
- "openssl_csr_info - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
- "openssl_csr_pipe - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
- "openssl_privatekey - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
- "openssl_privatekey_info - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
- "openssl_privatekey_pipe - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
- "openssl_publickey - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
- "openssl_publickey_info - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
- "openssl_signature - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
- "openssl_signature_info - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
- "x509_certificate - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
- "x509_certificate_info - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
- "x509_certificate_pipe - removed the ``pyopenssl`` backend (https:/ansible-collections/community.crypto/pull/273)."
breaking_changes:
- "module_utils - removed various PyOpenSSL support functions and default backend values that are not needed for the openssl_pkcs12 module (https:/ansible-collections/community.crypto/pull/273)."
2 changes: 1 addition & 1 deletion plugins/doc_fragments/acme.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class ModuleDocFragment(object):
key."
- "Private keys can be created with the
M(community.crypto.openssl_privatekey) or M(community.crypto.openssl_privatekey_pipe)
modules. If the requisites (pyOpenSSL or cryptography) are not available,
modules. If the requisite (cryptography) is not available,
keys can also be created directly with the C(openssl) command line tool:
RSA keys can be created with C(openssl genrsa ...). Elliptic curve keys
can be created with C(openssl ecparam -genkey ...). Any other tool creating
Expand Down
14 changes: 4 additions & 10 deletions plugins/doc_fragments/module_certificate.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,9 @@ class ModuleDocFragment(object):
DOCUMENTATION = r'''
description:
- This module allows one to (re)generate OpenSSL certificates.
- It uses the pyOpenSSL or cryptography python library to interact with OpenSSL.
- If both the cryptography and PyOpenSSL libraries are available (and meet the minimum version requirements)
cryptography will be preferred as a backend over PyOpenSSL (unless the backend is forced with C(select_crypto_backend)).
Please note that the PyOpenSSL backend was deprecated in Ansible 2.9 and will be removed in community.crypto 2.0.0.
- It uses the cryptography python library to interact with OpenSSL.
requirements:
- PyOpenSSL >= 0.15 or cryptography >= 1.6 (if using C(selfsigned), C(ownca) or C(assertonly) provider)
- cryptography >= 1.6 (if using C(selfsigned), C(ownca) or C(assertonly) provider)
options:
force:
description:
Expand Down Expand Up @@ -58,14 +55,11 @@ class ModuleDocFragment(object):
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.
- The default choice is C(auto), which tries to use C(cryptography) if available.
- If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in community.crypto 2.0.0.
From that point on, only the C(cryptography) backend will be available.
type: str
default: auto
choices: [ auto, cryptography, pyopenssl ]
choices: [ auto, cryptography ]
notes:
- All ASN.1 TIME values should be specified following the YYYYMMDDHHMMSSZ pattern.
Expand Down
14 changes: 3 additions & 11 deletions plugins/doc_fragments/module_csr.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,8 @@ class ModuleDocFragment(object):
- This module allows one to (re)generate OpenSSL certificate signing requests.
- This module supports the subjectAltName, keyUsage, extendedKeyUsage, basicConstraints and OCSP Must Staple
extensions.
- "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. Please note that the
PyOpenSSL backend was deprecated in Ansible 2.9 and will be removed in community.crypto 2.0.0."
requirements:
- Either cryptography >= 1.3
- Or pyOpenSSL >= 0.15
- cryptography >= 1.3
options:
digest:
description:
Expand Down Expand Up @@ -196,14 +191,11 @@ class ModuleDocFragment(object):
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.
- The default choice is C(auto), which tries to use C(cryptography) if available.
- If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in community.crypto 2.0.0.
From that point on, only the C(cryptography) backend will be available.
type: str
default: auto
choices: [ auto, cryptography, pyopenssl ]
choices: [ auto, cryptography ]
create_subject_key_identifier:
description:
- Create the Subject Key Identifier from the public key.
Expand Down
21 changes: 4 additions & 17 deletions plugins/doc_fragments/module_privatekey.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,8 @@ class ModuleDocFragment(object):
(or specify none), change the keysize, etc., the private key will be
regenerated. If you are concerned that this could B(overwrite your private key),
consider using the I(backup) option."
- "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. Please note that the
PyOpenSSL backend was deprecated in Ansible 2.9 and will be removed in community.crypto 2.0.0."
requirements:
- Either cryptography >= 1.2.3 (older versions might work as well)
- Or pyOpenSSL
- cryptography >= 1.2.3 (older versions might work as well)
options:
size:
description:
Expand Down Expand Up @@ -80,22 +75,16 @@ class ModuleDocFragment(object):
type: str
cipher:
description:
- The cipher to encrypt the private key. (Valid values can be found by
running `openssl list -cipher-algorithms` or `openssl list-cipher-algorithms`,
depending on your OpenSSL version.)
- When using the C(cryptography) backend, use C(auto).
- The cipher to encrypt the private key. Must be C(auto).
type: str
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.
- The default choice is C(auto), which tries to use C(cryptography) if available.
- If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in community.crypto 2.0.0.
From that point on, only the C(cryptography) backend will be available.
type: str
default: auto
choices: [ auto, cryptography, pyopenssl ]
choices: [ auto, cryptography ]
format:
description:
- Determines which format the private key is written in. By default, PKCS1 (traditional OpenSSL format)
Expand All @@ -105,8 +94,6 @@ class ModuleDocFragment(object):
selected one for generation.
- Note that if the format for an existing private key mismatches, the key is B(regenerated) by default.
To change this behavior, use the I(format_mismatch) option.
- The I(format) option is only supported by the C(cryptography) backend. The C(pyopenssl) backend will
fail if a value different from C(auto_ignore) is used.
type: str
default: auto_ignore
choices: [ pkcs1, pkcs8, raw, auto, auto_ignore ]
Expand Down
7 changes: 0 additions & 7 deletions plugins/module_utils/crypto/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
# please stop doing so.

from .basic import (
HAS_PYOPENSSL,
CRYPTOGRAPHY_HAS_X25519,
CRYPTOGRAPHY_HAS_X25519_FULL,
CRYPTOGRAPHY_HAS_X448,
Expand Down Expand Up @@ -74,12 +73,6 @@
from ._objects import NORMALIZE_NAMES as _NORMALIZE_NAMES
from ._objects import NORMALIZE_NAMES_SHORT as _NORMALIZE_NAMES_SHORT

from .pyopenssl_support import (
pyopenssl_normalize_name,
pyopenssl_get_extensions_from_cert,
pyopenssl_get_extensions_from_csr,
)

from .support import (
get_fingerprint_of_bytes,
get_fingerprint,
Expand Down
8 changes: 0 additions & 8 deletions plugins/module_utils/crypto/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,6 @@

from distutils.version import LooseVersion

try:
import OpenSSL # noqa
from OpenSSL import crypto # noqa
HAS_PYOPENSSL = True
except ImportError:
# Error handled in the calling module.
HAS_PYOPENSSL = False

try:
import cryptography
from cryptography import x509
Expand Down
88 changes: 7 additions & 81 deletions plugins/module_utils/crypto/module_backends/certificate.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,6 @@
)

MINIMAL_CRYPTOGRAPHY_VERSION = '1.6'
MINIMAL_PYOPENSSL_VERSION = '0.15'

PYOPENSSL_IMP_ERR = None
try:
import OpenSSL
from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError:
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:
PYOPENSSL_FOUND = True

CRYPTOGRAPHY_IMP_ERR = None
CRYPTOGRAPHY_VERSION = None
Expand Down Expand Up @@ -173,43 +161,12 @@ def _ensure_existing_certificate_loaded(self):

def _check_privatekey(self):
"""Check whether provided parameters match, assuming self.existing_certificate and self.privatekey have been populated."""
if self.backend == 'pyopenssl':
ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_2_METHOD)
ctx.use_privatekey(self.privatekey)
ctx.use_certificate(self.existing_certificate)
try:
ctx.check_privatekey()
return True
except OpenSSL.SSL.Error:
return False
elif self.backend == 'cryptography':
if self.backend == 'cryptography':
return cryptography_compare_public_keys(self.existing_certificate.public_key(), self.privatekey.public_key())

def _check_csr(self):
"""Check whether provided parameters match, assuming self.existing_certificate and self.csr have been populated."""
if self.backend == 'pyopenssl':
# Verify that CSR is signed by certificate's private key
try:
self.csr.verify(self.existing_certificate.get_pubkey())
except OpenSSL.crypto.Error:
return False
# Check subject
if self.check_csr_subject and self.csr.get_subject() != self.existing_certificate.get_subject():
return False
# Check extensions
if not self.check_csr_extensions:
return True
csr_extensions = self.csr.get_extensions()
cert_extension_count = self.existing_certificate.get_extension_count()
if len(csr_extensions) != cert_extension_count:
return False
for extension_number in range(0, cert_extension_count):
cert_extension = self.existing_certificate.get_extension(extension_number)
csr_extension = filter(lambda extension: extension.get_short_name() == cert_extension.get_short_name(), csr_extensions)
if cert_extension.get_data() != list(csr_extension)[0].get_data():
return False
return True
elif self.backend == 'cryptography':
if self.backend == 'cryptography':
# Verify that CSR is signed by certificate's private key
if not self.csr.is_signature_valid:
return False
Expand Down Expand Up @@ -244,10 +201,6 @@ def _check_csr(self):

def _check_subject_key_identifier(self):
"""Check whether Subject Key Identifier matches, assuming self.existing_certificate has been populated."""
if self.backend != 'cryptography':
# We do not support SKI with pyOpenSSL backend
return True

# Get hold of certificate's SKI
try:
ext = self.existing_certificate.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
Expand Down Expand Up @@ -328,10 +281,6 @@ def validate_module_args(self, module):
def needs_version_two_certs(self, module):
"""Whether the provider needs to create a version 2 certificate."""

def needs_pyopenssl_get_extensions(self, module):
"""Whether the provider needs to use get_extensions() with pyOpenSSL."""
return True

@abc.abstractmethod
def create_backend(self, module, backend):
"""Create an implementation for a backend.
Expand All @@ -352,45 +301,22 @@ def select_backend(module, backend, provider):
if backend == 'auto':
# Detect what backend we can use
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)

# If cryptography is available we'll use it
if can_use_cryptography:
backend = 'cryptography'
elif can_use_pyopenssl:
backend = 'pyopenssl'

if provider.needs_version_two_certs(module):
module.warn('crypto backend forced to pyopenssl. The cryptography library does not support v2 certificates')
backend = 'pyopenssl'

# Fail if no backend has been found
if backend == 'auto':
module.fail_json(msg=("Can't detect any of the required Python libraries "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format(
MINIMAL_CRYPTOGRAPHY_VERSION,
MINIMAL_PYOPENSSL_VERSION))

if backend == 'pyopenssl':
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated',
version='2.0.0', collection_name='community.crypto')

if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
exception=PYOPENSSL_IMP_ERR)
if provider.needs_pyopenssl_get_extensions(module):
try:
getattr(crypto.X509Req, 'get_extensions')
except AttributeError:
module.fail_json(msg='You need to have PyOpenSSL>=0.15')
module.fail_json(msg=("Can't detect the required Python library "
"cryptography (>= {0})").format(MINIMAL_CRYPTOGRAPHY_VERSION))

elif backend == 'cryptography':
if backend == 'cryptography':
if not CRYPTOGRAPHY_FOUND:
module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
exception=CRYPTOGRAPHY_IMP_ERR)
if provider.needs_version_two_certs(module):
module.fail_json(msg='The cryptography backend does not support v2 certificates, '
'use select_crypto_backend=pyopenssl for v2 certificates')
module.fail_json(msg='The cryptography backend does not support v2 certificates')

return provider.create_backend(module, backend)

Expand All @@ -402,7 +328,7 @@ def get_certificate_argument_spec():
force=dict(type='bool', default=False,),
csr_path=dict(type='path'),
csr_content=dict(type='str'),
select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']),
select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography']),

# General properties of a certificate
privatekey_path=dict(type='path'),
Expand Down
Loading

0 comments on commit f644db3

Please sign in to comment.