VOL-264: REST Channel ( External REST Client <==> Chameleon) needs to be Secured

Chameleon starts a https server using the SSL certificate available at its
disposal. If the SSL certificates are absent, then it falls back to starting a
http server. When using https, it enforces TLSv1_2 encryption.

Change-Id: I891ae62ebd20b574c1ad654ec976373cf32c2aff
diff --git a/main.py b/main.py
index bb82ed5..a06e15d 100755
--- a/main.py
+++ b/main.py
@@ -49,6 +49,9 @@
     rest_port=os.environ.get('REST_PORT', 8881),
     work_dir=os.environ.get('WORK_DIR', '/tmp/chameleon'),
     swagger_url=os.environ.get('SWAGGER_URL', ''),
+    enable_tls=os.environ.get('ENABLE_TLS',"True"),
+    key=os.environ.get('KEY','/chameleon/pki/voltha.key'),
+    cert=os.environ.get('CERT','/chameleon/pki/voltha.crt'),
 )
 
 
@@ -168,6 +171,33 @@
                         default=defs['swagger_url'],
                         help=_help)
 
+    _help = ('Enable TLS or not (default: %s). '
+             % defs['enable_tls'])
+    parser.add_argument('-t', '--tls-enable',
+                        dest='enable_tls',
+                        action='store',
+                        default=defs['enable_tls'],
+                        help=_help)
+
+    _help = ('Path to chameleon ssl server private key (default: %s). '
+             'If relative, it is relative to main.py of chameleon.'
+             % defs['key'])
+    parser.add_argument('-k', '--key',
+                        dest='key',
+                        action='store',
+                        default=defs['key'],
+                        help=_help)
+
+    _help = ('Path to chameleon ssl server certificate file (default: %s). '
+             'If relative, it is relative to main.py of chameleon.'
+             % defs['cert'])
+    parser.add_argument('-f', '--cert-file',
+                        dest='cert',
+                        action='store',
+                        default=defs['cert'],
+                        help=_help)
+
+
     args = parser.parse_args()
 
     # post-processing
@@ -235,8 +265,34 @@
             args = self.args
             self.grpc_client = yield \
                 GrpcClient(args.consul, args.work_dir, args.grpc_endpoint)
-            self.rest_server = yield \
-                WebServer(args.rest_port, args.work_dir, args.swagger_url, self.grpc_client).start()
+
+            if args.enable_tls == "False":
+                self.log.info('tls-disabled-through-configuration')
+                self.rest_server = yield \
+                    WebServer(args.rest_port, args.work_dir, args.swagger_url,\
+                    self.grpc_client).start()
+            else:
+                # If TLS is enabled, but the server key or cert is not found,
+                # then automatically disable TLS
+                if not os.path.exists(args.key) or \
+                   not os.path.exists(args.cert):
+                    if not os.path.exists(args.key):
+                        self.log.error('key-not-found')
+                    if not os.path.exists(args.cert):
+                        self.log.error('cert-not-found')
+                    self.log.info('disabling-tls-due-to-missing-pki-files')
+                    self.rest_server = yield \
+                                        WebServer(args.rest_port, args.work_dir,\
+                                        args.swagger_url,\
+                                        self.grpc_client).start()
+                else:
+                    self.log.info('tls-enabled')
+                    self.rest_server = yield \
+                                        WebServer(args.rest_port, args.work_dir,\
+                                        args.swagger_url,\
+                                        self.grpc_client, args.key,\
+                                        args.cert).start()
+
             self.grpc_client.set_reconnect_callback(
                 self.rest_server.reload_generated_routes).start()
             self.log.info('started-internal-services')
diff --git a/web_server/web_server.py b/web_server/web_server.py
index 7676b42..e660b05 100644
--- a/web_server/web_server.py
+++ b/web_server/web_server.py
@@ -24,8 +24,11 @@
 from twisted.internet import reactor, endpoints
 from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.internet.tcp import Port
+from twisted.internet.endpoints import SSL4ServerEndpoint
+from twisted.internet.ssl import DefaultOpenSSLContextFactory
 from twisted.web.server import Site
 from twisted.web.static import File
+from OpenSSL.SSL import TLSv1_2_METHOD
 from werkzeug.exceptions import BadRequest
 from grpc import StatusCode
 
@@ -37,12 +40,14 @@
 
     app = Klein()
 
-    def __init__(self, port, work_dir, swagger_url, grpc_client):
+    def __init__(self, port, work_dir, swagger_url, grpc_client, key=None, cert=None):
         self.port = port
         self.site = None
         self.work_dir = work_dir
-        self.grpc_client = grpc_client
         self.swagger_url = swagger_url
+        self.grpc_client = grpc_client
+        self.key = key
+        self.cert = cert
 
         self.swagger_ui_root_dir = os.path.abspath(
             os.path.join(os.path.dirname(__file__), '../swagger_ui'))
@@ -88,11 +93,20 @@
 
     @inlineCallbacks
     def _open_endpoint(self):
-        endpoint = endpoints.TCP4ServerEndpoint(reactor, self.port)
-        self.site = Site(self.app.resource())
-        self.tcp_port = yield endpoint.listen(self.site)
-        log.info('web-server-started', port=self.port)
-        self.endpoint = endpoint
+        try:
+            if self.key == None or self.cert == None:
+                endpoint = endpoints.TCP4ServerEndpoint(reactor, self.port)
+            else:
+                # Enforce TLSv1_2_METHOD
+                ctx = DefaultOpenSSLContextFactory(self.key, self.cert, TLSv1_2_METHOD)
+                endpoint = SSL4ServerEndpoint(reactor, self.port, ctx)
+
+            self.site = Site(self.app.resource())
+            self.tcp_port = yield endpoint.listen(self.site)
+            log.info('web-server-started', port=self.port)
+            self.endpoint = endpoint
+        except Exception, e:
+            self.log.exception('web-server-failed-to-start', e=e)
 
     def reload_generated_routes(self):
         for fname in os.listdir(self.work_dir):