From 37ba2ac9828be133ed218386f9dbcf56196f2832 Mon Sep 17 00:00:00 2001 From: montag Date: Fri, 28 Apr 2017 03:05:12 -0700 Subject: [PATCH] TLS migration for demos/web (#260) What does this PR do? Minimally invasive migration to TLS for the http (8000) and websocket (9000) endpoints for the real time web demo. Where should the reviewer start? Install the demo like a normal install. During the install-deps.sh script it will prompt for questions to generate a local self-signed cert. Anything can be entered into the cert. Start the demo as normal but connect on https://domain:http_port instead of http. How should this PR be tested? The first step is the cert generation (added to install-deps.sh for convenience). The second step is ensuring the two endpoints are brought up. The next step is loading the web page and accepting the self-signed cert. Reloading the web app everything should now be secure, e.g. no errors or warnings and the video works as normal. Any background context you want to provide? I tried to be as minimal as possible so the changes tends to follow the existing structure rather than a refactor of anything major. With that said, SimpleHTTPServer doesn't work with TLS via the -m flag so that is now a short script. I updated all the html/js files to point to https/wss. I also updated the js for Firefox's change from navigator.mozGetUserMedia to navigator.mediaDevices.getUserMedia. What are the relevant issues? #75 Questions: Do the docs need to be updated? Yes. I updated the script's docs in demos/web but didn't make any changes outside of demos/web Does this PR add new (Python) dependencies? I don't think so. --- demos/web/create-cert.sh | 10 ++++++++++ demos/web/index.html | 4 ++-- demos/web/install-deps.sh | 2 ++ demos/web/js/openface-demo.js | 13 ++++++++----- demos/web/simpleSSLServer.py | 18 ++++++++++++++++++ demos/web/start-servers.sh | 21 ++++++++------------- demos/web/websocket-server.py | 23 +++++++++++++++-------- 7 files changed, 63 insertions(+), 28 deletions(-) create mode 100644 demos/web/create-cert.sh create mode 100644 demos/web/simpleSSLServer.py diff --git a/demos/web/create-cert.sh b/demos/web/create-cert.sh new file mode 100644 index 000000000..50c9e00df --- /dev/null +++ b/demos/web/create-cert.sh @@ -0,0 +1,10 @@ +# generate self-signed certs with no password for the web and socket servers +mkdir tls +openssl genrsa -des3 -out tls/server.key 1024 +openssl req -new -key tls/server.key -out tls/server.csr +cp tls/server.key tls/server.key.org +openssl rsa -in tls/server.key.org -out tls/server.key +openssl x509 -req -days 365 -in tls/server.csr -signkey tls/server.key -out tls/server.crt +echo 'converting to pem' +cat tls/server.crt tls/server.key > tls/server.pem +echo 'cert complete' diff --git a/demos/web/index.html b/demos/web/index.html index b49ef4ee0..761d84e71 100644 --- a/demos/web/index.html +++ b/demos/web/index.html @@ -233,7 +233,7 @@

Training $("#viewTSNEBtn").click(viewTSNECallback); redrawPeople(); - // createSocket("ws://facerec.cmusatyalab.org:9000", "CMU"); - createSocket("ws:" + window.location.hostname + ":9000", "Local"); + // createSocket("wss://facerec.cmusatyalab.org:9000", "CMU"); + createSocket("wss://" + window.location.hostname + ":9000", "Local"); diff --git a/demos/web/install-deps.sh b/demos/web/install-deps.sh index bd4812bcc..90238e18e 100755 --- a/demos/web/install-deps.sh +++ b/demos/web/install-deps.sh @@ -10,6 +10,8 @@ sudo apt-get install -y libprotobuf-dev libleveldb-dev libsnappy-dev \ python-pip python-numpy python-imaging python-opencv \ git wget cmake gfortran +source ~/openface/demos/web/create-cert.sh + mkdir -p ~/src cd ~/src git clone https://github.com/bvlc/caffe.git diff --git a/demos/web/js/openface-demo.js b/demos/web/js/openface-demo.js index 1ba9deed6..676607ee7 100644 --- a/demos/web/js/openface-demo.js +++ b/demos/web/js/openface-demo.js @@ -16,7 +16,10 @@ limitations under the License. navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || - navigator.mozGetUserMedia || + (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) ? + function(c, os, oe) { + navigator.mediaDevices.getUserMedia(c).then(os,oe); + } : null || navigator.msGetUserMedia; window.URL = window.URL || @@ -329,22 +332,22 @@ function changeServerCallback() { case "Local": socket.close(); redrawPeople(); - createSocket("ws:" + window.location.hostname + ":9000", "Local"); + createSocket("wss:" + window.location.hostname + ":9000", "Local"); break; case "CMU": socket.close(); redrawPeople(); - createSocket("ws://facerec.cmusatyalab.org:9000", "CMU"); + createSocket("wss://facerec.cmusatyalab.org:9000", "CMU"); break; case "AWS East": socket.close(); redrawPeople(); - createSocket("ws://54.159.128.49:9000", "AWS-East"); + createSocket("wss://54.159.128.49:9000", "AWS-East"); break; case "AWS West": socket.close(); redrawPeople(); - createSocket("ws://54.188.234.61:9000", "AWS-West"); + createSocket("wss://54.188.234.61:9000", "AWS-West"); break; default: alert("Unrecognized server: " + $(this.html())); diff --git a/demos/web/simpleSSLServer.py b/demos/web/simpleSSLServer.py new file mode 100644 index 000000000..71c07de1f --- /dev/null +++ b/demos/web/simpleSSLServer.py @@ -0,0 +1,18 @@ +from __future__ import print_function +import BaseHTTPServer +import SimpleHTTPServer +import ssl +import sys + + +'''Adopted from https://www.piware.de/2011/01/creating-an-https-server-in-python/''' + + +def main(port): + httpd = BaseHTTPServer.HTTPServer(('0.0.0.0', port), SimpleHTTPServer.SimpleHTTPRequestHandler) + httpd.socket = ssl.wrap_socket(httpd.socket, certfile='tls/server.pem', server_side=True) + print('now serving tls http on port:', port) + httpd.serve_forever() + +if __name__ == '__main__': + main(int(sys.argv[1])) diff --git a/demos/web/start-servers.sh b/demos/web/start-servers.sh index f10774fa5..0d5768e44 100755 --- a/demos/web/start-servers.sh +++ b/demos/web/start-servers.sh @@ -22,30 +22,25 @@ trap 'kill $(jobs -p)' EXIT cat <:$HTTP_PORT. +address of the Docker container and use https://:$HTTP_PORT. If you're running on a remote computer, find the IP address -and use http://:$HTTP_PORT. - -WARNING: Chromium refuses to connect to the insecure WebSocket server -if you are running a remote or Docker deployment. -We have posted a workaround to forward traffic through localhost -using ncat at http://cmusatyalab.github.io/openface/demo-1-web/. -Track our progress on fixing this at: -https://github.com/cmusatyalab/openface/issues/75. +and use https://:$HTTP_PORT. +WARNING: Chromium will warn on self-signed certificates. Please accept the certificate +and reload the app. EOF WEBSOCKET_LOG='/tmp/openface.websocket.log' printf "WebSocket Server: Logging to '%s'\n\n" $WEBSOCKET_LOG -python2 -m SimpleHTTPServer $HTTP_PORT &> /dev/null & +python2 simpleSSLServer.py $HTTP_PORT &> /dev/null & cd ../../ # Root OpenFace directory. ./demos/web/websocket-server.py --port $WEBSOCKET_PORT 2>&1 | tee $WEBSOCKET_LOG & diff --git a/demos/web/websocket-server.py b/demos/web/websocket-server.py index 9e0f921f7..4a58a992c 100755 --- a/demos/web/websocket-server.py +++ b/demos/web/websocket-server.py @@ -24,8 +24,10 @@ from autobahn.twisted.websocket import WebSocketServerProtocol, \ WebSocketServerFactory +from twisted.internet import task, defer +from twisted.internet.ssl import DefaultOpenSSLContextFactory + from twisted.python import log -from twisted.internet import reactor import argparse import cv2 @@ -53,6 +55,9 @@ modelDir = os.path.join(fileDir, '..', '..', 'models') dlibModelDir = os.path.join(modelDir, 'dlib') openfaceModelDir = os.path.join(modelDir, 'openface') +# For TLS connections +tls_crt = os.path.join(fileDir, 'tls', 'server.crt') +tls_key = os.path.join(fileDir, 'tls', 'server.key') parser = argparse.ArgumentParser() parser.add_argument('--dlibFacePredictor', type=str, help="Path to dlib's face predictor.", @@ -88,8 +93,8 @@ def __repr__(self): class OpenFaceServerProtocol(WebSocketServerProtocol): - def __init__(self): + super(OpenFaceServerProtocol, self).__init__() self.images = {} self.training = True self.people = [] @@ -353,12 +358,14 @@ def processFrame(self, dataURL, identity): plt.close() self.sendMessage(json.dumps(msg)) -if __name__ == '__main__': - log.startLogging(sys.stdout) - factory = WebSocketServerFactory("ws://localhost:{}".format(args.port), - debug=False) +def main(reactor): + log.startLogging(sys.stdout) + factory = WebSocketServerFactory() factory.protocol = OpenFaceServerProtocol + ctx_factory = DefaultOpenSSLContextFactory(tls_key, tls_crt) + reactor.listenSSL(args.port, factory, ctx_factory) + return defer.Deferred() - reactor.listenTCP(args.port, factory) - reactor.run() +if __name__ == '__main__': + task.react(main)