From: Quentin Smith Date: Sun, 17 Jun 2018 03:27:35 +0000 (-0400) Subject: Drop privileges in VNC proxy if requested X-Git-Tag: 0.1.57~5 X-Git-Url: http://xvm.mit.edu/gitweb/invirt/packages/invirt-web.git/commitdiff_plain/921b070eca3ccdf51b685020f8bf9c228c885d9d Drop privileges in VNC proxy if requested --- diff --git a/files/usr/bin/invirt-novnc-wsproxy b/files/usr/bin/invirt-novnc-wsproxy index f66aaba..708a9b1 100755 --- a/files/usr/bin/invirt-novnc-wsproxy +++ b/files/usr/bin/invirt-novnc-wsproxy @@ -1,5 +1,9 @@ #!/usr/bin/env python +import os +import pwd +import grp +import contextlib import socket import ssl from select import select @@ -8,6 +12,23 @@ import urlparse 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 = ( @@ -16,12 +37,44 @@ _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) @@ -46,13 +99,20 @@ class WebSocketProxy(WebSocketServer): 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: @@ -131,8 +191,19 @@ class WebSocketProxy(WebSocketServer): 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()