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:
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user