Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for trusetd CA certificate in ssl.wrap_socket function #76

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 55 additions & 8 deletions sslserver/management/commands/runsslserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,46 @@
else:
upath = unicode


class SecureHTTPServer(WSGIServer):
def __init__(self, address, handler_cls, certificate, key):
def __init__(self, address, handler_cls, certificate, key, ca_certificate=None):
super(SecureHTTPServer, self).__init__(address, handler_cls)
self.socket = ssl.wrap_socket(self.socket, certfile=certificate,
keyfile=key, server_side=True,
ssl_version=ssl.PROTOCOL_TLSv1_2,
cert_reqs=ssl.CERT_NONE)
cert_reqs=ssl.CERT_NONE, ca_certs=ca_certificate)


class ThreadedSecureHTTPServer(SecureHTTPServer):
def __init__(self, address, handler_cls, certificate, key, ca_certificate=None):
super(ThreadedSecureHTTPServer, self).__init__(address, handler_cls, certificate, key, ca_certificate)
# thread executor with 10 thread for now
from concurrent.futures import ThreadPoolExecutor
self.max_thread_in_pool = 10
self._thread_executor = ThreadPoolExecutor(self.max_thread_in_pool)

def _handle_request_noblock(self):
"""Handle one request in separate thread, without blocking.

I assume that selector.select() has returned that the socket is
readable before this function was called, so there should be no risk of
blocking in get_request().
"""
try:
request, client_address = self.get_request()
except OSError:
return
self._thread_executor.submit(self._handle_request_in_separate_thread, request, client_address)

def _handle_request_in_separate_thread(self, request, client_address):
if self.verify_request(request, client_address):
try:
self.process_request(request, client_address)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)
else:
self.shutdown_request(request)


class WSGIRequestHandler(WSGIRequestHandler):
Expand Down Expand Up @@ -56,6 +89,9 @@ def add_arguments(self, parser):
default=os.path.join(default_ssl_files_dir(),
"development.key"),
help="Path to the key file"),
parser.add_argument("--caCertificate",
default=None, # TODO: add default trusted CA certificate, None for now
help="Path to the ca certificate"),
parser.add_argument("--nostatic", dest='use_static_handler',
action='store_false', default=None,
help="Do not use internal static file handler"),
Expand Down Expand Up @@ -90,22 +126,26 @@ def should_use_static_handler(self, options):
return True
return False

def check_certs(self, key_file, cert_file):
def check_certs(self, key_file, cert_file, ca_cert_file=None):
# TODO: maybe validate these? wrap_socket doesn't...

if not os.path.exists(key_file):
raise CommandError("Can't find key at %s" % key_file)
if not os.path.exists(cert_file):
raise CommandError("Can't find certificate at %s" %
cert_file)

if ca_cert_file is not None and not os.path.exists(ca_cert_file):
raise CommandError("Can't find CA certificate at %s" %
ca_cert_file)

def inner_run(self, *args, **options):
# Django did a shitty job abstracting this.

key_file = options.get("key")
cert_file = options.get("certificate")
self.check_certs(key_file, cert_file)
ca_cert_file = options.get("caCertificate")

self.check_certs(key_file, cert_file, ca_cert_file)

from django.conf import settings
from django.utils import translation
Expand All @@ -122,6 +162,7 @@ def inner_run(self, *args, **options):
"Starting development server at https://%(addr)s:%(port)s/\n"
"Using SSL certificate: %(cert)s\n"
"Using SSL key: %(key)s\n"
"Using CA certificate: %(ca_cert)s\n"
"Quit the server with %(quit_command)s.\n"
) % {
"started_at": datetime.now().strftime('%B %d, %Y - %X'),
Expand All @@ -131,7 +172,8 @@ def inner_run(self, *args, **options):
"port": self.port,
"quit_command": quit_command,
"cert": cert_file,
"key": key_file
"key": key_file,
"ca_cert": ca_cert_file if ca_cert_file is not None else "no trusted CA certificate specified"
})
# django.core.management.base forces the locale to en-us. We should
# set it up correctly for the first request (particularly important
Expand All @@ -140,9 +182,14 @@ def inner_run(self, *args, **options):

try:
handler = self.get_handler(*args, **options)
server = SecureHTTPServer((self.addr, int(self.port)),
# server = SecureHTTPServer((self.addr, int(self.port)),
# WSGIRequestHandler,
# cert_file, key_file, ca_certificate=ca_cert_file)

server = ThreadedSecureHTTPServer((self.addr, int(self.port)),
WSGIRequestHandler,
cert_file, key_file)
cert_file, key_file, ca_certificate=ca_cert_file)

server.set_app(handler)
server.serve_forever()

Expand Down