Drop M2Crypto

This commit is contained in:
Jakub Warmuz
2015-07-06 12:18:27 +00:00
parent 61aa29d28c
commit 02e7154c0d
17 changed files with 204 additions and 217 deletions
-6
View File
@@ -44,20 +44,14 @@ else
fi
# dpkg-dev: dpkg-architecture binary necessary to compile M2Crypto, c.f.
# #276, https://github.com/martinpaljak/M2Crypto/issues/62,
# M2Crypto setup.py:add_multiarch_paths
apt-get install -y --no-install-recommends \
git-core \
python \
python-dev \
"$virtualenv" \
gcc \
swig \
dialog \
libaugeas0 \
libssl-dev \
libffi-dev \
ca-certificates \
dpkg-dev \
-1
View File
@@ -12,7 +12,6 @@ yum install -y \
python-virtualenv \
python-devel \
gcc \
swig \
dialog \
augeas-libs \
openssl-devel \
+1 -1
View File
@@ -1,2 +1,2 @@
#!/bin/sh
brew install augeas swig
brew install augeas
+1 -1
View File
@@ -23,7 +23,7 @@ import mock
# http://docs.readthedocs.org/en/latest/faq.html#i-get-import-errors-on-libraries-that-depend-on-c-modules
# c.f. #262
sys.modules.update(
(mod_name, mock.MagicMock()) for mod_name in ['augeas', 'M2Crypto'])
(mod_name, mock.MagicMock()) for mod_name in ['augeas'])
here = os.path.abspath(os.path.dirname(__file__))
-18
View File
@@ -52,7 +52,6 @@ are provided mainly for the :ref:`developers <hacking>` reference.
In general:
* ``sudo`` is required as a suggested way of running privileged process
* `SWIG`_ is required for compiling `M2Crypto`_
* `Augeas`_ is required for the Python bindings
@@ -102,14 +101,6 @@ Centos 7
sudo ./bootstrap/centos.sh
For installation run this modified command (note the trailing
backslash):
.. code-block:: shell
SWIG_FEATURES="-includeall -D__`uname -m`__-I/usr/include/openssl" \
./venv/bin/pip install -r requirements.txt .
Installation
============
@@ -127,13 +118,6 @@ Installation
your operating system and are **not supported** by the
Let's Encrypt team!
.. note:: If your operating system uses SWIG 3.0.5+, you will need to
run ``pip install -r requirements-swig-3.0.5.txt -r
requirements.txt .`` instead. Known affected systems:
* Fedora 22
* some versions of Mac OS X
Usage
=====
@@ -152,6 +136,4 @@ The ``letsencrypt`` commandline tool has a builtin help:
.. _Augeas: http://augeas.net/
.. _M2Crypto: https://github.com/M2Crypto/M2Crypto
.. _SWIG: http://www.swig.org/
.. _Virtualenv: https://virtualenv.pypa.io
+4 -1
View File
@@ -58,7 +58,10 @@ class DVSNI(AnnotatedChallenge):
"""
response = challenges.DVSNIResponse(s=s)
cert_pem = crypto_util.make_ss_cert(self.key.pem, [
self.nonce_domain, self.domain, response.z_domain(self.challb)])
# TODO: setting nonce_domain first will cause "Error:
# [('asn1 encoding routines', 'ASN1_mbstring_ncopy',
# 'string too long')]" (get_subject().CN = domains[0])
self.domain, self.nonce_domain, response.z_domain(self.challb)])
return cert_pem, response
+4 -3
View File
@@ -3,7 +3,6 @@ import logging
import os
import pkg_resources
import M2Crypto
import OpenSSL.crypto
import zope.component
@@ -435,8 +434,10 @@ def validate_key_csr(privkey, csr=None):
if csr:
if csr.form == "der":
csr_obj = M2Crypto.X509.load_request_der_string(csr.data)
csr = le_util.CSR(csr.file, csr_obj.as_pem(), "der")
csr_obj = OpenSSL.crypto.load_certificate_request(
OpenSSL.crypto.FILETYPE_ASN1, csr.data)
csr = le_util.CSR(csr.file, OpenSSL.crypto.dump_certificate(
OpenSSL.crypto.FILETYPE_PEM, csr_obj), "pem")
# If CSR is provided, it must be readable and valid.
if csr.data and not crypto_util.valid_csr(csr.data):
+96 -62
View File
@@ -4,13 +4,13 @@
is capable of handling the signatures.
"""
import datetime
import logging
import os
import time
import M2Crypto
import OpenSSL
from letsencrypt import errors
from letsencrypt import le_util
@@ -86,7 +86,7 @@ def init_save_csr(privkey, names, path, csrname="csr-letsencrypt.pem"):
def make_csr(key_str, domains):
"""Generate a CSR.
:param str key_str: RSA key.
:param str key_str: PEM-encoded RSA key.
:param list domains: Domains included in the certificate.
.. todo:: Detect duplicates in `domains`? Using a set doesn't
@@ -97,25 +97,23 @@ def make_csr(key_str, domains):
"""
assert domains, "Must provide one or more hostnames for the CSR."
rsa_key = M2Crypto.RSA.load_key_string(key_str)
pubkey = M2Crypto.EVP.PKey()
pubkey.assign_rsa(rsa_key)
csr = M2Crypto.X509.Request()
csr.set_pubkey(pubkey)
# TODO: what to put into csr.get_subject()?
extstack = M2Crypto.X509.X509_Extension_Stack()
ext = M2Crypto.X509.new_extension(
"subjectAltName", ", ".join("DNS:%s" % d for d in domains))
extstack.push(ext)
csr.add_extensions(extstack)
csr.sign(pubkey, "sha256")
assert csr.verify(pubkey)
pubkey2 = csr.get_pubkey()
assert csr.verify(pubkey2)
return csr.as_pem(), csr.as_der()
pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, key_str)
req = OpenSSL.crypto.X509Req()
req.get_subject().CN = domains[0]
# TODO: what to put into req.get_subject()?
# TODO: put SAN if len(domains) > 1
req.add_extensions([
OpenSSL.crypto.X509Extension(
"subjectAltName",
critical=False,
value=", ".join("DNS:%s" % d for d in domains)
),
])
req.set_pubkey(pkey)
req.sign(pkey, "sha256")
return tuple(OpenSSL.crypto.dump_certificate_request(method, req)
for method in (OpenSSL.crypto.FILETYPE_PEM,
OpenSSL.crypto.FILETYPE_ASN1))
# WARNING: the csr and private key file are possible attack vectors for TOCTOU
@@ -135,9 +133,11 @@ def valid_csr(csr):
"""
try:
csr_obj = M2Crypto.X509.load_request_string(csr)
return bool(csr_obj.verify(csr_obj.get_pubkey()))
except M2Crypto.X509.X509Error:
req = OpenSSL.crypto.load_certificate_request(
OpenSSL.crypto.FILETYPE_PEM, csr)
return req.verify(req.get_pubkey())
except OpenSSL.crypto.Error as error:
logger.debug(error, exc_info=True)
return False
@@ -145,15 +145,20 @@ def csr_matches_pubkey(csr, privkey):
"""Does private key correspond to the subject public key in the CSR?
:param str csr: CSR in PEM.
:param str privkey: Private key file contents
:param str privkey: Private key file contents (PEM)
:returns: Correspondence of private key to CSR subject public key.
:rtype: bool
"""
csr_obj = M2Crypto.X509.load_request_string(csr)
privkey_obj = M2Crypto.RSA.load_key_string(privkey)
return csr_obj.get_pubkey().get_rsa().pub() == privkey_obj.pub()
req = OpenSSL.crypto.load_certificate_request(
OpenSSL.crypto.FILETYPE_PEM, csr)
pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, privkey)
try:
return req.verify(pkey)
except OpenSSL.crypto.Error as error:
logger.debug(error, exc_info=True)
return False
def make_key(bits):
@@ -174,18 +179,39 @@ def make_key(bits):
def valid_privkey(privkey):
"""Is valid RSA private key?
:param str privkey: Private key file contents
:param str privkey: Private key file contents in PEM
:returns: Validity of private key.
:rtype: bool
"""
try:
return bool(M2Crypto.RSA.load_key_string(privkey).check_key())
except M2Crypto.RSA.RSAError:
return OpenSSL.crypto.load_privatekey(
OpenSSL.crypto.FILETYPE_PEM, privkey).check()
except (TypeError, OpenSSL.crypto.Error):
return False
def _pyopenssl_load(data, method, types=(
OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1)):
openssl_errors = []
for filetype in types:
try:
return method(filetype, data), filetype
except OpenSSL.crypto.Error as error: # TODO: anything else?
openssl_errors.append(error)
raise errors.Error("Unable to load: {0}".format(",".join(
str(error) for error in openssl_errors)))
def pyopenssl_load_certificate(data):
"""Load PEM/DER certificate.
:raises errors.Error:
"""
return _pyopenssl_load(data, OpenSSL.crypto.load_certificate)
def make_ss_cert(key_str, domains, not_before=None,
validity=(7 * 24 * 60 * 60)):
"""Returns new self-signed cert in PEM form.
@@ -193,44 +219,36 @@ def make_ss_cert(key_str, domains, not_before=None,
Uses key_str and contains all domains.
"""
assert domains, "Must provide one or more hostnames for the CSR."
rsa_key = M2Crypto.RSA.load_key_string(key_str)
pubkey = M2Crypto.EVP.PKey()
pubkey.assign_rsa(rsa_key)
cert = M2Crypto.X509.X509()
cert.set_pubkey(pubkey)
assert domains, "Must provide one or more hostnames for the cert."
pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, key_str)
cert = OpenSSL.crypto.X509()
cert.set_serial_number(1337)
cert.set_version(2)
current_ts = long(time.time() if not_before is None else not_before)
current = M2Crypto.ASN1.ASN1_UTCTIME()
current.set_time(current_ts)
expire = M2Crypto.ASN1.ASN1_UTCTIME()
expire.set_time(current_ts + validity)
cert.set_not_before(current)
cert.set_not_after(expire)
extensions = [
OpenSSL.crypto.X509Extension(
"basicConstraints", True, 'CA:TRUE, pathlen:0'),
]
subject = cert.get_subject()
subject.C = "US"
subject.ST = "Michigan"
subject.L = "Ann Arbor"
subject.O = "University of Michigan and the EFF"
subject.CN = domains[0]
cert.get_subject().CN = domains[0]
# TODO: what to put into cert.get_subject()?
cert.set_issuer(cert.get_subject())
if len(domains) > 1:
cert.add_ext(M2Crypto.X509.new_extension(
"basicConstraints", "CA:FALSE"))
cert.add_ext(M2Crypto.X509.new_extension(
"subjectAltName", ", ".join(["DNS:%s" % d for d in domains])))
extensions.append(OpenSSL.crypto.X509Extension(
"subjectAltName",
critical=False,
value=", ".join("DNS:%s" % d for d in domains)
))
cert.sign(pubkey, "sha256")
assert cert.verify(pubkey)
assert cert.verify()
# print check_purpose(,0
return cert.as_pem()
cert.add_extensions(extensions)
cert.gmtime_adj_notBefore(0 if not_before is None else not_before)
cert.gmtime_adj_notAfter(validity)
cert.set_pubkey(pkey)
cert.sign(pkey, "sha256")
return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
def _pyopenssl_cert_or_req_san(cert_or_req):
@@ -308,3 +326,19 @@ def get_sans_from_csr(csr, typ=OpenSSL.crypto.FILETYPE_PEM):
"""
return _get_sans_from_cert_or_req(
csr, OpenSSL.crypto.load_certificate_request, typ)
def asn1_generalizedtime_to_dt(timestamp):
"""Convert ASN.1 GENERALIZEDTIME to datetime.
Useful for deserialization of `OpenSSL.crypto.X509.get_notAfter` and
`OpenSSL.crypto.X509.get_notAfter` outputs.
"""
# TODO: there are two other ts formats
return datetime.datetime.strptime(timestamp, '%Y%m%d%H%M%SZ')
def pyopenssl_x509_name_as_text(x509name):
"""Convert `OpenSSL.crypto.X509Name to text."""
return "/".join("{0}={1}" for key, value in x509name.get_components())
+1
View File
@@ -409,6 +409,7 @@ def separate_list_input(input_):
"""
no_commas = input_.replace(",", " ")
# Each string is naturally unicode, this causes problems with M2Crypto SANs
# TODO: check if above is still true when M2Crypto is gone ^
return [str(string) for string in no_commas.split()]
+16 -8
View File
@@ -1,7 +1,9 @@
"""Proof of Possession Identifier Validation Challenge."""
import M2Crypto
import logging
import os
from cryptography import x509
from cryptography.hazmat.backends import default_backend
import zope.component
from acme import challenges
@@ -43,15 +45,21 @@ class ProofOfPossession(object): # pylint: disable=too-few-public-methods
return None
for cert, key, _ in self.installer.get_all_certs_keys():
pkey = M2Crypto.X509.load_cert(cert).get_pubkey()
with open(cert) as cert_file:
cert_data = cert_file.read()
try:
rsa_pkey = pkey.get_rsa()
cert_obj = x509.load_pem_x509_certificate(
cert_data, default_backend())
except ValueError:
logger.warn("Only RSA supported at this time")
continue
pem_cert_key = rsa_pkey.as_pem()
cert_key = achall.alg.kty.load(pem_cert_key)
# TODO: If JWKES.load raises other exceptions, they should be caught here
try:
cert_obj = x509.load_der_x509_certificate(
cert_data, default_backend())
except ValueError:
logger.warn("Certificate is neither PER nor DER: %s", cert)
# TODO: only RSA is supported
cert_key = achall.alg.kty(key=jose.ComparableRSAKey(
cert_obj.public_key()))
if cert_key == achall.hints.jwk:
return self._gen_response(achall, key)
+41 -24
View File
@@ -13,11 +13,11 @@ import os
import shutil
import tempfile
import M2Crypto
import OpenSSL
from acme.jose import util as jose_util
from letsencrypt import crypto_util
from letsencrypt import errors
from letsencrypt import le_util
from letsencrypt import network
@@ -196,10 +196,15 @@ class Revoker(object):
for (cert_path, _, path) in self.installer.get_all_certs_keys():
try:
cert_sha1 = M2Crypto.X509.load_cert(
cert_path).get_fingerprint(md="sha1")
except (IOError, M2Crypto.X509.X509Error):
with open(cert_path) as cert_file:
cert_data = cert_file.read()
except IOError:
continue
try:
cert_obj, _ = crypto_util.pyopenssl_load_certificate(cert_data)
except errors.Error:
continue
cert_sha1 = cert_obj.digest("sha1")
if cert_sha1 in csha1_vhlist:
csha1_vhlist[cert_sha1].append(path)
else:
@@ -246,7 +251,6 @@ class Revoker(object):
"""
# XXX | pylint: disable=unused-variable
# These will both have to change in the future away from M2Crypto
# pylint: disable=protected-access
certificate = jose_util.ComparableX509(cert._cert)
try:
@@ -373,8 +377,8 @@ class Revoker(object):
class Cert(object):
"""Cert object used for Revocation convenience.
:ivar _cert: M2Crypto X509 cert
:type _cert: :class:`M2Crypto.X509`
:ivar _cert: Certificate
:type _cert: :class:`OpenSSL.crypto.X509`
:ivar int idx: convenience index used for listing
:ivar orig: (`str` path - original certificate, `str` status)
@@ -402,8 +406,16 @@ class Cert(object):
"""
try:
self._cert = M2Crypto.X509.load_cert(cert_path)
except (IOError, M2Crypto.X509.X509Error):
with open(cert_path) as cert_file:
cert_data = cert_file.read()
except IOError:
raise errors.RevokerError(
"Error loading certificate: %s" % cert_path)
try:
self._cert = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert_data)
except OpenSSL.crypto.Error:
raise errors.RevokerError(
"Error loading certificate: %s" % cert_path)
@@ -451,8 +463,11 @@ class Cert(object):
if not os.path.isfile(orig):
status = Cert.DELETED_MSG
else:
o_cert = M2Crypto.X509.load_cert(orig)
if self.get_fingerprint() != o_cert.get_fingerprint(md="sha1"):
with open(orig) as orig_file:
orig_data = orig_file.read()
o_cert = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, orig_data)
if self.get_fingerprint() != o_cert.digest("sha1"):
status = Cert.CHANGED_MSG
# Verify original key path
@@ -472,47 +487,49 @@ class Cert(object):
self.backup_path = backup
self.backup_key_path = backup_key
# M2Crypto is eventually going to be replaced, hence the reason for _cert
def get_cn(self):
"""Get common name."""
return self._cert.get_subject().CN
def get_fingerprint(self):
"""Get SHA1 fingerprint."""
return self._cert.get_fingerprint(md="sha1")
return self._cert.digest("sha1")
def get_not_before(self):
"""Get not_valid_before field."""
return self._cert.get_not_before().get_datetime()
return crypto_util.asn1_generalizedtime_to_dt(
self._cert.get_notBefore())
def get_not_after(self):
"""Get not_valid_after field."""
return self._cert.get_not_after().get_datetime()
return crypto_util.asn1_generalizedtime_to_dt(
self._cert.get_notAfter())
def get_der(self):
"""Get certificate in der format."""
return self._cert.as_der()
return OpenSSL.crypto.dump_certificate(
OpenSSL.crypto.FILETYPE_ASN1, self._cert)
def get_pub_key(self):
"""Get public key size.
.. todo:: M2Crypto doesn't support ECC, this will have to be updated
.. todo:: Support for ECC
"""
return "RSA " + str(self._cert.get_pubkey().size() * 8)
return "RSA {0}".format(self._cert.get_pubkey().bits)
def get_san(self):
"""Get subject alternative name if available."""
try:
return self._cert.get_ext("subjectAltName").get_value()
except LookupError:
return ""
# pylint: disable=protected-access
return ", ".join(crypto_util._pyopenssl_cert_or_req_san(self._cert))
def __str__(self):
text = [
"Subject: %s" % self._cert.get_subject().as_text(),
"Subject: %s" % crypto_util.pyopenssl_x509_name_as_text(
self._cert.get_subject()),
"SAN: %s" % self.get_san(),
"Issuer: %s" % self._cert.get_issuer().as_text(),
"Issuer: %s" % crypto_util.pyopenssl_x509_name_as_text(
self._cert.get_issuer()),
"Public Key: %s" % self.get_pub_key(),
"Not Before: %s" % str(self.get_not_before()),
"Not After: %s" % str(self.get_not_after()),
+10 -11
View File
@@ -1,12 +1,13 @@
"""Tests for letsencrypt.achallenges."""
import os
import pkg_resources
import re
import unittest
import M2Crypto
import OpenSSL
from acme import challenges
from letsencrypt import crypto_util
from letsencrypt import le_util
from letsencrypt.tests import acme_util
@@ -31,15 +32,13 @@ class DVSNITest(unittest.TestCase):
def test_gen_cert_and_response(self):
cert_pem, _ = self.achall.gen_cert_and_response(s=self.response.s)
cert = M2Crypto.X509.load_cert_string(cert_pem)
self.assertEqual(cert.get_subject().CN, self.chall.nonce_domain)
sans = cert.get_ext("subjectAltName").get_value()
self.assertEqual(
set([self.chall.nonce_domain, "example.com",
self.response.z_domain(self.chall)]),
set(re.findall(r"DNS:([^, $]*)", sans)),
)
cert = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert_pem)
self.assertEqual(cert.get_subject().CN, "example.com")
# pylint: disable=protected-access
self.assertEqual(crypto_util._pyopenssl_cert_or_req_san(cert), [
"example.com", self.chall.nonce_domain,
self.response.z_domain(self.chall)])
if __name__ == "__main__":
+2 -2
View File
@@ -6,7 +6,6 @@ import shutil
import tempfile
import unittest
import M2Crypto
import OpenSSL
import mock
@@ -146,7 +145,8 @@ class MakeKeyTest(unittest.TestCase): # pylint: disable=too-few-public-methods
def test_it(self): # pylint: disable=no-self-use
from letsencrypt.crypto_util import make_key
# Do not test larger keys as it takes too long.
M2Crypto.RSA.load_key_string(make_key(1024))
OpenSSL.crypto.load_privatekey(
OpenSSL.crypto.FILETYPE_PEM, make_key(1024))
class ValidPrivkeyTest(unittest.TestCase):
@@ -1,6 +1,7 @@
"""Tests for letsencrypt.proof_of_possession."""
import os
import pkg_resources
import tempfile
import unittest
from cryptography.hazmat.backends import default_backend
@@ -18,9 +19,7 @@ from letsencrypt.display import util as display_util
BASE_PACKAGE = "letsencrypt.tests"
CERT0_PATH = pkg_resources.resource_filename(
BASE_PACKAGE, os.path.join("testdata", "cert.pem"))
CERT1_PATH = pkg_resources.resource_filename(
BASE_PACKAGE, os.path.join("testdata", "cert-san.pem"))
"acme.jose", os.path.join("testdata", "cert.der"))
CERT2_PATH = pkg_resources.resource_filename(
BASE_PACKAGE, os.path.join("testdata", "dsa_cert.pem"))
CERT2_KEY_PATH = pkg_resources.resource_filename(
@@ -38,7 +37,8 @@ with open(CERT3_KEY_PATH) as cert3_file:
class ProofOfPossessionTest(unittest.TestCase):
def setUp(self):
self.installer = mock.MagicMock()
certs = [CERT0_PATH, CERT1_PATH, CERT2_PATH, CERT3_PATH]
self.cert1_path = tempfile.mkstemp()[1]
certs = [CERT0_PATH, self.cert1_path, CERT2_PATH, CERT3_PATH]
keys = [None, None, CERT2_KEY_PATH, CERT3_KEY_PATH]
self.installer.get_all_certs_keys.return_value = zip(
certs, keys, 4 * [None])
@@ -56,6 +56,9 @@ class ProofOfPossessionTest(unittest.TestCase):
self.achall = achallenges.ProofOfPossession(
challb=challb, domain="example.com")
def tearDown(self):
os.remove(self.cert1_path)
def test_perform_bad_challenge(self):
hints = challenges.ProofOfPossession.Hints(
jwk=jose.jwk.JWKOct(key="foo"), cert_fingerprints=(),
+20 -3
View File
@@ -267,18 +267,28 @@ class RevokerInstallerTest(RevokerBase):
self.assertEqual(
sha_vh[cert.get_fingerprint()], self.installs[i])
@mock.patch("letsencrypt.revoker.M2Crypto.X509.load_cert")
def test_get_installed_load_failure(self, mock_m2):
@mock.patch("letsencrypt.revoker.OpenSSL.crypto.load_certificate")
def test_get_installed_load_failure(self, mock_load_certificate):
mock_installer = mock.MagicMock()
mock_installer.get_all_certs_keys.return_value = self.certs_keys
mock_m2.side_effect = IOError
mock_load_certificate.side_effect = OpenSSL.crypto.Error
revoker = self._get_revoker(mock_installer)
# pylint: disable=protected-access
self.assertEqual(revoker._get_installed_locations(), {})
def test_get_installed_load_failure_open(self):
tmp = tempfile.mkdtemp()
mock_installer = mock.MagicMock()
mock_installer.get_all_certs_keys.return_value = [(
os.path.join(tmp, 'missing'), None, None)]
revoker = self._get_revoker(mock_installer)
# pylint: disable=protected-access
self.assertEqual(revoker._get_installed_locations(), {})
os.rmdir(tmp)
class RevokerClassMethodsTest(RevokerBase):
def setUp(self):
@@ -334,6 +344,13 @@ class CertTest(unittest.TestCase):
from letsencrypt.revoker import Cert
self.assertRaises(errors.RevokerError, Cert, self.key_path)
def test_failed_load_open(self):
tmp = tempfile.mkdtemp()
from letsencrypt.revoker import Cert
self.assertRaises(
errors.RevokerError, Cert, os.path.join(tmp, 'missing'))
os.rmdir(tmp)
def test_no_row(self):
self.assertEqual(self.certs[0].get_row(), None)
-67
View File
@@ -1,67 +0,0 @@
# Support swig 3.0.5+
# https://github.com/M2Crypto/M2Crypto/issues/24
# https://github.com/M2Crypto/M2Crypto/pull/30
git+https://github.com/M2Crypto/M2Crypto.git@d13a3a46c8934c5f50b31d5f95b23e6e06f845c3#egg=M2Crypto
# This requirements file will fail on Travis CI 12.04 LTS Ubuntu build
# machine under TOX_ENV=py26 with very confusing error (full tracback
# at https://api.travis-ci.org/jobs/66529698/log.txt?deansi=true):
#Traceback (most recent call last):
# File "setup.py", line 133, in <module>
# include_package_data=True,
# File "/opt/python/2.6.9/lib/python2.6/distutils/core.py", line 152, in setup
# dist.run_commands()
# File "/opt/python/2.6.9/lib/python2.6/distutils/dist.py", line 975, in run_commands
# self.run_command(cmd)
# File "/opt/python/2.6.9/lib/python2.6/distutils/dist.py", line 995, in run_command
# cmd_obj.run()
# File "/home/travis/build/letsencrypt/lets-encrypt-preview/.tox/py26/lib/python2.6/site-packages/setuptools/command/test.py", line 142, in run
# self.with_project_on_sys_path(self.run_tests)
# File "/home/travis/build/letsencrypt/lets-encrypt-preview/.tox/py26/lib/python2.6/site-packages/setuptools/command/test.py", line 122, in with_project_on_sys_path
# func()
# File "/home/travis/build/letsencrypt/lets-encrypt-preview/.tox/py26/lib/python2.6/site-packages/setuptools/command/test.py", line 163, in run_tests
# testRunner=self._resolve_as_ep(self.test_runner),
# File "/opt/python/2.6.9/lib/python2.6/unittest.py", line 816, in __init__
# self.parseArgs(argv)
# File "/opt/python/2.6.9/lib/python2.6/unittest.py", line 843, in parseArgs
# self.createTests()
# File "/opt/python/2.6.9/lib/python2.6/unittest.py", line 849, in createTests
# self.module)
# File "/opt/python/2.6.9/lib/python2.6/unittest.py", line 613, in loadTestsFromNames
# suites = [self.loadTestsFromName(name, module) for name in names]
# File "/opt/python/2.6.9/lib/python2.6/unittest.py", line 587, in loadTestsFromName
# return self.loadTestsFromModule(obj)
# File "/home/travis/build/letsencrypt/lets-encrypt-preview/.tox/py26/lib/python2.6/site-packages/setuptools/command/test.py", line 37, in loadTestsFromModule
# tests.append(self.loadTestsFromName(submodule))
# File "/opt/python/2.6.9/lib/python2.6/unittest.py", line 584, in loadTestsFromName
# parent, obj = obj, getattr(obj, part)
#AttributeError: 'module' object has no attribute 'continuity_auth'
# the above error happens because letsencrypt.continuity_auth cannot import M2Crypto:
#>>> import M2Crypto
#Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# File "/root/lets-encrypt-preview/venv/lib/python2.6/site-packages/M2Crypto-0.21.1-py2.6-linux-x86_64.egg/M2Crypto/__init__.py", line 22, in <module>
# import m2crypto
# File "/root/lets-encrypt-preview/venv/lib/python2.6/site-packages/M2Crypto-0.21.1-py2.6-linux-x86_64.egg/M2Crypto/m2crypto.py", line 26, in <module>
# _m2crypto = swig_import_helper()
# File "/root/lets-encrypt-preview/venv/lib/python2.6/site-packages/M2Crypto-0.21.1-py2.6-linux-x86_64.egg/M2Crypto/m2crypto.py", line 22, in swig_import_helper
# _mod = imp.load_module('_m2crypto', fp, pathname, description)
#ImportError: /root/lets-encrypt-preview/venv/lib/python2.6/site-packages/M2Crypto-0.21.1-py2.6-linux-x86_64.egg/M2Crypto/_m2crypto.so: undefined symbol: SSLv2_method
# For more info see:
# - https://github.com/martinpaljak/M2Crypto/commit/84977c532c2444c5487db57146d81bb68dd5431d
# - http://stackoverflow.com/questions/10547332/install-m2crypto-on-a-virtualenv-without-system-packages
# - http://stackoverflow.com/questions/8206546/undefined-symbol-sslv2-method
# In short: Python has been built without SSLv2 support, and
# github.com/M2Crypto/M2Crypto version doesn't contain necessary
# patch, but it's the only one that has a patch for newer versions of
# swig...
# Problem seems not exists on Python 2.7. It's unlikely that the
# target distribution has swig 3.0.5+ and doesn't have Python 2.7, so
# this file should only be used in conjuction with Python 2.6.
+1 -5
View File
@@ -53,7 +53,7 @@ letsencrypt_install_requires = [
'argparse',
'ConfigArgParse',
'configobj',
'M2Crypto',
#'cryptography>=0.7', # load_pem_x509_certificate, version pin mismatch
'mock',
'parsedatetime',
'psutil>=2.1.0', # net_connections introduced in 2.1.0
@@ -64,7 +64,6 @@ letsencrypt_install_requires = [
'pytz',
'zope.component',
'zope.interface',
'M2Crypto',
]
letsencrypt_apache_install_requires = [
#'acme',
@@ -103,9 +102,6 @@ install_requires = [
'werkzeug',
'zope.component',
'zope.interface',
# order of items in install_requires DOES matter and M2Crypto has
# to go last, see #152
'M2Crypto',
]
assert set(install_requires) == set.union(*(set(ireq) for ireq in (