Przeglądaj źródła

Add nova-novncproxy

 * Adds the nova-novncproxy binary, to provide support for vnc + OpensStack nova
 * Adds the ability to pass an auth token in via url, which is subsequently
   passed back to the proxy as a cookie.
Anthony Young 13 lat temu
rodzic
commit
4c75210a4d
2 zmienionych plików z 158 dodań i 3 usunięć
  1. 148 0
      utils/nova-novncproxy
  2. 10 3
      vnc_auto.html

+ 148 - 0
utils/nova-novncproxy

@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2012 Openstack, LLC.
+# All Rights Reserved.
+#
+#    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.
+#!/usr/bin/env python
+
+'''
+Websocket proxy that is compatible with Openstack Nova.
+Leverages wsproxy.py by Joel Martin
+'''
+
+import Cookie
+import socket
+import sys
+
+import wsproxy
+
+from nova import context
+from nova import flags
+from nova import rpc
+from nova import utils
+from nova.openstack.common import cfg
+
+
+opts = [
+    cfg.BoolOpt('record',
+                default=False,
+                help='Record sessions to FILE.[session_number]'),
+    cfg.BoolOpt('daemon',
+                default=False,
+                help='Become a daemon (background process)'),
+    cfg.BoolOpt('ssl_only',
+                default=False,
+                help='Disallow non-encrypted connections'),
+    cfg.BoolOpt('source_is_ipv6',
+                default=False,
+                help='Source is ipv6'),
+    cfg.StrOpt('cert',
+               default='self.pem',
+               help='SSL certificate file'),
+    cfg.StrOpt('key',
+               default=None,
+               help='SSL key file (if separate from cert)'),
+    cfg.StrOpt('web',
+               default='.',
+               help='Run webserver on same port. Serve files from DIR.'),
+    cfg.StrOpt('novncproxy_host',
+               default='0.0.0.0',
+               help='Host on which to listen for incoming requests'),
+    cfg.IntOpt('novncproxy_port',
+               default=6080,
+               help='Port on which to listen for incoming requests'),
+    ]
+FLAGS = flags.FLAGS
+FLAGS.register_cli_opts(opts)
+
+
+class NovaWebSocketProxy(wsproxy.WebSocketProxy):
+    def __init__(self, *args, **kwargs):
+        wsproxy.WebSocketProxy.__init__(self, *args, **kwargs)
+
+    def new_client(self):
+        """
+        Called after a new WebSocket connection has been established.
+        """
+        cookie = Cookie.SimpleCookie()
+        cookie.load(self.headers.getheader('cookie'))
+        token = cookie['token'].value
+        ctxt = context.get_admin_context()
+        connect_info = rpc.call(ctxt, 'consoleauth',
+                                {'method': 'check_token',
+                                 'args': {'token': token}})
+
+        if not connect_info:
+            raise Exception("Invalid Token")
+
+        host = connect_info['host']
+        port = int(connect_info['port'])
+
+        # Connect to the target
+        self.msg("connecting to: %s:%s" % (
+                 host, port))
+        tsock = self.socket(host, port,
+                connect=True)
+
+        # Handshake as necessary
+        if connect_info.get('internal_access_path'):
+            tsock.send("CONNECT %s HTTP/1.1\r\n\r\n" %
+                        connect_info['internal_access_path'])
+            while True:
+                data = tsock.recv(4096, socket.MSG_PEEK)
+                if data.find("\r\n\r\n") != -1:
+                    if not data.split("\r\n")[0].find("200"):
+                        raise Exception("Invalid Connection Info")
+                    tsock.recv(len(data))
+                    break
+
+        if self.verbose and not self.daemon:
+            print(self.traffic_legend)
+
+        # Start proxying
+        try:
+            self.do_proxy(tsock)
+        except:
+            if tsock:
+                tsock.shutdown(socket.SHUT_RDWR)
+                tsock.close()
+                self.vmsg("%s:%s: Target closed" % (host, port))
+            raise
+
+
+if __name__ == '__main__':
+    if FLAGS.ssl_only and not os.path.exists(FLAGS.cert):
+        parser.error("SSL only and %s not found" % FLAGS.cert)
+
+    # Setup flags
+    utils.default_flagfile()
+    FLAGS(sys.argv)
+
+    # Create and start the NovaWebSockets proxy
+    server = NovaWebSocketProxy(listen_host=FLAGS.novncproxy_host,
+                                listen_port=FLAGS.novncproxy_port,
+                                source_is_ipv6=FLAGS.source_is_ipv6,
+                                verbose=FLAGS.verbose,
+                                cert=FLAGS.cert,
+                                key=FLAGS.key,
+                                ssl_only=FLAGS.ssl_only,
+                                daemon=FLAGS.daemon,
+                                record=FLAGS.record,
+                                web=FLAGS.web,
+                                target_host='ignore',
+                                target_port='ignore',
+                                wrap_mode='exit',
+                                wrap_cmd=None)
+    server.start_server()

+ 10 - 3
vnc_auto.html

@@ -84,14 +84,15 @@
         }
         }
 
 
         window.onload = function () {
         window.onload = function () {
-            var host, port, password, path;
+            var host, port, password, path, token;
 
 
             $D('sendCtrlAltDelButton').style.display = "inline";
             $D('sendCtrlAltDelButton').style.display = "inline";
             $D('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
             $D('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
 
 
             document.title = unescape(WebUtil.getQueryVar('title', 'noVNC'));
             document.title = unescape(WebUtil.getQueryVar('title', 'noVNC'));
-            host = WebUtil.getQueryVar('host', null);
-            port = WebUtil.getQueryVar('port', null);
+            token = WebUtil.getQueryVar('token', null);
+            host = window.location.hostname;
+            port = window.location.port;
             password = WebUtil.getQueryVar('password', '');
             password = WebUtil.getQueryVar('password', '');
             path = WebUtil.getQueryVar('path', 'websockify');
             path = WebUtil.getQueryVar('path', 'websockify');
             if ((!host) || (!port)) {
             if ((!host) || (!port)) {
@@ -109,6 +110,12 @@
                            'view_only':    WebUtil.getQueryVar('view_only', false),
                            'view_only':    WebUtil.getQueryVar('view_only', false),
                            'updateState':  updateState,
                            'updateState':  updateState,
                            'onPasswordRequired':  passwordRequired});
                            'onPasswordRequired':  passwordRequired});
+
+            /* If a token variable is passed in, set the parameter in a cookie.  This is
+               used by nova-novncproxy. */
+            if (token) {
+                WebUtil.createCookie('token', token, 1)
+            }
             rfb.connect(host, port, password, path);
             rfb.connect(host, port, password, path);
         };
         };
         </script>
         </script>