1
0
mirror of https://github.com/certbot/certbot.git synced 2026-01-21 19:01:07 +03:00

Remove SimpleHTTP TLS from acme.

This commit is contained in:
Jakub Warmuz
2015-10-08 21:10:12 +00:00
parent 1efec6b399
commit 304414a214
4 changed files with 96 additions and 145 deletions

View File

@@ -70,20 +70,10 @@ class SSLSocket(object): # pylint: disable=too-few-public-methods
class FakeConnection(object):
"""Fake OpenSSL.SSL.Connection."""
MAKEFILE_SUPPORT = hasattr(socket, "_fileobject")
"""Is `makefile` supported on your platform?
.. warning:: `makefile`, as currently implemented, is supported
on select platforms only, as it uses CPython's internal API.
You've been warned!
"""
# pylint: disable=missing-docstring
def __init__(self, connection):
self._wrapped = connection
self._makefile_refs = 0
def __getattr__(self, name):
return getattr(self._wrapped, name)
@@ -92,27 +82,6 @@ class SSLSocket(object): # pylint: disable=too-few-public-methods
# OpenSSL.SSL.Connection.shutdown doesn't accept any args
return self._wrapped.shutdown()
# stuff below ripped off from
# https://hg.python.org/cpython/file/2.7/Lib/ssl.py
def makefile(self, mode='r', bufsize=-1):
assert self.MAKEFILE_SUPPORT, (
"You need compatible version for makefile support")
self._makefile_refs += 1
# SocketServer.StreamRequesthandler.finish will try to
# close the wfile/rfile. close=True causes curl: (56)
# GnuTLS recv error (-110): The TLS connection was
# non-properly terminated.
# TODO: doesn't work in Python3
# pylint: disable=protected-access
return socket._fileobject(self._wrapped, mode, bufsize, close=False)
def close(self):
if self._makefile_refs < 1:
self._wrapped.close()
else:
self._makefile_refs -= 1
def accept(self): # pylint: disable=missing-docstring
sock, addr = self.sock.accept()

View File

@@ -44,15 +44,7 @@ class TLSServer(socketserver.TCPServer):
return socketserver.TCPServer.server_bind(self)
class HTTPSServer(TLSServer, BaseHTTPServer.HTTPServer):
"""HTTPS Server."""
def server_bind(self):
self._wrap_sock()
BaseHTTPServer.HTTPServer.server_bind(self)
class ACMEServerMixin: # pylint: disable=old-style-class,no-init
class ACMEServerMixin: # pylint: disable=old-style-class
"""ACME server common settings mixin."""
server_version = "ACME standalone client"
allow_reuse_address = True
@@ -81,27 +73,23 @@ class ACMEServerMixin: # pylint: disable=old-style-class,no-init
self.server_close()
class ACMETLSServer(HTTPSServer, ACMEServerMixin):
"""ACME TLS Server."""
class DVSNIServer(TLSServer, ACMEServerMixin):
"""DVSNI Server."""
SIMPLE_HTTP_SUPPORT = crypto_util.SSLSocket.FakeConnection.MAKEFILE_SUPPORT
"""Is SimpleHTTP supported on your platform.
Please see a warning for `acme.crypto_util.SSLSocket.FakeConnection`.
"""
def __init__(self, *args, **kwargs):
def __init__(self, server_address, certs):
ACMEServerMixin.__init__(self)
HTTPSServer.__init__(self, *args, **kwargs)
TLSServer.__init__(
self, server_address, socketserver.BaseRequestHandler, certs=certs)
class ACMEServer(BaseHTTPServer.HTTPServer, ACMEServerMixin):
"""ACME Server (non-TLS)."""
class SimpleHTTPServer(BaseHTTPServer.HTTPServer, ACMEServerMixin):
"""SimpleHTTP Server."""
def __init__(self, *args, **kwargs):
def __init__(self, server_address, resources):
ACMEServerMixin.__init__(self)
BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
BaseHTTPServer.HTTPServer.__init__(
self, server_address, SimpleHTTPRequestHandler.partial_init(
simple_http_resources=resources))
class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
@@ -133,14 +121,14 @@ class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(self.server.server_version)
self.wfile.write(self.server.server_version.encode())
def handle_404(self):
"""Handler 404 Not Found errors."""
self.send_response(http_client.NOT_FOUND, message="Not Found")
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("404")
self.wfile.write(b"404")
def handle_simple_http_resource(self):
"""Handle SimpleHTTP provisioned resources."""
@@ -171,24 +159,8 @@ class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
cls, simple_http_resources=simple_http_resources)
class ACMERequestHandler(SimpleHTTPRequestHandler):
"""ACME request handler."""
def handle_one_request(self):
"""Handle single request.
Makes sure that DVSNI probers are ignored.
"""
try:
return SimpleHTTPRequestHandler.handle_one_request(self)
except OpenSSL.SSL.ZeroReturnError:
logger.debug("Client prematurely closed connection (prober?). "
"Ignoring request.")
def simple_server(cli_args, forever=True):
"""Run simple standalone client server."""
def simple_dvsni_server(cli_args, forever=True):
"""Run simple standalone DVSNI server."""
logging.basicConfig(level=logging.DEBUG)
parser = argparse.ArgumentParser()
@@ -198,7 +170,6 @@ def simple_server(cli_args, forever=True):
args = parser.parse_args(cli_args[1:])
certs = {}
resources = {}
_, hosts, _ = next(os.walk('.'))
for host in hosts:
@@ -206,15 +177,13 @@ def simple_server(cli_args, forever=True):
cert_contents = cert_file.read()
with open(os.path.join(host, "key.pem")) as key_file:
key_contents = key_file.read()
certs[host] = (
certs[host.encode()] = (
OpenSSL.crypto.load_privatekey(
OpenSSL.crypto.FILETYPE_PEM, key_contents),
OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert_contents))
handler = ACMERequestHandler.partial_init(
simple_http_resources=resources)
server = ACMETLSServer(('', int(args.port)), handler, certs=certs)
server = DVSNIServer(('', int(args.port)), certs=certs)
six.print_("Serving at https://localhost:{0}...".format(
server.socket.getsockname()[1]))
if forever: # pragma: no cover
@@ -224,4 +193,4 @@ def simple_server(cli_args, forever=True):
if __name__ == "__main__":
sys.exit(simple_server(sys.argv)) # pragma: no cover
sys.exit(simple_dvsni_server(sys.argv)) # pragma: no cover

