From c002bbfb236ee3dc17a2083a92acaede5a0200aa Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sat, 24 Jan 2015 12:11:51 -0700 Subject: [PATCH] Minimal SSL support. --- gns3server/main.py | 36 ++++++++++++++++-------------------- gns3server/server.py | 31 +++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/gns3server/main.py b/gns3server/main.py index c34bcffe..83d5c88f 100644 --- a/gns3server/main.py +++ b/gns3server/main.py @@ -73,31 +73,27 @@ def locale_check(): def parse_arguments(): parser = argparse.ArgumentParser(description="GNS3 server version {}".format(__version__)) - parser.add_argument("-l", "--host", help="run on the given host/IP address", default="127.0.0.1", nargs="?") - parser.add_argument("-p", "--port", type=int, help="run on the given port", default=8000, nargs="?") parser.add_argument("-v", "--version", help="show the version", action="version", version=__version__) - parser.add_argument("-q", "--quiet", action="store_true", help="Do not show logs on stdout") - parser.add_argument("-d", "--debug", action="store_true", help="Show debug logs") - parser.add_argument("-L", "--local", action="store_true", help="Local mode (allow some insecure operations)") - - parser.add_argument("-A", "--allow-remote-console", dest="allow", action="store_true", help="Allow remote connections to console ports") + parser.add_argument("--host", help="run on the given host/IP address", default="127.0.0.1") + parser.add_argument("--port", help="run on the given port", type=int, default=8000) + parser.add_argument("--ssl", action="store_true", help="run in SSL mode") + parser.add_argument("--certfile", help="SSL cert file", default="") + parser.add_argument("--certkey", help="SSL key file", default="") + parser.add_argument("-L", "--local", action="store_true", help="local mode (allow some insecure operations)") + parser.add_argument("-A", "--allow", action="store_true", help="allow remote connections to local console ports") + parser.add_argument("-q", "--quiet", action="store_true", help="do not show logs on stdout") + parser.add_argument("-d", "--debug", action="store_true", help="show debug logs") args = parser.parse_args() config = Config.instance() server_config = config.get_section_config("Server") - - if args.local: - server_config["local"] = "true" - else: - server_config["local"] = "false" - - if args.allow: - server_config["allow_remote_console"] = "true" - else: - server_config["allow_remote_console"] = "false" - - server_config["host"] = args.host - server_config["port"] = str(args.port) + server_config["local"] = server_config.get("local", "true" if args.local else "false") + server_config["allow_remote_console"] = server_config.get("allow_remote_console", "true" if args.allow else "false") + server_config["host"] = server_config.get("host", args.host) + server_config["port"] = server_config.get("port", str(args.port)) + server_config["ssl"] = server_config.get("ssl", "true" if args.ssl else "false") + server_config["certfile"] = server_config.get("certfile", args.certfile) + server_config["certkey"] = server_config.get("certkey", args.certkey) config.set_section_config("Server", server_config) return args diff --git a/gns3server/server.py b/gns3server/server.py index f1dd6685..e616007c 100644 --- a/gns3server/server.py +++ b/gns3server/server.py @@ -67,9 +67,9 @@ class Server: # log.error("could not create the projects directory {}: {}".format(self._projects_dir, e)) @asyncio.coroutine - def _run_application(self, app): + def _run_application(self, app, ssl_context=None): - server = yield from self._loop.create_server(app.make_handler(), self._host, self._port) + server = yield from self._loop.create_server(app.make_handler(), self._host, self._port, ssl=ssl_context) return server def _stop_application(self): @@ -130,6 +130,22 @@ class Server: reload() self._loop.call_later(1, self._reload_hook) + def _create_ssl_context(self, server_config): + + import ssl + ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + certfile = server_config["certfile"] + certkey = server_config["certkey"] + try: + ssl_context.load_cert_chain(certfile, certkey) + except FileNotFoundError: + log.critical("Could not find the SSL certfile or certkey") + raise SystemExit + except ssl.SSLError as e: + log.critical("SSL error: {}".format(e)) + raise SystemExit + return ssl_context + def run(self): """ Starts the server. @@ -138,11 +154,18 @@ class Server: logger = logging.getLogger("asyncio") logger.setLevel(logging.WARNING) + server_config = Config.instance().get_section_config("Server") if sys.platform.startswith("win"): # use the Proactor event loop on Windows asyncio.set_event_loop(asyncio.ProactorEventLoop()) - # TODO: SSL support for Rackspace cloud integration (here or with nginx for instance). + ssl_context = None + if server_config.getboolean("ssl"): + if sys.platform.startswith("win"): + log.critical("SSL mode is not supported on Windows") + raise SystemExit + ssl_context = self._create_ssl_context(server_config) + self._loop = asyncio.get_event_loop() app = aiohttp.web.Application() for method, route, handler in Route.get_routes(): @@ -154,7 +177,7 @@ class Server: m.port_manager = self._port_manager log.info("Starting server on {}:{}".format(self._host, self._port)) - self._loop.run_until_complete(self._run_application(app)) + self._loop.run_until_complete(self._run_application(app, ssl_context)) self._signal_handling() # FIXME: remove it in production or in tests