5 from select import select
8 from novnc.websocket import WebSocketServer
10 from invirt.config import structs as config
13 _RESTRICTED_SERVER_CIPHERS = (
14 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:'
15 'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:!aNULL:'
16 '!eNULL:!MD5:!DSS:!RC4'
19 class WebSocketProxy(WebSocketServer):
21 Proxy traffic from a WebSockets client to an Invirt VNC server,
22 doing the auth handshake for the client.
26 url = urlparse.urlparse(self.path)
27 query = urlparse.parse_qs(url.query)
29 host = query.get('host', [None])[-1]
30 vmname = query.get('vmname', [None])[-1]
31 token = query.get('token', [None])[-1]
34 target_port = config.vnc.base_port
36 for h in config.hosts:
37 if h.hostname == host:
41 raise Exception("host not found")
43 raise Exception("vmname not provided")
45 raise Exception("token not provided")
47 tsock = self.socket(target_host, target_port, connect=True)
49 with tempfile.NamedTemporaryFile() as cafile:
50 cadata = invirt.remctl.remctl(config.remote.hostname, "web", "vnccert", host)
54 # TODO: Use ssl.create_default_context when we move to Python >=2.7.9
55 tsock = ssl.wrap_socket(tsock, ca_certs=cafile.name, cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_SSLv23, ciphers=_RESTRICTED_SERVER_CIPHERS)
59 extra_data = self.do_auth_handshake(tsock, vmname, token)
60 self.do_proxy(tsock, extra_data)
63 tsock.shutdown(socket.SHUT_RDWR)
65 self.vmsg("%s:%s: Target closed" % (
66 target_host, target_port))
69 def do_auth_handshake(self, target, vmname, token):
70 target.send("CONNECTVNC %s VNCProxy/1.0\r\nAuth-token: %s\r\n\r\n" % (vmname, token))
71 data = target.recv(128)
72 if data.startswith("VNCProxy/1.0 200 "):
74 return data[data.find("\n")+3:]
79 def do_proxy(self, target, extra_data):
81 Proxy client WebSocket to normal target socket.
86 rlist = [self.client, target]
89 tqueue.append(extra_data)
94 if tqueue: wlist.append(target)
95 if cqueue or c_pend: wlist.append(self.client)
96 ins, outs, excepts = select(rlist, wlist, [], 1)
97 if excepts: raise Exception("Socket exception")
100 # Send queued client data to the target
102 sent = target.send(dat)
104 # requeue the remaining data
105 tqueue.insert(0, dat[sent:])
109 # Receive target data, encode it and queue for client
110 buf = target.recv(self.buffer_size)
111 if len(buf) == 0: raise self.EClose("Target closed")
116 if self.client in outs:
117 # Send queued target data to the client
118 c_pend = self.send_frames(cqueue)
123 if self.client in ins:
124 # Receive client data, decode it, and queue for target
125 bufs, closed = self.recv_frames()
129 # TODO: What about blocking on client socket?
131 raise self.EClose(closed)
133 if __name__ == '__main__':
134 server = WebSocketProxy(cert="/etc/apache2/ssl/server.crt",
135 key="/etc/apache2/ssl/server.key",
136 listen_port=config.vnc.novnc_port,
138 server.start_server()