Forum Discussion

Troy_Murray_196's avatar
May 09, 2019

Mattermost, F5 LTM, and Websockets

I recently worked with a team that wanted to use the F5 Local Traffic Manager (LTM) feature to load balance connections to their new deployment of the Mattermost open source messaging platform within their on-premises datacenter. This application uses both HTTPS and Websockets connections for real-time chat. We ran into a few configuration issues but eventually found the right combination of “nerd knobs” to allow successful ingress traffic. This post is to consolidate these details and hopefully save time to other F5 engineers attempting to do the same.

Business Requirements

  • Ingress (client-side) connections TLS 1.1 or higher
  • Support Websockets
  • Only allow customized Mattermost mobile (iOS and Android) applications, provisioned from within the organization and using a custom header, to connect from the public Internet.
  • The same VIP for the mobile traffic should be used by internal desktop or web browsers, which will not include this custom header

Virtual Server Configuration

The virtual server configuration was fairly straight forward:

  • Protocol Profile (Client) = a mobile optimized TCP profile
  • Protocol Profile (Server) = a LAN optimized TCP profile
  • SSL Profile (Client) = profile with the Option
    No TLSv1
    enabled
  • SSL Profile (Server) = standard serverssl profile or custom one
  • WebSocket Profile = WebSocket (or a custom one with this as the parent)
  • SNAT = custom pool, but AutoMap would work
  • OneConnect = standard oneconnect profile (or a custom one with this as the parent)
  • Default Persistence = cookie
  • Fallback Persistence = source address
  • Pool = Mattermost server pool
  • iRule = custom Mattermost iRule

Mattermost iRule

To meet some of the business requirements a custom iRule was created to handle some of the conditions outlined. Comments in-line, but this checks to see if the connection was outside of the organization and if so verifies the presence of the custom HTTP header and value. This also checks to see if the connection was requested to upgrade to Websockets, and if it is, change the HTTP filter from full parsing to passthrough mode.

when HTTP_REQUEST {
  if { !(IP:addr [IP::client_addr] equals 192.168.0.0/255.255.255.0]) } {
     Request from IP outside of organization, check for customer HTTP header
    if { [HTTP::header x-the-custom-http-header-name] contains "customvalue" } {
       Custom HTTP header and matching value found
      if { [string tolower [HTTP::header Upgrade]] contains "websocket" }{
         Connection is requesting WebSockets, stop HTTP parsing
        HTTP::disable
      }
    }
    elseif { [HTTP::cookie exists MMAUTHENTOKEN] && [HTTP::cookie exists MMUSERID] } {
       Since WebSocket connections do not have HTTP Header, check to see if
       connection has already authenticated and allow the connection
      return
    }
    else {
       Connection fails conditions, reject it
      reject
    }
  }
  else {
    if { [string tolower [HTTP::header Upgrade]] contains "websocket" }{
       Connection is requesting WebSockets, stop HTTP parsing
      HTTP::disable
    }
  }
}
  • Hi,

    Great post. I wonder why in VS configuration no HTTP profile is mentioned but in iRule there is HTTP::disable? Is that because of omission in VS configuration part or some leftover in iRule?

    Piotr

  • Troy,

     

    Thank You for the write-up! I stumbled on this while starting at Zero for the same problem set. Would you be willing to discuss (privately if you'd prefer) how it's been working for you since, and possibly how you incorporated the header check on the App side? We're having to "white label" the iOS App for some security reasons, but it would definitely assist in totality incorporating a header check that prevents a third party app from connecting as well!

     

    Thank You,

     

    Bryan