1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-23 22:50:09 +03:00

F #1012: update websockify to latest version (#2809)

This commit is contained in:
Jan Orel 2019-01-18 15:48:10 +01:00 committed by Ruben S. Montero
parent 3ed4ede17d
commit 19f04965d4
9 changed files with 446 additions and 83 deletions

View File

@ -219,6 +219,7 @@ fi
SHARE_DIRS="$SHARE_LOCATION/examples \
$SHARE_LOCATION/websockify \
$SHARE_LOCATION/websockify/websockify \
$SHARE_LOCATION/esx-fw-vnc \
$SHARE_LOCATION/oneprovision"
@ -477,7 +478,8 @@ INSTALL_FILES=(
NETWORK_OVSWITCH_VXLAN_FILES:$VAR_LOCATION/remotes/vnm/ovswitch_vxlan
NETWORK_VCENTER_FILES:$VAR_LOCATION/remotes/vnm/vcenter
EXAMPLE_SHARE_FILES:$SHARE_LOCATION/examples
WEBSOCKIFY_SHARE_FILES:$SHARE_LOCATION/websockify
WEBSOCKIFY_SHARE_RUN_FILES:$SHARE_LOCATION/websockify
WEBSOCKIFY_SHARE_MODULE_FILES:$SHARE_LOCATION/websockify/websockify
ESX_FW_VNC_SHARE_FILES:$SHARE_LOCATION/esx-fw-vnc
INSTALL_GEMS_SHARE_FILES:$SHARE_LOCATION
ONETOKEN_SHARE_FILE:$SHARE_LOCATION
@ -1530,9 +1532,12 @@ EXAMPLE_SHARE_FILES="share/examples/vm.template \
# Files required to interact with the websockify server
#-------------------------------------------------------------------------------
WEBSOCKIFY_SHARE_FILES="share/websockify/websocketproxy.py \
share/websockify/websocket.py \
share/websockify/websockify"
WEBSOCKIFY_SHARE_RUN_FILES="share/websockify/run"
WEBSOCKIFY_SHARE_MODULE_FILES="share/websockify/websockify/__init__.py \
share/websockify/websockify/auth_plugins.py \
share/websockify/websockify/token_plugins.py \
share/websockify/websockify/websocket.py \
share/websockify/websockify/websocketproxy.py"
#-------------------------------------------------------------------------------
# Installation packages for ESX hosts to enable VNC ports

5
share/websockify/run Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env python
import websockify
websockify.websocketproxy.websockify_init()

View File

@ -1 +0,0 @@
websocketproxy.py

View File

@ -0,0 +1,2 @@
from websockify.websocket import *
from websockify.websocketproxy import *

View File

@ -0,0 +1,83 @@
class BasePlugin(object):
def __init__(self, src=None):
self.source = src
def authenticate(self, headers, target_host, target_port):
pass
class AuthenticationError(Exception):
def __init__(self, log_msg=None, response_code=403, response_headers={}, response_msg=None):
self.code = response_code
self.headers = response_headers
self.msg = response_msg
if log_msg is None:
log_msg = response_msg
super(AuthenticationError, self).__init__('%s %s' % (self.code, log_msg))
class InvalidOriginError(AuthenticationError):
def __init__(self, expected, actual):
self.expected_origin = expected
self.actual_origin = actual
super(InvalidOriginError, self).__init__(
response_msg='Invalid Origin',
log_msg="Invalid Origin Header: Expected one of "
"%s, got '%s'" % (expected, actual))
class BasicHTTPAuth(object):
"""Verifies Basic Auth headers. Specify src as username:password"""
def __init__(self, src=None):
self.src = src
def authenticate(self, headers, target_host, target_port):
import base64
auth_header = headers.get('Authorization')
if auth_header:
if not auth_header.startswith('Basic '):
raise AuthenticationError(response_code=403)
try:
user_pass_raw = base64.b64decode(auth_header[6:])
except TypeError:
raise AuthenticationError(response_code=403)
try:
# http://stackoverflow.com/questions/7242316/what-encoding-should-i-use-for-http-basic-authentication
user_pass_as_text = user_pass_raw.decode('ISO-8859-1')
except UnicodeDecodeError:
raise AuthenticationError(response_code=403)
user_pass = user_pass_as_text.split(':', 1)
if len(user_pass) != 2:
raise AuthenticationError(response_code=403)
if not self.validate_creds(*user_pass):
raise AuthenticationError(response_code=403)
else:
raise AuthenticationError(response_code=401,
response_headers={'WWW-Authenticate': 'Basic realm="Websockify"'})
def validate_creds(self, username, password):
if '%s:%s' % (username, password) == self.src:
return True
else:
return False
class ExpectOrigin(object):
def __init__(self, src=None):
if src is None:
self.source = []
else:
self.source = src.split()
def authenticate(self, headers, target_host, target_port):
origin = headers.get('Origin', None)
if origin is None or origin not in self.source:
raise InvalidOriginError(expected=self.source, actual=origin)

View File

@ -0,0 +1,83 @@
import os
class BasePlugin(object):
def __init__(self, src):
self.source = src
def lookup(self, token):
return None
class ReadOnlyTokenFile(BasePlugin):
# source is a token file with lines like
# token: host:port
# or a directory of such files
def __init__(self, *args, **kwargs):
super(ReadOnlyTokenFile, self).__init__(*args, **kwargs)
self._targets = None
def _load_targets(self):
if os.path.isdir(self.source):
cfg_files = [os.path.join(self.source, f) for
f in os.listdir(self.source)]
else:
cfg_files = [self.source]
self._targets = {}
for f in cfg_files:
for line in [l.strip() for l in open(f).readlines()]:
if line and not line.startswith('#'):
tok, target = line.split(': ')
self._targets[tok] = target.strip().rsplit(':', 1)
def lookup(self, token):
if self._targets is None:
self._load_targets()
if token in self._targets:
return self._targets[token]
else:
return None
# the above one is probably more efficient, but this one is
# more backwards compatible (although in most cases
# ReadOnlyTokenFile should suffice)
class TokenFile(ReadOnlyTokenFile):
# source is a token file with lines like
# token: host:port
# or a directory of such files
def lookup(self, token):
self._load_targets()
return super(TokenFile, self).lookup(token)
class BaseTokenAPI(BasePlugin):
# source is a url with a '%s' in it where the token
# should go
# we import things on demand so that other plugins
# in this file can be used w/o unecessary dependencies
def process_result(self, resp):
return resp.text.split(':')
def lookup(self, token):
import requests
resp = requests.get(self.source % token)
if resp.ok:
return self.process_result(resp)
else:
return None
class JSONTokenApi(BaseTokenAPI):
# source is a url with a '%s' in it where the token
# should go
def process_result(self, resp):
resp_json = resp.json()
return (resp_json['host'], resp_json['port'])

View File

@ -104,6 +104,8 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
self.handler_id = getattr(server, "handler_id", False)
self.file_only = getattr(server, "file_only", False)
self.traffic = getattr(server, "traffic", False)
self.auto_pong = getattr(server, "auto_pong", False)
self.strict_mode = getattr(server, "strict_mode", True)
self.logger = getattr(server, "logger", None)
if self.logger is None:
@ -111,6 +113,9 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
SimpleHTTPRequestHandler.__init__(self, req, addr, server)
def log_message(self, format, *args):
self.logger.info("%s - - [%s] %s" % (self.address_string(), self.log_date_time_string(), format % args))
@staticmethod
def unmask(buf, hlen, plen):
pstart = hlen + 4
@ -118,20 +123,24 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
if numpy:
b = c = s2b('')
if plen >= 4:
mask = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
offset=hlen, count=1)
data = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
offset=pstart, count=int(plen / 4))
dtype=numpy.dtype('<u4')
if sys.byteorder == 'big':
dtype = dtype.newbyteorder('>')
mask = numpy.frombuffer(buf, dtype, offset=hlen, count=1)
data = numpy.frombuffer(buf, dtype, offset=pstart,
count=int(plen / 4))
#b = numpy.bitwise_xor(data, mask).data
b = numpy.bitwise_xor(data, mask).tostring()
if plen % 4:
#self.msg("Partial unmask")
mask = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
offset=hlen, count=(plen % 4))
data = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
offset=pend - (plen % 4),
dtype=numpy.dtype('B')
if sys.byteorder == 'big':
dtype = dtype.newbyteorder('>')
mask = numpy.frombuffer(buf, dtype, offset=hlen,
count=(plen % 4))
data = numpy.frombuffer(buf, dtype,
offset=pend - (plen % 4), count=(plen % 4))
c = numpy.bitwise_xor(data, mask).tostring()
return b + c
else:
@ -172,7 +181,7 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
return header + buf, len(header), 0
@staticmethod
def decode_hybi(buf, base64=False, logger=None):
def decode_hybi(buf, base64=False, logger=None, strict=True):
""" Decode HyBi style WebSocket packets.
Returns:
{'fin' : 0_or_1,
@ -238,6 +247,10 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
f['length'])
else:
logger.debug("Unmasked frame: %s" % repr(buf))
if strict:
raise WebSocketRequestHandler.CClose(1002, "The client sent an unmasked frame.")
f['payload'] = buf[(f['hlen'] + f['masked'] * 4):full_len]
if base64 and f['opcode'] in [1, 2]:
@ -346,7 +359,8 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
while buf:
frame = self.decode_hybi(buf, base64=self.base64,
logger=self.logger)
logger=self.logger,
strict=self.strict_mode)
#self.msg("Received buf: %s, frame: %s", repr(buf), frame)
if frame['payload'] == None:
@ -360,6 +374,15 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
closed = {'code': frame['close_code'],
'reason': frame['close_reason']}
break
elif self.auto_pong and frame['opcode'] == 0x9: # ping
self.print_traffic("} ping %s\n" %
repr(frame['payload']))
self.send_pong(frame['payload'])
return [], False
elif frame['opcode'] == 0xA: # pong
self.print_traffic("} pong %s\n" %
repr(frame['payload']))
return [], False
self.print_traffic("}")
@ -388,10 +411,20 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
def send_close(self, code=1000, reason=''):
""" Send a WebSocket orderly close frame. """
msg = pack(">H%ds" % len(reason), code, reason)
msg = pack(">H%ds" % len(reason), code, s2b(reason))
buf, h, t = self.encode_hybi(msg, opcode=0x08, base64=False)
self.request.send(buf)
def send_pong(self, data=''):
""" Send a WebSocket pong frame. """
buf, h, t = self.encode_hybi(s2b(data), opcode=0x0A, base64=False)
self.request.send(buf)
def send_ping(self, data=''):
""" Send a WebSocket ping frame. """
buf, h, t = self.encode_hybi(s2b(data), opcode=0x09, base64=False)
self.request.send(buf)
def do_websocket_handshake(self):
h = self.headers
@ -444,9 +477,13 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
"""Upgrade a connection to Websocket, if requested. If this succeeds,
new_websocket_client() will be called. Otherwise, False is returned.
"""
if (self.headers.get('upgrade') and
self.headers.get('upgrade').lower() == 'websocket'):
# ensure connection is authorized, and determine the target
self.validate_connection()
if not self.do_websocket_handshake():
return False
@ -519,6 +556,10 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
""" Do something with a WebSockets client connection. """
raise Exception("WebSocketRequestHandler.new_websocket_client() must be overloaded")
def validate_connection(self):
""" Ensure that the connection is a valid connection, and set the target. """
pass
def do_HEAD(self):
if self.only_upgrade:
self.send_error(405, "Method Not Allowed")
@ -567,7 +608,7 @@ class WebSocketServer(object):
file_only=False,
run_once=False, timeout=0, idle_timeout=0, traffic=False,
tcp_keepalive=True, tcp_keepcnt=None, tcp_keepidle=None,
tcp_keepintvl=None):
tcp_keepintvl=None, auto_pong=False, strict_mode=True):
# settings
self.RequestHandlerClass = RequestHandlerClass
@ -581,6 +622,8 @@ class WebSocketServer(object):
self.timeout = timeout
self.idle_timeout = idle_timeout
self.traffic = traffic
self.file_only = file_only
self.strict_mode = strict_mode
self.launch_time = time.time()
self.ws_connection = False
@ -592,6 +635,7 @@ class WebSocketServer(object):
self.tcp_keepidle = tcp_keepidle
self.tcp_keepintvl = tcp_keepintvl
self.auto_pong = auto_pong
# Make paths settings absolute
self.cert = os.path.abspath(cert)
self.key = self.web = self.record = ''
@ -618,7 +662,10 @@ class WebSocketServer(object):
self.listen_host, self.listen_port)
self.msg(" - Flash security policy server")
if self.web:
self.msg(" - Web server. Web root: %s", self.web)
if self.file_only:
self.msg(" - Web server (no directory listings). Web root: %s", self.web)
else:
self.msg(" - Web server. Web root: %s", self.web)
if ssl:
if os.path.exists(self.cert):
self.msg(" - SSL/TLS support")
@ -701,6 +748,10 @@ class WebSocketServer(object):
@staticmethod
def daemonize(keepfd=None, chdir='/'):
if keepfd is None:
keepfd = []
os.umask(0)
if chdir:
os.chdir(chdir)
@ -723,7 +774,7 @@ class WebSocketServer(object):
if maxfd == resource.RLIM_INFINITY: maxfd = 256
for fd in reversed(range(maxfd)):
try:
if fd != keepfd:
if fd not in keepfd:
os.close(fd)
except OSError:
_, exc, _ = sys.exc_info()
@ -753,7 +804,7 @@ class WebSocketServer(object):
"""
ready = select.select([sock], [], [], 3)[0]
if not ready:
raise self.EClose("ignoring socket not ready")
# Peek, but do not read the data so that we have a opportunity
@ -761,7 +812,7 @@ class WebSocketServer(object):
handshake = sock.recv(1024, socket.MSG_PEEK)
#self.msg("Handshake [%s]" % handshake)
if handshake == "":
if not handshake:
raise self.EClose("ignoring empty handshake")
elif handshake.startswith(s2b("<policy-file-request/>")):
@ -844,11 +895,14 @@ class WebSocketServer(object):
raise self.Terminate()
def multiprocessing_SIGCHLD(self, sig, stack):
self.vmsg('Reaping zombies, active child count is %s', len(multiprocessing.active_children()))
# TODO: figure out a way to actually log this information without
# calling `log` in the signal handlers
multiprocessing.active_children()
def fallback_SIGCHLD(self, sig, stack):
# Reap zombies when using os.fork() (python 2.4)
self.vmsg("Got SIGCHLD, reaping zombies")
# TODO: figure out a way to actually log this information without
# calling `log` in the signal handlers
try:
result = os.waitpid(-1, os.WNOHANG)
while result[0]:
@ -858,16 +912,18 @@ class WebSocketServer(object):
pass
def do_SIGINT(self, sig, stack):
self.msg("Got SIGINT, exiting")
# TODO: figure out a way to actually log this information without
# calling `log` in the signal handlers
self.terminate()
def do_SIGTERM(self, sig, stack):
self.msg("Got SIGTERM, exiting")
# TODO: figure out a way to actually log this information without
# calling `log` in the signal handlers
self.terminate()
def top_new_client(self, startsock, address):
""" Do something with a WebSockets client connection. """
# handler process
# handler process
client = None
try:
try:
@ -890,6 +946,18 @@ class WebSocketServer(object):
# Original socket closed by caller
client.close()
def get_log_fd(self):
"""
Get file descriptors for the loggers.
They should not be closed when the process is forked.
"""
descriptors = []
for handler in self.logger.parent.handlers:
if isinstance(handler, logging.FileHandler):
descriptors.append(handler.stream.fileno())
return descriptors
def start_server(self):
"""
Daemonize if requested. Listen for for connections. Run
@ -905,7 +973,9 @@ class WebSocketServer(object):
tcp_keepintvl=self.tcp_keepintvl)
if self.daemon:
self.daemonize(keepfd=lsock.fileno(), chdir=self.web)
keepfd = self.get_log_fd()
keepfd.append(lsock.fileno())
self.daemonize(keepfd=keepfd, chdir=self.web)
self.started() # Some things need to happen after daemonizing
@ -1009,8 +1079,17 @@ class WebSocketServer(object):
except (self.Terminate, SystemExit, KeyboardInterrupt):
self.msg("In exit")
# terminate all child processes
if multiprocessing and not self.run_once:
children = multiprocessing.active_children()
for child in children:
self.msg("Terminating child %s" % child.pid)
child.terminate()
break
except Exception:
exc = sys.exc_info()[1]
self.msg("handler exception: %s", str(exc))
self.vmsg("exception", exc_info=True)

View File

@ -11,13 +11,14 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
'''
import signal, socket, optparse, time, os, sys, subprocess, logging
import signal, socket, optparse, time, os, sys, subprocess, logging, errno
try: from socketserver import ForkingMixIn
except: from SocketServer import ForkingMixIn
try: from http.server import HTTPServer
except: from BaseHTTPServer import HTTPServer
from select import select
import websocket
import select
from websockify import websocket
from websockify import auth_plugins as auth
try:
from urllib.parse import parse_qs, urlparse
except:
@ -38,14 +39,33 @@ Traffic Legend:
<. - Client send partial
"""
def send_auth_error(self, ex):
self.send_response(ex.code, ex.msg)
self.send_header('Content-Type', 'text/html')
for name, val in ex.headers.items():
self.send_header(name, val)
self.end_headers()
def validate_connection(self):
if self.server.token_plugin:
(self.server.target_host, self.server.target_port) = self.get_target(self.server.token_plugin, self.path)
if self.server.auth_plugin:
try:
self.server.auth_plugin.authenticate(
headers=self.headers, target_host=self.server.target_host,
target_port=self.server.target_port)
except auth.AuthenticationError:
ex = sys.exc_info()[1]
self.send_auth_error(ex)
raise
def new_websocket_client(self):
"""
Called after a new WebSocket connection has been established.
"""
# Checks if we receive a token, and look
# for a valid target for it then
if self.server.target_cfg:
(self.server.target_host, self.server.target_port) = self.get_target(self.server.target_cfg, self.path)
# Checking for a token is done in validate_connection()
# Connect to the target
if self.server.wrap_cmd:
@ -73,15 +93,15 @@ Traffic Legend:
if tsock:
tsock.shutdown(socket.SHUT_RDWR)
tsock.close()
if self.verbose:
if self.verbose:
self.log_message("%s:%s: Closed target",
self.server.target_host, self.server.target_port)
raise
def get_target(self, target_cfg, path):
def get_target(self, target_plugin, path):
"""
Parses the path, extracts a token, and looks for a valid
target for that token in the configuration file(s). Sets
Parses the path, extracts a token, and looks up a target
for that token using the token plugin. Sets
target_host and target_port if successful
"""
# The files in targets contain the lines
@ -90,32 +110,17 @@ Traffic Legend:
# Extract the token parameter from url
args = parse_qs(urlparse(path)[4]) # 4 is the query from url
if not args.has_key('token') or not len(args['token']):
raise self.EClose("Token not present")
if not 'token' in args or not len(args['token']):
raise self.server.EClose("Token not present")
token = args['token'][0].rstrip('\n')
# target_cfg can be a single config file or directory of
# config files
if os.path.isdir(target_cfg):
cfg_files = [os.path.join(target_cfg, f)
for f in os.listdir(target_cfg)]
result_pair = target_plugin.lookup(token)
if result_pair is not None:
return result_pair
else:
cfg_files = [target_cfg]
targets = {}
for f in cfg_files:
for line in [l.strip() for l in file(f).readlines()]:
if line and not line.startswith('#'):
ttoken, target = line.split(': ')
targets[ttoken] = target.strip()
self.vmsg("Target config: %s" % repr(targets))
if targets.has_key(token):
return targets[token].split(':')
else:
raise self.EClose("Token '%s' not found" % token)
raise self.server.EClose("Token '%s' not found" % token)
def do_proxy(self, target):
"""
@ -126,12 +131,37 @@ Traffic Legend:
tqueue = []
rlist = [self.request, target]
if self.server.heartbeat:
now = time.time()
self.heartbeat = now + self.server.heartbeat
else:
self.heartbeat = None
while True:
wlist = []
if self.heartbeat is not None:
now = time.time()
if now > self.heartbeat:
self.heartbeat = now + self.server.heartbeat
self.send_ping()
if tqueue: wlist.append(target)
if cqueue or c_pend: wlist.append(self.request)
ins, outs, excepts = select(rlist, wlist, [], 1)
try:
ins, outs, excepts = select.select(rlist, wlist, [], 1)
except (select.error, OSError):
exc = sys.exc_info()[1]
if hasattr(exc, 'errno'):
err = exc.errno
else:
err = exc[0]
if err != errno.EINTR:
raise
else:
continue
if excepts: raise Exception("Socket exception")
if self.request in outs:
@ -147,7 +177,7 @@ Traffic Legend:
if closed:
# TODO: What about blocking on client socket?
if self.verbose:
if self.verbose:
self.log_message("%s:%s: Client closed connection",
self.server.target_host, self.server.target_port)
raise self.CClose(closed['code'], closed['reason'])
@ -195,7 +225,11 @@ class WebSocketProxy(websocket.WebSocketServer):
self.wrap_mode = kwargs.pop('wrap_mode', None)
self.unix_target = kwargs.pop('unix_target', None)
self.ssl_target = kwargs.pop('ssl_target', None)
self.target_cfg = kwargs.pop('target_cfg', None)
self.heartbeat = kwargs.pop('heartbeat', None)
self.token_plugin = kwargs.pop('token_plugin', None)
self.auth_plugin = kwargs.pop('auth_plugin', None)
# Last 3 timestamps command was run
self.wrap_times = [0, 0, 0]
@ -251,9 +285,9 @@ class WebSocketProxy(websocket.WebSocketServer):
else:
dst_string = "%s:%s" % (self.target_host, self.target_port)
if self.target_cfg:
msg = " - proxying from %s:%s to targets in %s" % (
self.listen_host, self.listen_port, self.target_cfg)
if self.token_plugin:
msg = " - proxying from %s:%s to targets generated by %s" % (
self.listen_host, self.listen_port, type(self.token_plugin).__name__)
else:
msg = " - proxying from %s:%s to %s" % (
self.listen_host, self.listen_port, dst_string)
@ -352,20 +386,69 @@ def websockify_init():
parser.add_option("--prefer-ipv6", "-6",
action="store_true", dest="source_is_ipv6",
help="prefer IPv6 when resolving source_addr")
parser.add_option("--libserver", action="store_true",
help="use Python library SocketServer engine")
parser.add_option("--target-config", metavar="FILE",
dest="target_cfg",
help="Configuration file containing valid targets "
"in the form 'token: host:port' or, alternatively, a "
"directory containing configuration files of this form")
parser.add_option("--libserver", action="store_true",
help="use Python library SocketServer engine")
"directory containing configuration files of this form "
"(DEPRECATED: use `--token-plugin TokenFile --token-source "
" path/to/token/file` instead)")
parser.add_option("--token-plugin", default=None, metavar="PLUGIN",
help="use the given Python class to process tokens "
"into host:port pairs")
parser.add_option("--token-source", default=None, metavar="ARG",
help="an argument to be passed to the token plugin"
"on instantiation")
parser.add_option("--auth-plugin", default=None, metavar="PLUGIN",
help="use the given Python class to determine if "
"a connection is allowed")
parser.add_option("--auth-source", default=None, metavar="ARG",
help="an argument to be passed to the auth plugin"
"on instantiation")
parser.add_option("--auto-pong", action="store_true",
help="Automatically respond to ping frames with a pong")
parser.add_option("--heartbeat", type=int, default=0,
help="send a ping to the client every HEARTBEAT seconds")
parser.add_option("--log-file", metavar="FILE",
dest="log_file",
help="File where logs will be saved")
(opts, args) = parser.parse_args()
if opts.log_file:
opts.log_file = os.path.abspath(opts.log_file)
handler = logging.FileHandler(opts.log_file)
handler.setLevel(logging.DEBUG)
handler.setFormatter(logging.Formatter("%(message)s"))
logging.getLogger(WebSocketProxy.log_prefix).addHandler(handler)
del opts.log_file
if opts.verbose:
logging.getLogger(WebSocketProxy.log_prefix).setLevel(logging.DEBUG)
if opts.token_source and not opts.token_plugin:
parser.error("You must use --token-plugin to use --token-source")
if opts.auth_source and not opts.auth_plugin:
parser.error("You must use --auth-plugin to use --auth-source")
# Transform to absolute path as daemon may chdir
if opts.target_cfg:
opts.target_cfg = os.path.abspath(opts.target_cfg)
if opts.target_cfg:
opts.token_plugin = 'TokenFile'
opts.token_source = opts.target_cfg
del opts.target_cfg
# Sanity checks
if len(args) < 2 and not (opts.target_cfg or opts.unix_target):
if len(args) < 2 and not (opts.token_plugin or opts.unix_target):
parser.error("Too few arguments")
if sys.argv.count('--'):
opts.wrap_cmd = args[1:]
@ -390,7 +473,7 @@ def websockify_init():
try: opts.listen_port = int(opts.listen_port)
except: parser.error("Error parsing listen port")
if opts.wrap_cmd or opts.unix_target or opts.target_cfg:
if opts.wrap_cmd or opts.unix_target or opts.token_plugin:
opts.target_host = None
opts.target_port = None
else:
@ -402,9 +485,32 @@ def websockify_init():
try: opts.target_port = int(opts.target_port)
except: parser.error("Error parsing target port")
# Transform to absolute path as daemon may chdir
if opts.target_cfg:
opts.target_cfg = os.path.abspath(opts.target_cfg)
if opts.token_plugin is not None:
if '.' not in opts.token_plugin:
opts.token_plugin = (
'websockify.token_plugins.%s' % opts.token_plugin)
token_plugin_module, token_plugin_cls = opts.token_plugin.rsplit('.', 1)
__import__(token_plugin_module)
token_plugin_cls = getattr(sys.modules[token_plugin_module], token_plugin_cls)
opts.token_plugin = token_plugin_cls(opts.token_source)
del opts.token_source
if opts.auth_plugin is not None:
if '.' not in opts.auth_plugin:
opts.auth_plugin = 'websockify.auth_plugins.%s' % opts.auth_plugin
auth_plugin_module, auth_plugin_cls = opts.auth_plugin.rsplit('.', 1)
__import__(auth_plugin_module)
auth_plugin_cls = getattr(sys.modules[auth_plugin_module], auth_plugin_cls)
opts.auth_plugin = auth_plugin_cls(opts.auth_source)
del opts.auth_source
# Create and start the WebSockets proxy
libserver = opts.libserver
@ -433,9 +539,13 @@ class LibProxyServer(ForkingMixIn, HTTPServer):
self.wrap_mode = kwargs.pop('wrap_mode', None)
self.unix_target = kwargs.pop('unix_target', None)
self.ssl_target = kwargs.pop('ssl_target', None)
self.target_cfg = kwargs.pop('target_cfg', None)
self.token_plugin = kwargs.pop('token_plugin', None)
self.auth_plugin = kwargs.pop('auth_plugin', None)
self.heartbeat = kwargs.pop('heartbeat', None)
self.token_plugin = None
self.auth_plugin = None
self.daemon = False
self.target_cfg = None
# Server configuration
listen_host = kwargs.pop('listen_host', '')
@ -456,8 +566,8 @@ class LibProxyServer(ForkingMixIn, HTTPServer):
if web:
os.chdir(web)
HTTPServer.__init__(self, (listen_host, listen_port),
HTTPServer.__init__(self, (listen_host, listen_port),
RequestHandlerClass)

View File

@ -109,7 +109,7 @@ class OpenNebulaVNC
@pipe = nil
@token_folder = File.join(VAR_LOCATION, opts[:token_folder_name])
@proxy_path = File.join(SHARE_LOCATION, "websockify/websocketproxy.py")
@proxy_path = File.join(SHARE_LOCATION, "websockify/run")
@proxy_port = config[:vnc_proxy_port]
@proxy_ipv6 = config[:vnc_proxy_ipv6]
@ -151,10 +151,7 @@ class OpenNebulaVNC
proxy_options << " -6"
end
system("which python2 >/dev/null 2>&1")
python = $?.success? ? "python2" : "python"
cmd ="#{python} #{@proxy_path} #{proxy_options} #{@proxy_port}"
cmd ="python #{@proxy_path} #{proxy_options} #{@proxy_port}"
begin
@logger.info { "Starting VNC proxy: #{cmd}" }