[PATCH] v2 Proxy websocket connections through nginx

This is v2 of my patch against master that sets up nginx to proxy the websocket requests handled by websockify running on port 64667. Rob Lemley (1): Configure nginx to proxy connections to the websocket server. This helps in networks with restrictive firewalls. - modify the nginx config file (kimchi.conf.in) and code to support changes - update README files - update firewalld config - move console.html into Kimchi plugin (websockify startup code is in Kimchi) - browsers tested - Firefox and Chrome (current versions) - tested on Centos 7 configure.ac | 1 - docs/README.md | 5 +---- src/firewalld.xml | 1 - src/nginx/wok.conf.in | 15 +++++++++++++ src/wok/plugins/kimchi/config.py.in | 6 +++++- src/wok/plugins/kimchi/configure.ac | 1 + src/wok/plugins/kimchi/docs/README.md | 5 +---- src/wok/plugins/kimchi/model/config.py | 2 ++ src/wok/plugins/kimchi/ui/Makefile.am | 2 +- src/wok/plugins/kimchi/ui/js/src/kimchi.api.js | 18 ++++++++-------- src/wok/plugins/kimchi/ui/pages/storage.html.tmpl | 5 +---- .../kimchi/ui/spice-html5/pages/spice_auto.html | 2 +- src/wok/plugins/kimchi/ui/websockify/Makefile.am | 22 +++++++++++++++++++ src/wok/plugins/kimchi/ui/websockify/console.html | 25 ++++++++++++++++++++++ src/wok/plugins/kimchi/vnc.py | 4 ++-- src/wok/proxy.py | 1 + src/wokd.in | 1 + ui/pages/Makefile.am | 2 -- ui/pages/websockify/Makefile.am | 22 ------------------- ui/pages/websockify/console.html | 25 ---------------------- 20 files changed, 88 insertions(+), 77 deletions(-) create mode 100644 src/wok/plugins/kimchi/ui/websockify/Makefile.am create mode 100644 src/wok/plugins/kimchi/ui/websockify/console.html delete mode 100644 ui/pages/websockify/Makefile.am delete mode 100644 ui/pages/websockify/console.html -- 1.8.3.1

