#!/usr/bin/env python
+import os
+import pwd
+import grp
+import contextlib
import socket
import ssl
from select import select
from novnc.websocket import WebSocketServer
import invirt.remctl
from invirt.config import structs as config
+from optparse import OptionParser
+
+def drop_privileges(uid_name='nobody', gid_name='nogroup'):
+ if os.getuid() != 0:
+ return
+
+ # Get the uid/gid from the name
+ uid = pwd.getpwnam(uid_name).pw_uid
+ gid = grp.getgrnam(gid_name).gr_gid
+
+ # Try setting the new uid/gid
+ os.setgid(gid)
+ os.setuid(uid)
+
+ # Ensure a very convervative umask
+ new_umask = 077
+ os.umask(new_umask)
# From Python >=2.7.9
_RESTRICTED_SERVER_CIPHERS = (
'!eNULL:!MD5:!DSS:!RC4'
)
+@contextlib.contextmanager
+def noop():
+ yield
+
class WebSocketProxy(WebSocketServer):
"""
Proxy traffic from a WebSockets client to an Invirt VNC server,
doing the auth handshake for the client.
"""
+ def __init__(self, user=None, group=None, *args, **kwargs):
+ super(WebSocketProxy, self).__init__(*args, **kwargs)
+ self.user = user
+ self.group = group
+ self.server_cas = None
+
+ def started(self):
+ super(WebSocketProxy, self).started()
+ if self.user:
+ cert = open(self.cert).read()
+ key = open(self.key).read()
+ cas = ""
+ for h in config.hosts:
+ cas += invirt.remctl.remctl(config.remote.hostname, "web", "vnccert", h.hostname)
+ drop_privileges(self.user, self.group)
+ self.cert_tf = tempfile.NamedTemporaryFile()
+ self.cert_tf.write(cert)
+ self.cert_tf.flush()
+ self.cert = self.cert_tf.name
+ self.key_tf = tempfile.NamedTemporaryFile()
+ self.key_tf.write(key)
+ self.key_tf.flush()
+ self.key = self.key_tf.name
+ self.server_cafile = tempfile.NamedTemporaryFile()
+ self.server_cafile.write(cas)
+ self.server_cafile.flush()
+ self.server_cas = self.server_cafile.name
+
def new_client(self):
url = urlparse.urlparse(self.path)
query = urlparse.parse_qs(url.query)
tsock = self.socket(target_host, target_port, connect=True)
- with tempfile.NamedTemporaryFile() as cafile:
- cadata = invirt.remctl.remctl(config.remote.hostname, "web", "vnccert", host)
- cafile.write(cadata)
- cafile.flush()
+ server_cas = self.server_cas
+ ctx = noop()
+ if not server_cas:
+ ctx = tempfile.NamedTemporaryFile()
+
+ with ctx as cafile:
+ if not server_cas:
+ cadata = invirt.remctl.remctl(config.remote.hostname, "web", "vnccert", host)
+ cafile.write(cadata)
+ cafile.flush()
+ server_cas = cafile.name
# TODO: Use ssl.create_default_context when we move to Python >=2.7.9
- tsock = ssl.wrap_socket(tsock, ca_certs=cafile.name, cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_SSLv23, ciphers=_RESTRICTED_SERVER_CIPHERS)
+ tsock = ssl.wrap_socket(tsock, ca_certs=server_cas, cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_SSLv23, ciphers=_RESTRICTED_SERVER_CIPHERS)
# Start proxying
try:
raise self.EClose(closed)
if __name__ == '__main__':
+ parser = OptionParser()
+ parser.add_option("-u", "--user", dest="user", default="nobody",
+ help="user to drop privileges to", metavar="USER")
+ parser.add_option("-g", "--group", dest="group", default="nogroup",
+ help="group to drop privileges to", metavar="GROUP")
+
+ (options, args) = parser.parse_args()
+
server = WebSocketProxy(cert="/etc/apache2/ssl/server.crt",
key="/etc/apache2/ssl/server.key",
listen_port=config.vnc.novnc_port,
- ssl_only=True)
+ ssl_only=True,
+ user=options.user,
+ group=options.group,
+ )
server.start_server()