View File

@@ -13,6 +13,7 @@ import requests
from acme import challenges
from acme import crypto_util
from acme import errors
from acme import jose
from acme import test_util
@@ -30,25 +31,27 @@ class TLSServerTest(unittest.TestCase):
class ACMEServerMixinTest(unittest.TestCase):
"""Tests for acme.standalone.ACMEServerMixin."""
def setUp(self):
from acme.standalone import ACMEServerMixin
class _MockServer(socketserver.TCPServer, ACMEServerMixin):
def __init__(self, *args, **kwargs):
socketserver.TCPServer.__init__(self, *args, **kwargs)
ACMEServerMixin.__init__(self)
self.server = _MockServer(("", 0), socketserver.BaseRequestHandler)
def test_serve_shutdown(self):
thread = threading.Thread(target=self.server.serve_forever2)
thread.start()
self.server.shutdown2()
def test_shutdown2_not_running(self):
from acme.standalone import ACMEServer
server = ACMEServer(("", 0), socketserver.BaseRequestHandler)
server.shutdown2()
server.shutdown2()
self.server.shutdown2()
self.server.shutdown2()
class ACMEServerTest(unittest.TestCase):
"""Test for acme.standalone.ACMEServer."""
def test_init(self):
from acme.standalone import ACMEServer
server = ACMEServer(("", 0), socketserver.BaseRequestHandler)
# pylint: disable=protected-access
self.assertFalse(server._stopped)
class ACMESimpleHTTPTLSServerTestEndToEnd(unittest.TestCase):
"""End-to-end test for ACME TLS server with SimpleHTTP."""
class DVSNIServerTest(unittest.TestCase):
"""Test for acme.standalone.DVSNIServer."""
def setUp(self):
self.certs = {
@@ -56,36 +59,19 @@ class ACMESimpleHTTPTLSServerTestEndToEnd(unittest.TestCase):
# pylint: disable=protected-access
test_util.load_cert('cert.pem')._wrapped),
}
self.account_key = jose.JWK.load(
test_util.load_vector('rsa1024_key.pem'))
from acme.standalone import ACMETLSServer
from acme.standalone import ACMERequestHandler
self.resources = set()
handler = ACMERequestHandler.partial_init(
simple_http_resources=self.resources)
self.server = ACMETLSServer(('', 0), handler, certs=self.certs)
self.server_thread = threading.Thread(
# pylint: disable=no-member
target=self.server.serve_forever2)
self.server_thread.start()
self.port = self.server.socket.getsockname()[1]
from acme.standalone import DVSNIServer
self.server = DVSNIServer(("", 0), certs=self.certs)
# pylint: disable=no-member
self.thread = threading.Thread(target=self.server.handle_request)
self.thread.start()
def tearDown(self):
self.server.shutdown2()
self.server_thread.join()
self.thread.join()
def test_index(self):
response = requests.get(
'https://localhost:{0}'.format(self.port), verify=False)
self.assertEqual(response.text, 'ACME standalone client')
self.assertTrue(response.ok)
def test_404(self):
response = requests.get(
'https://localhost:{0}/foo'.format(self.port), verify=False)
self.assertEqual(response.status_code, http_client.NOT_FOUND)
def test_init(self):
# pylint: disable=protected-access
self.assertFalse(self.server._stopped)
def test_dvsni(self):
cert = crypto_util.probe_sni(
@@ -93,9 +79,41 @@ class ACMESimpleHTTPTLSServerTestEndToEnd(unittest.TestCase):
self.assertEqual(jose.ComparableX509(cert),
jose.ComparableX509(self.certs[b'localhost'][1]))
class SimpleHTTPServerTest(unittest.TestCase):
"""Tests for acme.standalone.SimpleHTTPServer."""
def setUp(self):
self.account_key = jose.JWK.load(
test_util.load_vector('rsa1024_key.pem'))
self.resources = set()
from acme.standalone import SimpleHTTPServer
self.server = SimpleHTTPServer(('', 0), resources=self.resources)
# pylint: disable=no-member
self.port = self.server.socket.getsockname()[1]
self.thread = threading.Thread(target=self.server.handle_request)
self.thread.start()
def tearDown(self):
self.server.shutdown2()
self.thread.join()
def test_index(self):
response = requests.get(
'http://localhost:{0}'.format(self.port), verify=False)
self.assertEqual(response.text, 'ACME standalone client')
self.assertTrue(response.ok)
def test_404(self):
response = requests.get(
'http://localhost:{0}/foo'.format(self.port), verify=False)
self.assertEqual(response.status_code, http_client.NOT_FOUND)
def _test_simple_http(self, add):
chall = challenges.SimpleHTTP(token=(b'x' * 16))
response = challenges.SimpleHTTPResponse(tls=True)
response = challenges.SimpleHTTPResponse(tls=False)
from acme.standalone import SimpleHTTPRequestHandler
resource = SimpleHTTPRequestHandler.SimpleHTTPResource(
@@ -114,8 +132,8 @@ class ACMESimpleHTTPTLSServerTestEndToEnd(unittest.TestCase):
self.assertFalse(self._test_simple_http(add=False))
class TestSimpleServer(unittest.TestCase):
"""Tests for acme.standalone.simple_server."""
class TestSimpleDVSNIServer(unittest.TestCase):
"""Tests for acme.standalone.simple_dvsni_server."""
def setUp(self):
# mirror ../examples/standalone
@@ -126,9 +144,10 @@ class TestSimpleServer(unittest.TestCase):
shutil.copy(test_util.vector_path('rsa512_key.pem'),
os.path.join(localhost_dir, 'key.pem'))
from acme.standalone import simple_server
self.thread = threading.Thread(target=simple_server, kwargs={
'cli_args': ('xxx', '--port', '1234'),
from acme.standalone import simple_dvsni_server
self.port = 1234
self.thread = threading.Thread(target=simple_dvsni_server, kwargs={
'cli_args': ('xxx', '--port', str(self.port)),
'forever': False,
})
self.old_cwd = os.getcwd()
@@ -145,12 +164,13 @@ class TestSimpleServer(unittest.TestCase):
while max_attempts:
max_attempts -= 1
try:
response = requests.get('https://localhost:1234', verify=False)
except requests.ConnectionError:
cert = crypto_util.probe_sni(b'localhost', b'0.0.0.0', self.port)
except errors.Error:
self.assertTrue(max_attempts > 0, "Timeout!")
time.sleep(1) # wait until thread starts
else:
self.assertEqual(response.text, 'ACME standalone client')
self.assertEqual(jose.ComparableX509(cert),
test_util.load_cert('cert.pem'))
break

View File

@@ -1,7 +1,6 @@
"""Standalone Authenticator."""
import argparse
import collections
import functools
import logging
import random
import socket
@@ -61,25 +60,19 @@ class ServerManager(object):
if port in self._instances:
return self._instances[port].server
logger.debug("Starting new server at %s (tls=%s)", port, tls)
handler = acme_standalone.ACMERequestHandler.partial_init(
self.simple_http_resources)
if tls:
cls = functools.partial(
acme_standalone.ACMETLSServer, certs=self.certs)
else:
cls = acme_standalone.ACMEServer
address = ("", port)
try:
server = cls(("", port), handler)
if tls:
server = acme_standalone.DVSNIServer(address, self.certs)
else:
server = acme_standalone.SimpleHTTPServer(
address, self.simple_http_resources)
except socket.error as error:
raise errors.StandaloneBindError(error, port)
# if port == 0, then random free port on OS is taken
# pylint: disable=no-member
host, real_port = server.socket.getsockname()
thread = threading.Thread(target=server.serve_forever2)
logger.debug("Starting server at %s:%d", host, real_port)
thread.start()