From 2ba30f20221d22c191dced77b1e59ce59e142c47 Mon Sep 17 00:00:00 2001 From: Raphael Deem Date: Tue, 7 Mar 2017 19:54:02 -0800 Subject: [PATCH] allow running with SSL via commandline --- docs/sanic/ssl.rst | 10 +++++++++- requirements.txt | 1 + sanic/__main__.py | 7 ++++++- sanic/app.py | 20 ++++++++++++++++---- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/docs/sanic/ssl.rst b/docs/sanic/ssl.rst index ef2cfd34..ec0f6516 100644 --- a/docs/sanic/ssl.rst +++ b/docs/sanic/ssl.rst @@ -9,4 +9,12 @@ Optionally pass in an SSLContext: context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH) context.load_cert_chain("/path/to/cert", keyfile="/path/to/keyfile") - app.run(host="0.0.0.0", port=8443, ssl=context) \ No newline at end of file + app.run(host="0.0.0.0", port=8443, ssl=context) + +You can also pass in the locations of a certificate and key as a dictionary: + + +.. code:: python + + ssl = {'cert': "/path/to/cert", 'key': "/path/to/keyfile"} + app.run(host="0.0.0.0", port=8443, ssl=ssl) diff --git a/requirements.txt b/requirements.txt index 724a5835..e370b52f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ aiofiles httptools ujson uvloop +websockets diff --git a/sanic/__main__.py b/sanic/__main__.py index 5e13eba0..322d735d 100644 --- a/sanic/__main__.py +++ b/sanic/__main__.py @@ -8,6 +8,10 @@ if __name__ == "__main__": parser = ArgumentParser(prog='sanic') parser.add_argument('--host', dest='host', type=str, default='127.0.0.1') parser.add_argument('--port', dest='port', type=int, default=8000) + parser.add_argument('--cert', dest='cert', type=str, + help='location of certificate for SSL') + parser.add_argument('--key', dest='key', type=str, + help='location of keyfile for SSL.') parser.add_argument('--workers', dest='workers', type=int, default=1, ) parser.add_argument('--debug', dest='debug', action="store_true") parser.add_argument('module') @@ -26,7 +30,8 @@ if __name__ == "__main__": .format(type(app).__name__, args.module)) app.run(host=args.host, port=args.port, - workers=args.workers, debug=args.debug) + workers=args.workers, debug=args.debug, + cert=args.cert, key=args.key) except ImportError: log.error("No module named {} found.\n" " Example File: project/sanic_server.py -> app\n" diff --git a/sanic/app.py b/sanic/app.py index bb7b0efe..e2957532 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -7,6 +7,7 @@ from functools import partial from inspect import isawaitable, stack, getmodulename from traceback import format_exc from urllib.parse import urlencode, urlunparse +from ssl import create_default_context from sanic.config import Config from sanic.constants import HTTP_METHODS @@ -499,17 +500,18 @@ class Sanic: :param port: Port to host on :param debug: Enables debug output (slows server) :param before_start: Functions to be executed before the server starts - accepting connections + accepting connections :param after_start: Functions to be executed after the server starts accepting connections :param before_stop: Functions to be executed when a stop signal is received before it is respected :param after_stop: Functions to be executed when all requests are - complete - :param ssl: SSLContext for SSL encryption of worker(s) + complete + :param ssl: SSLContext, or location of certificate and key + for SSL encryption of worker(s) :param sock: Socket for the server to accept connections from :param workers: Number of processes - received before it is respected + received before it is respected :param loop: :param backlog: :param stop_event: @@ -574,6 +576,16 @@ class Sanic: register_sys_signals=True, run_async=False): """Helper function used by `run` and `create_server`.""" + if isinstance(ssl, dict): + # try common aliaseses + cert = ssl.get('cert') or ssl.get('certificate') + key = ssl.get('key') or ssl.get('keyfile') + if not cert and key: + raise ValueError("SSLContext or certificate and key required.") + context = create_default_context(purpose=ssl.Purpose.CLIENT_AUTH) + context.load_cert_chain(cert, keyfile=key) + ssl = context + if loop is not None: if debug: warnings.simplefilter('default')