Signed-off-by: Rob Lemley <rob.lemley@rochester.edu> --- configure.ac | 1 - docs/README.md | 5 +---- src/firewalld.xml | 1 - src/nginx/wok.conf.in | 15 +++++++++++++ src/wok/plugins/kimchi/config.py.in | 6 +++++- src/wok/plugins/kimchi/configure.ac | 1 + src/wok/plugins/kimchi/docs/README.md | 5 +---- src/wok/plugins/kimchi/model/config.py | 2 ++ src/wok/plugins/kimchi/ui/Makefile.am | 2 +- src/wok/plugins/kimchi/ui/js/src/kimchi.api.js | 18 ++++++++-------- src/wok/plugins/kimchi/ui/pages/storage.html.tmpl | 5 +---- .../kimchi/ui/spice-html5/pages/spice_auto.html | 2 +- src/wok/plugins/kimchi/ui/websockify/Makefile.am | 22 +++++++++++++++++++ src/wok/plugins/kimchi/ui/websockify/console.html | 25 ++++++++++++++++++++++ src/wok/plugins/kimchi/vnc.py | 4 ++-- src/wok/proxy.py | 1 + src/wokd.in | 1 + ui/pages/Makefile.am | 2 -- ui/pages/websockify/Makefile.am | 22 ------------------- ui/pages/websockify/console.html | 25 ---------------------- 20 files changed, 88 insertions(+), 77 deletions(-) create mode 100644 src/wok/plugins/kimchi/ui/websockify/Makefile.am create mode 100644 src/wok/plugins/kimchi/ui/websockify/console.html delete mode 100644 ui/pages/websockify/Makefile.am delete mode 100644 ui/pages/websockify/console.html diff --git a/configure.ac b/configure.ac index 67c3920..d79de7a 100644 --- a/configure.ac +++ b/configure.ac @@ -123,7 +123,6 @@ AC_CONFIG_FILES([ ui/libs/jquery-ui/themes/base/Makefile ui/libs/jquery-ui/themes/base/images/Makefile ui/pages/Makefile - ui/pages/websockify/Makefile contrib/Makefile contrib/DEBIAN/Makefile contrib/DEBIAN/control diff --git a/docs/README.md b/docs/README.md index f0b8697..2686eff 100644 --- a/docs/README.md +++ b/docs/README.md @@ -119,26 +119,23 @@ Run If you cannot access Wok, take a look at these 2 points: 1. Firewall: -Wok uses by default the ports 8000, 8001 and 64667. To allow incoming connections: +Wok uses by default the ports 8000 and 8001. To allow incoming connections: For system using firewalld, do: $ sudo firewall-cmd --add-port=8000/tcp --permanent $ sudo firewall-cmd --add-port=8001/tcp --permanent - $ sudo firewall-cmd --add-port=64667/tcp --permanent $ sudo firewall-cmd --reload For openSUSE systems, do: $ sudo /sbin/SuSEfirewall2 open EXT TCP 8000 $ sudo /sbin/SuSEfirewall2 open EXT TCP 8001 - $ sudo /sbin/SuSEfirewall2 open EXT TCP 64667 For system using iptables, do: $ sudo iptables -A INPUT -p tcp --dport 8000 -j ACCEPT $ sudo iptables -A INPUT -p tcp --dport 8001 -j ACCEPT - $ sudo iptables -A INPUT -p tcp --dport 64667 -j ACCEPT Don't forget to correctly save the rules. diff --git a/src/firewalld.xml b/src/firewalld.xml index ff9fafe..3a564fe 100644 --- a/src/firewalld.xml +++ b/src/firewalld.xml @@ -4,5 +4,4 @@ <description>wokd is a daemon service for wok which is a web framework.</description> <port protocol="tcp" port="8000"/> <port protocol="tcp" port="8001"/> - <port protocol="tcp" port="64667"/> </service> diff --git a/src/nginx/wok.conf.in b/src/nginx/wok.conf.in index db68893..16b0201 100644 --- a/src/nginx/wok.conf.in +++ b/src/nginx/wok.conf.in @@ -49,6 +49,15 @@ http { proxy_read_timeout 600; send_timeout 600; + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + upstream websocket { + server 127.0.0.1:${display_proxy_port}; + } + server { listen ${proxy_ssl_port} ssl; @@ -71,6 +80,12 @@ http { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_redirect http://127.0.0.1:${wokd_port}/ https://$host:${proxy_ssl_port}/; } + location /websockify { + proxy_pass http://websocket; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + } } server { diff --git a/src/wok/plugins/kimchi/config.py.in b/src/wok/plugins/kimchi/config.py.in index 6ae0ccd..425eaed 100644 --- a/src/wok/plugins/kimchi/config.py.in +++ b/src/wok/plugins/kimchi/config.py.in @@ -81,6 +81,8 @@ class KimchiPaths(PluginPaths): super(KimchiPaths, self).__init__('kimchi') self.spice_file = os.path.join(self.ui_dir, 'spice-html5/pages/spice_auto.html') + self.console_file = os.path.join(self.ui_dir, + 'websockify/console.html') if __with_spice__ == 'yes': self.spice_dir = self.add_prefix('ui/spice-html5') @@ -115,7 +117,9 @@ class KimchiConfig(PluginConfig): '/spice_auto.html': {'type': 'file', 'path': kimchiPaths.spice_file}, '/spice-html5/spice.css': {'type': 'file', - 'path': kimchiPaths.spice_css_file}} + 'path': kimchiPaths.spice_css_file}, + '/websockify/console.html': {'type': 'file', + 'path': kimchiPaths.console_file}} custom_config = {} for uri, data in static_config.iteritems(): diff --git a/src/wok/plugins/kimchi/configure.ac b/src/wok/plugins/kimchi/configure.ac index adab45b..3eeb4b3 100644 --- a/src/wok/plugins/kimchi/configure.ac +++ b/src/wok/plugins/kimchi/configure.ac @@ -105,6 +105,7 @@ AC_CONFIG_FILES([ ui/pages/help/ru_RU/Makefile ui/pages/help/zh_CN/Makefile ui/pages/help/zh_TW/Makefile + ui/websockify/Makefile contrib/Makefile contrib/DEBIAN/Makefile contrib/DEBIAN/control diff --git a/src/wok/plugins/kimchi/docs/README.md b/src/wok/plugins/kimchi/docs/README.md index fb0aef9..3c30d8e 100644 --- a/src/wok/plugins/kimchi/docs/README.md +++ b/src/wok/plugins/kimchi/docs/README.md @@ -135,23 +135,20 @@ Run If you cannot access Wok, take a look at these 2 points: 1. Firewall -Wok uses by default the ports 8000, 8001 and 64667. To allow incoming connections: +Wok uses by default the ports 8000 and 8001. To allow incoming connections: For system using firewalld, do: sudo firewall-cmd --add-port=8000/tcp --permanent sudo firewall-cmd --add-port=8001/tcp --permanent - sudo firewall-cmd --add-port=64667/tcp --permanent sudo firewall-cmd --reload For openSUSE systems, do: sudo /sbin/SuSEfirewall2 open EXT TCP 8000 sudo /sbin/SuSEfirewall2 open EXT TCP 8001 - sudo /sbin/SuSEfirewall2 open EXT TCP 64667 For system using iptables, do: sudo iptables -A INPUT -p tcp --dport 8000 -j ACCEPT sudo iptables -A INPUT -p tcp --dport 8001 -j ACCEPT - sudo iptables -A INPUT -p tcp --dport 64667 -j ACCEPT Don't forget to correctly save the rules. diff --git a/src/wok/plugins/kimchi/model/config.py b/src/wok/plugins/kimchi/model/config.py index b6cc4d9..cc7fe4d 100644 --- a/src/wok/plugins/kimchi/model/config.py +++ b/src/wok/plugins/kimchi/model/config.py @@ -42,7 +42,9 @@ class ConfigModel(object): def lookup(self, name): proxy_port = kconfig.get('display', 'display_proxy_port') + ssl_port = kconfig.get('server', 'ssl_port') return {'display_proxy_port': proxy_port, + 'ssl_port': ssl_port, 'version': get_version()} diff --git a/src/wok/plugins/kimchi/ui/Makefile.am b/src/wok/plugins/kimchi/ui/Makefile.am index 21fe703..62bcfeb 100644 --- a/src/wok/plugins/kimchi/ui/Makefile.am +++ b/src/wok/plugins/kimchi/ui/Makefile.am @@ -15,6 +15,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -SUBDIRS = config css images js pages spice-html5 +SUBDIRS = config css images js pages spice-html5 websockify uidir = $(datadir)/wok/plugins/kimchi/ui diff --git a/src/wok/plugins/kimchi/ui/js/src/kimchi.api.js b/src/wok/plugins/kimchi/ui/js/src/kimchi.api.js index c82d040..0e784f5 100644 --- a/src/wok/plugins/kimchi/ui/js/src/kimchi.api.js +++ b/src/wok/plugins/kimchi/ui/js/src/kimchi.api.js @@ -343,16 +343,16 @@ var kimchi = { type : 'GET', dataType : 'json' }).done(function(data, textStatus, xhr) { - proxy_port = data['display_proxy_port']; + ssl_port = data['ssl_port']; wok.requestJSON({ url : "plugins/kimchi/vms/" + encodeURIComponent(vm) + "/connect", type : "POST", dataType : "json" }).done(function() { - url = 'https://' + location.hostname + ':' + proxy_port; - url += "/console.html?url="; + url = 'https://' + location.hostname + ':' + ssl_port; + url += "/plugins/kimchi/websockify/console.html?url="; url += encodeURIComponent("plugins/kimchi/novnc/vnc_auto.html"); - url += "&port=" + proxy_port; + url += "&port=" + ssl_port; /* * From python documentation base64.urlsafe_b64encode(s) * substitutes - instead of + and _ instead of / in the @@ -360,7 +360,7 @@ var kimchi = { * contain = which is not safe in a URL query component. * So remove it when needed as base64 can work well without it. * */ - url += "&path=?token=" + wok.urlSafeB64Encode(vm).replace(/=*$/g, ""); + url += "&path=websockify?token=" + wok.urlSafeB64Encode(vm).replace(/=*$/g, ""); url += "&wok=" + location.port; url += '&encrypt=1'; window.open(url); @@ -376,15 +376,15 @@ var kimchi = { type : 'GET', dataType : 'json' }).done(function(data, textStatus, xhr) { - proxy_port = data['display_proxy_port']; + ssl_port = data['ssl_port']; wok.requestJSON({ url : "plugins/kimchi/vms/" + encodeURIComponent(vm) + "/connect", type : "POST", dataType : "json" }).done(function(data, textStatus, xhr) { - url = 'https://' + location.hostname + ':' + proxy_port; - url += "/console.html?url=plugins/kimchi/spice_auto.html"; - url += "&port=" + proxy_port + "&listen=" + location.hostname; + url = 'https://' + location.hostname + ':' + ssl_port; + url += "/plugins/kimchi/websockify/console.html?url=plugins/kimchi/spice_auto.html"; + url += "&port=" + ssl_port + "&listen=" + location.hostname; /* * From python documentation base64.urlsafe_b64encode(s) * substitutes - instead of + and _ instead of / in the diff --git a/src/wok/plugins/kimchi/ui/pages/storage.html.tmpl b/src/wok/plugins/kimchi/ui/pages/storage.html.tmpl index dfc006e..7b51a8b 100644 --- a/src/wok/plugins/kimchi/ui/pages/storage.html.tmpl +++ b/src/wok/plugins/kimchi/ui/pages/storage.html.tmpl @@ -37,10 +37,7 @@ <div class="grid-control"><input type="text" class="filter" placeholder="$_("Filter")"></div> <div id='storageGrid'> <div> - <span class="storage-name"> - <span>$_("Name")</span> - <span class="usage">$_("%Used")</span> - </span> + <span class="storage-name">$_("Name")</span> <span class="storage-state" >$_("State")</span> <span class="storage-type">$_("Type")</span> <span class="storage-capacity">$_("Capacity")</span> diff --git a/src/wok/plugins/kimchi/ui/spice-html5/pages/spice_auto.html b/src/wok/plugins/kimchi/ui/spice-html5/pages/spice_auto.html index c87f5c2..91ddadb 100644 --- a/src/wok/plugins/kimchi/ui/spice-html5/pages/spice_auto.html +++ b/src/wok/plugins/kimchi/ui/spice-html5/pages/spice_auto.html @@ -143,7 +143,7 @@ * to point wok.user to a specific console represented by * token value. */ - uri = scheme + host + ":" + port + "/?token=" + token; + uri = scheme + host + ":" + port + "/websockify?token=" + token; try { diff --git a/src/wok/plugins/kimchi/ui/websockify/Makefile.am b/src/wok/plugins/kimchi/ui/websockify/Makefile.am new file mode 100644 index 0000000..ca3ddc3 --- /dev/null +++ b/src/wok/plugins/kimchi/ui/websockify/Makefile.am @@ -0,0 +1,22 @@ +# +# Project Wok +# +# Copyright IBM, Corp. 2014-2015 +# +# Code derived from Project Kimchi +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +websockifyhtmldir = $(datadir)/wok/ui/pages/websockify + +dist_websockifyhtml_DATA = $(wildcard *.html) $(NULL) diff --git a/src/wok/plugins/kimchi/ui/websockify/console.html b/src/wok/plugins/kimchi/ui/websockify/console.html new file mode 100644 index 0000000..e0a3cd4 --- /dev/null +++ b/src/wok/plugins/kimchi/ui/websockify/console.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<html> + <head> + <script type="text/javascript"> + redirectToWok = function() { + var query = window.location.search; + + var path = /.*url=(.*?)(&|$)/g.exec(query)[1]; + query = query.replace("url=" + path + "&", "") + query = query.replace("url=" + path, "") + + var wok_port = /.*wok=(.*?)(&|$)/g.exec(query)[1]; + query = query.replace("wok=" + wok_port + "&", "") + query = query.replace("wok=" + wok_port, "") + + var url = "https://" + location.hostname + ":" + wok_port + "/"; + url += decodeURIComponent(path) + query + + window.location.replace(url) + } + </script> + </head> + + <body onload="redirectToWok()"/> +</html> diff --git a/src/wok/plugins/kimchi/vnc.py b/src/wok/plugins/kimchi/vnc.py index 2532449..5d10bc6 100644 --- a/src/wok/plugins/kimchi/vnc.py +++ b/src/wok/plugins/kimchi/vnc.py @@ -43,10 +43,10 @@ def new_ws_proxy(): cert = '%s/wok-cert.pem' % paths.conf_dir key = '%s/wok-key.pem' % paths.conf_dir - params = {'web': os.path.join(paths.ui_dir, 'pages/websockify'), + params = {'listen_host': '127.0.0.1', 'listen_port': config.get('display', 'display_proxy_port'), 'target_cfg': WS_TOKENS_DIR, - 'key': key, 'cert': cert, 'ssl_only': True} + 'ssl_only': False} def start_proxy(): server = WebSocketProxy(**params) diff --git a/src/wok/proxy.py b/src/wok/proxy.py index 46ce857..f9f1607 100644 --- a/src/wok/proxy.py +++ b/src/wok/proxy.py @@ -82,6 +82,7 @@ def _create_proxy_config(options): proxy_port=options.port, wokd_port=options.cherrypy_port, proxy_ssl_port=options.ssl_port, + display_proxy_port=options.display_proxy_port, cert_pem=cert, cert_key=key, max_body_size=eval(options.max_body_size), dhparams_pem=dhparams_pem) diff --git a/src/wokd.in b/src/wokd.in index c5510fd..a23fcfc 100644 --- a/src/wokd.in +++ b/src/wokd.in @@ -94,6 +94,7 @@ def main(options): setattr(options, 'ssl_key', config.config.get('server', 'ssl_key')) setattr(options, 'max_body_size', config.config.get('server', 'max_body_size')) + setattr(options, 'display_proxy_port', config.config.get('display', 'display_proxy_port')) wok.server.main(options) diff --git a/ui/pages/Makefile.am b/ui/pages/Makefile.am index 2488203..85faa80 100644 --- a/ui/pages/Makefile.am +++ b/ui/pages/Makefile.am @@ -17,8 +17,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -SUBDIRS = websockify - htmldir = $(datadir)/wok/ui/pages dist_html_DATA = $(wildcard *.tmpl) $(NULL) diff --git a/ui/pages/websockify/Makefile.am b/ui/pages/websockify/Makefile.am deleted file mode 100644 index ca3ddc3..0000000 --- a/ui/pages/websockify/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -# -# Project Wok -# -# Copyright IBM, Corp. 2014-2015 -# -# Code derived from Project Kimchi -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -websockifyhtmldir = $(datadir)/wok/ui/pages/websockify - -dist_websockifyhtml_DATA = $(wildcard *.html) $(NULL) diff --git a/ui/pages/websockify/console.html b/ui/pages/websockify/console.html deleted file mode 100644 index e0a3cd4..0000000 --- a/ui/pages/websockify/console.html +++ /dev/null @@ -1,25 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <script type="text/javascript"> - redirectToWok = function() { - var query = window.location.search; - - var path = /.*url=(.*?)(&|$)/g.exec(query)[1]; - query = query.replace("url=" + path + "&", "") - query = query.replace("url=" + path, "") - - var wok_port = /.*wok=(.*?)(&|$)/g.exec(query)[1]; - query = query.replace("wok=" + wok_port + "&", "") - query = query.replace("wok=" + wok_port, "") - - var url = "https://" + location.hostname + ":" + wok_port + "/"; - url += decodeURIComponent(path) + query - - window.location.replace(url) - } - </script> - </head> - - <body onload="redirectToWok()"/> -</html> -- 1.8.3.1

--- a/src/wok/plugins/kimchi/ui/pages/storage.html.tmpl +++ b/src/wok/plugins/kimchi/ui/pages/storage.html.tmpl @@ -37,10 +37,7 @@ <div class="grid-control"><input type="text" class="filter" placeholder="$_("Filter")"></div> <div id='storageGrid'> <div> - <span class="storage-name"> - <span>$_("Name")</span> - <span class="usage">$_("%Used")</span> - </span> + <span class="storage-name">$_("Name")</span> <span class="storage-state" >$_("State")</span> <span class="storage-type">$_("Type")</span> <span class="storage-capacity">$_("Capacity")</span>
I think those changes came from a conflict during rebase, right? May I dis consider them?

The commit message is too big! Usually we restrict the line to 80 characters. The first line is the commit message and the following ones are any explanation you want to add. Example Configure nginx to proxy connections to the websocket server <blank line> This helps in ... Could you fix that and resend, please? On 08/10/2015 18:22, Aline Manera wrote:
--- a/src/wok/plugins/kimchi/ui/pages/storage.html.tmpl +++ b/src/wok/plugins/kimchi/ui/pages/storage.html.tmpl @@ -37,10 +37,7 @@ <div class="grid-control"><input type="text" class="filter" placeholder="$_("Filter")"></div> <div id='storageGrid'> <div> - <span class="storage-name"> - <span>$_("Name")</span> - <span class="usage">$_("%Used")</span> - </span> + <span class="storage-name">$_("Name")</span> <span class="storage-state" >$_("State")</span> <span class="storage-type">$_("Type")</span> <span class="storage-capacity">$_("Capacity")</span>
I think those changes came from a conflict during rebase, right? May I dis consider them?
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

Yeah that shouldn't be included. Sorry about that. -Rob -- Rob Lemley System Administrator Center for Integrated Research Computing University of Rochester rob.lemley@rochester.edu -----Original Message----- From: Aline Manera [mailto:alinefm@linux.vnet.ibm.com] Sent: Thursday, October 08, 2015 5:22 PM To: Lemley, Rob; Kimchi Devel Subject: Re: [PATCH] Configure nginx to proxy connections to the websocket server. This helps in networks with restrictive firewalls. - modify the nginx config file (kimchi.conf.in) and code to support changes - update README files - update firewalld config - m...
--- a/src/wok/plugins/kimchi/ui/pages/storage.html.tmpl +++ b/src/wok/plugins/kimchi/ui/pages/storage.html.tmpl @@ -37,10 +37,7 @@ <div class="grid-control"><input type="text" class="filter" placeholder="$_("Filter")"></div> <div id='storageGrid'> <div> - <span class="storage-name"> - <span>$_("Name")</span> - <span class="usage">$_("%Used")</span> - </span> + <span class="storage-name">$_("Name")</span> <span class="storage-state" >$_("State")</span> <span class="storage-type">$_("Type")</span> <span class="storage-capacity">$_("Capacity")</span>
I think those changes came from a conflict during rebase, right? May I dis consider them?
participants (3)
-
Aline Manera
-
Lemley, Rob
-
Rob Lemley