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 destination 192.168.1.100:https ip protocol tcp rules { sslh_irule } profiles { http {} serverssl { serverside } clientssl { clientside } tcp {} } } when RULE_INIT { # How many milliseconds to we pause for while waiting for the identifying packet set ::pause_time 1500 } when CLIENT_ACCEPTED { # 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 HTTP::disable # 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" LB::detach pool ssh_pool TCP::release } 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" } } } when CLIENT_DATA { # 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 HTTP::enable set http_stream 1 pool https_pool TCP::release } else { # http_stream already changed, so we've handled this connection already log local0. "Passing through existing https connection" } }