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):