diff --git a/setup.py b/setup.py index beaeba7..d8afc87 100755 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ setuptools.setup(name='warcprox', license='GPL', packages=['warcprox'], package_data={'warcprox':['version.txt']}, - install_requires=['pyopenssl', 'warctools>=4.8.3'], # gdbm not in pip :( + install_requires=['certauth>=1.1.0', 'warctools>=4.8.3'], # gdbm not in pip :( dependency_links=['git+https://github.com/internetarchive/warctools.git#egg=warctools-4.8.3'], tests_require=['requests>=2.0.1', 'pytest'], # >=2.0.1 for https://github.com/kennethreitz/requests/pull/1636 cmdclass = {'test': PyTest}, diff --git a/warcprox/certauth.py b/warcprox/certauth.py deleted file mode 100644 index 08e3b0d..0000000 --- a/warcprox/certauth.py +++ /dev/null @@ -1,93 +0,0 @@ -# vim:set sw=4 et: - -from __future__ import absolute_import - -import logging -import os -import OpenSSL -import socket -import random - -class CertificateAuthority(object): - logger = logging.getLogger("warcprox.certauth.CertificateAuthority") - - def __init__(self, ca_file='warcprox-ca.pem', certs_dir='./warcprox-ca'): - self.ca_file = ca_file - self.certs_dir = certs_dir - - if not os.path.exists(ca_file): - self._generate_ca() - else: - self._read_ca(ca_file) - - if not os.path.exists(certs_dir): - self.logger.info("directory for generated certs {} doesn't exist, creating it".format(certs_dir)) - os.mkdir(certs_dir) - - - def _generate_ca(self): - # Generate key - self.key = OpenSSL.crypto.PKey() - self.key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) - - # Generate certificate - self.cert = OpenSSL.crypto.X509() - self.cert.set_version(2) - # avoid sec_error_reused_issuer_and_serial - self.cert.set_serial_number(random.randint(0,2**64-1)) - self.cert.get_subject().CN = 'Warcprox CA on {}'.format(socket.gethostname())[:64] - self.cert.gmtime_adj_notBefore(0) # now - self.cert.gmtime_adj_notAfter(100*365*24*60*60) # 100 yrs in future - self.cert.set_issuer(self.cert.get_subject()) - self.cert.set_pubkey(self.key) - self.cert.add_extensions([ - OpenSSL.crypto.X509Extension(b"basicConstraints", True, b"CA:TRUE, pathlen:0"), - OpenSSL.crypto.X509Extension(b"keyUsage", True, b"keyCertSign, cRLSign"), - OpenSSL.crypto.X509Extension(b"subjectKeyIdentifier", False, b"hash", subject=self.cert), - ]) - self.cert.sign(self.key, "sha1") - - with open(self.ca_file, 'wb+') as f: - f.write(OpenSSL.crypto.dump_privatekey(OpenSSL.SSL.FILETYPE_PEM, self.key)) - f.write(OpenSSL.crypto.dump_certificate(OpenSSL.SSL.FILETYPE_PEM, self.cert)) - - self.logger.info('generated CA key+cert and wrote to {}'.format(self.ca_file)) - - - def _read_ca(self, filename): - self.cert = OpenSSL.crypto.load_certificate(OpenSSL.SSL.FILETYPE_PEM, open(filename).read()) - self.key = OpenSSL.crypto.load_privatekey(OpenSSL.SSL.FILETYPE_PEM, open(filename).read()) - self.logger.info('read CA key+cert from {}'.format(self.ca_file)) - - def __getitem__(self, cn): - cnp = os.path.sep.join([self.certs_dir, '%s.pem' % cn]) - if not os.path.exists(cnp): - # create certificate - key = OpenSSL.crypto.PKey() - key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) - - # Generate CSR - req = OpenSSL.crypto.X509Req() - req.get_subject().CN = cn - req.set_pubkey(key) - req.sign(key, 'sha1') - - # Sign CSR - cert = OpenSSL.crypto.X509() - cert.set_subject(req.get_subject()) - cert.set_serial_number(random.randint(0,2**64-1)) - cert.gmtime_adj_notBefore(0) - cert.gmtime_adj_notAfter(10*365*24*60*60) - cert.set_issuer(self.cert.get_subject()) - cert.set_pubkey(req.get_pubkey()) - cert.sign(self.key, 'sha1') - - with open(cnp, 'wb+') as f: - f.write(OpenSSL.crypto.dump_privatekey(OpenSSL.SSL.FILETYPE_PEM, key)) - f.write(OpenSSL.crypto.dump_certificate(OpenSSL.SSL.FILETYPE_PEM, cert)) - - self.logger.info('wrote generated key+cert to {}'.format(cnp)) - - return cnp - - diff --git a/warcprox/main.py b/warcprox/main.py index d131b4b..04156d3 100644 --- a/warcprox/main.py +++ b/warcprox/main.py @@ -15,7 +15,8 @@ import argparse import os import socket -import warcprox.certauth +import certauth.certauth + import warcprox.playback import warcprox.dedup import warcprox.warcwriter @@ -103,9 +104,11 @@ def main(argv=sys.argv): recorded_url_q = queue.Queue() - ca = warcprox.certauth.CertificateAuthority(args.cacert, args.certs_dir) + ca_name = 'Warcprox CA on {}'.format(socket.gethostname())[:64] + ca = certauth.certauth.CertificateAuthority(args.cacert, args.certs_dir, + ca_name=ca_name) - proxy = warcprox.warcprox.WarcProxy( + proxy = warcprox.warcprox.WarcProxy( server_address=(args.address, int(args.port)), ca=ca, recorded_url_q=recorded_url_q, digest_algorithm=args.digest_algorithm) diff --git a/warcprox/mitmproxy.py b/warcprox/mitmproxy.py index 7d656d6..d331caa 100644 --- a/warcprox/mitmproxy.py +++ b/warcprox/mitmproxy.py @@ -67,7 +67,7 @@ class MitmProxyHandler(http_server.BaseHTTPRequestHandler): def _transition_to_ssl(self): self.request = self.connection = ssl.wrap_socket(self.connection, - server_side=True, certfile=self.server.ca[self.hostname]) + server_side=True, certfile=self.server.ca.cert_for_host(self.hostname)) def do_CONNECT(self): self.is_connect = True diff --git a/warcprox/tests/test_warcprox.py b/warcprox/tests/test_warcprox.py index c3ef3fb..f263bef 100755 --- a/warcprox/tests/test_warcprox.py +++ b/warcprox/tests/test_warcprox.py @@ -24,9 +24,10 @@ try: except ImportError: import Queue as queue +import certauth.certauth + import warcprox.controller import warcprox.warcprox -import warcprox.certauth import warcprox.playback import warcprox.warcwriter import warcprox.dedup @@ -119,7 +120,7 @@ class WarcproxTest(unittest.TestCase): f.close() # delete it, or CertificateAuthority will try to read it self._ca_file = f.name self._ca_dir = tempfile.mkdtemp(prefix='warcprox-test-', suffix='-ca') - ca = warcprox.certauth.CertificateAuthority(self._ca_file, self._ca_dir) + ca = certauth.certauth.CertificateAuthority(self._ca_file, self._ca_dir, 'warcprox-test') recorded_url_q = queue.Queue() diff --git a/warcprox/warcprox.py b/warcprox/warcprox.py index c03c483..4753e70 100644 --- a/warcprox/warcprox.py +++ b/warcprox/warcprox.py @@ -35,8 +35,9 @@ import tempfile import traceback import hashlib import json +import socket -import warcprox.certauth +from certauth.certauth import CertificateAuthority import warcprox.mitmproxy class ProxyingRecorder(object): @@ -249,7 +250,10 @@ class WarcProxy(socketserver.ThreadingMixIn, http_server.HTTPServer): if ca is not None: self.ca = ca else: - self.ca = warcprox.certauth.CertificateAuthority() + ca_name = 'Warcprox CA on {}'.format(socket.gethostname())[:64] + self.ca = CertificateAuthority(ca_file='warcprox-ca.pem', + certs_dir='./warcprox-ca', + ca_name=ca_name) if recorded_url_q is not None: self.recorded_url_q = recorded_url_q