Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Under tornado v4+ WebSocket connections get refused with 403

I have an older tornado server that handles vanilla WebSocket connections. I proxy these connections, via Nginx, from wss://info.mydomain.com to wss://mydomain.com:8080 in order to get around customer proxies that block non standard ports.

After the recent upgrade to Tornado 4.0 all connections get refused with a 403. What is causing this problem and how can I fix it?

like image 766
aychedee Avatar asked Jul 17 '14 10:07

aychedee


People also ask

What is Tornado WebSocket?

websocket — Bidirectional communication to the browser. Implementation of the WebSocket protocol. WebSockets allow for bidirectional communication between the browser and server.

How do I enable WebSockets in nginx?

Configuring Proxy ServerCreate a new conf file for the Nginx server that will accept WebSocket requests. Saving your settings and then restart the NGINX server will enable it to support WebSocket connections.

How many WebSocket connections can a server hold?

The theoretical limit is 65k connections per IP address but the actual limit is often more like 20k, so we use multiple addresses to connect 20k to each (50 * 20k = 1 mil). I then run the web server as root by typing sudo -i followed by ulimit -n 1024000 and then node examples/WebSocket. js (in the uWebSockets.

Can you proxy WebSocket?

WebSocket communication can take successfully take place in the presence of forward proxies, providing the client and proxy server have been configured properly to deal with it.


2 Answers

Tornado 4.0 introduced an, on by default, same origin check. This checks that the origin header set by the browser is the same as the host header

The code looks like:

 def check_origin(self, origin):
    """Override to enable support for allowing alternate origins.

    The ``origin`` argument is the value of the ``Origin`` HTTP header,
    the url responsible for initiating this request.

    .. versionadded:: 4.0
    """
    parsed_origin = urlparse(origin)
    origin = parsed_origin.netloc
    origin = origin.lower()

    host = self.request.headers.get("Host")

    # Check to see that origin matches host directly, including ports
    return origin == host

In order for your proxied websocket connection to still work you will need to override check origin on the WebSocketHandler and whitelist the domains that you care about. Something like this.

import re
from tornado import websocket

class YouConnection(websocket.WebSocketHandler):

    def check_origin(self, origin):
        return bool(re.match(r'^.*?\.mydomain\.com', origin))

This will let the connections coming through from info.mydomain.com to get through as before.

like image 146
aychedee Avatar answered Nov 16 '22 03:11

aychedee


I would like to propose and alternative solution, instead of messing with the tornado application code, I solved the issue by telling nginx to fix the host header:

location /ws {
proxy_set_header Host $host;
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
like image 28
tovmeod Avatar answered Nov 16 '22 02:11

tovmeod