SSLH enables connections for SSH and HTTPS on one virtual server

Problem this snippet solves:

This iRule will enable both SSH and HTTPS connections on the same virtual server, while still having the ability to decrypt and re-encrypt the HTTPS traffic as usual. I commonly run an SSH server on port 443, so I can easily access my system through restrictive firewalls, proxies, networks, etc. But I also like to run an HTTPS server. So if I only have a single external IP address, I can typically only run one or the other. There are other scripts/programs out there that will do this, but I figured - if I can do this with an iRule, then do it with an iRule!

So, here it is: SSLH via iRule!

There is one big different between SSH and HTTPS that we can look for and act accordingly. That difference is how the protocols act after the 3-way handshake is complete. With SSH, the server will send the next packet, immediately responding with a banner. With HTTPS, the client will send the next packet, immediately initiating the ssl handshake. We can watch for this difference and then enable or disable profiles as needed. All we need to do is detect which side sends the first packet after the 3-way handshake completes. We can accomplish this using the relatively new 'after' command to pause for a period of time to wait for the identifying packet across the connection. The virtual server will need clientssl and serverssl profiles, along with an http profile, and a destination of port 443. Each pool needs to have proper members - https_pool has members on port 443 and ssh_pool has members on port 22. Here is the minimum virtual server configuration:

Code :

virtual sslh_vs {
   pool https_pool
   ip protocol tcp
   rules {
   profiles {
      http {}
      serverssl {
      clientssl {
      tcp {}

when RULE_INIT {
  # How many milliseconds to we pause for while waiting for the identifying packet
  set ::pause_time 1500
  # We need the ssl and http profiles on the virtual server, but we need them disabled for ssh connections
  SSL::disable clientside
  SSL::disable serverside

  # Collect the first 8 bytes from the client
  # For HTTPS, the client will send data
  # For SSH, the client won't send any data
  TCP::collect 8

  # Set a variable for tracking if this is an http stream or not
  set http_stream 0

  log local0. "Client connection established -- collecting data from [IP::client_addr]:[TCP::client_port]"

  # Wait $::pause_time for client or server to respond
  after $::pause_time {
    # http_stream variable may be changed in CLIENT_DATA - handle accordingly
    if { $http_stream eq 0 } {
       # http_stream not changed in CLIENT_DATA - must be SSH connection
      log local0. "Didn't receive data from [IP::client_addr]:[TCP::client_port] within $::pause_time ms - sending to ssh pool"
      pool ssh_pool
    } else {
      # http_stream changed in CLIENT_DATA - must be HTTPS connection
      log local0. "Received data from [IP::client_addr]:[TCP::client_port] within $::pause_time ms - should already be headed to https pool"
  # only HTTPS client will trigger CLIENT_DATA
  if { $http_stream eq 0 } {
    # new HTTPS connection, so we enable profiles, set http_stream to 1, and send traffic to pool
    log local0. "Received client data from [IP::client_addr]:[TCP::client_port] - assuming this is an https request"
    SSL::enable clientside
    SSL::enable serverside
    set http_stream 1
    pool https_pool
  } else {
    # http_stream already changed, so we've handled this connection already
    log local0. "Passing through existing https connection"
Published Mar 18, 2015
Version 1.0

Was this article helpful?

No CommentsBe the first to comment