Tuesday, 13 November 2012

F5 and Websockets

UPDATE: Well even though the F5 engineer swore that websockets were unsupported - it appears a bug has appeared with relation to handling of websockets and according to ask.f5.com - they now work as of TMOS >= 11.4

If you're interested in getting some shiny new HTML5 websockets working through an F5 load balancer with HTTP profiles enabled, you're in for some disappointment.
Despite websockets being documented in the RFC as proxy compatible - they don't work through an F5 when you are using full-proxy mode (i.e. you have an HTTP profile assigned). This looks to be down to the websockets connection sending an 'upgrade' notification to the client which turns the HTTP connection into a standard TCP connection (still on port 80/443 however but not strictly HTTP).

Anyway, unless you know of some hardcore iRule magic - I had to have a bit of an architectural rethink. Our websockets application on the backend works off of a virtual host which has the websockets proxy enabled on it. It runs on the same server as the main web application so you can see where disabling the HTTP profile becomes a problem if I want to run any iRules relating to HTTP. The way we've got around it is to create a new public virtual server then redirect any connections going to the /websocket  to the new virtual server using an iRule. On the new virtual server - you still assign an HTTP profile and then create an iRule so that only connections to the /websocket path go to the virtual server and anything else is redirected back to the main VS. It's a bit of a crude solution, but it's simple enough to do the job. For what it's worth, websockets support is in the pipeline apparently.
So for the iRules...

iRule for VS_main_web_app


when HTTP::REQUEST {
if {[HTTP::URI] starts_with '/websocket'}
  {
  HTTP::redirect "https://websocket.domain.com"
  }
}


iRule for VS_websocket_only


when HTTP::REQUEST {
if {![HTTP::URI] starts_with "/websocket"}
  {
  HTTP::redirect "https://mainapp.domain.com"
  }
else
  {
  HTTP::disable
  }
}


3 comments:

  1. Hi,

    I've recently been looking to solve just this problem, and have just come across your post from a year ago. As well as using websockets, I'm trying to hide URIs from the external world using iRules.

    I'm curious to learn how have you gotten around the issue of a user navigating the browser to https://websocket.domain.com/websockets/xxx, which disables the HTTP profile in its iRule and then changing their browser's URI to be https://websocket.domain.com/restricted-admin-pages/xxxx ?

    Thank you in advance for your help.

    ReplyDelete
    Replies
    1. Hi, in my situation the HTTP profile didn't provide any protection of paths within the application. I would have thought the application itself would prevent access to restricted pages unless you're using iRules to implement this check?

      Delete
    2. Yes, you're right; I'm using an iRule to do both the HTTP profile disabling for Websocket support and also to hide URLs.

      I spent some time looking at this and have come up with a solution that works quite well. Here's my iRule. I'd be interested in getting your opinion

      ---------------------

      when CLIENT_ACCEPTED {
      HTTP::enable
      }

      when HTTP_REQUEST {
      if { ([HTTP::uri] starts_with "/the-websocket-uri") } {
      HTTP::disable
      } elseif { [class match [HTTP::uri] starts_with UnrestrictedUris] } {
      # Do nothing by design - leaving the HTTP profile enabled
      } else {
      # Drop the request
      drop
      }
      }

      Delete