Handling HTTP Requests on an HTTPS Virtual Server
There are scenarios where it might be prudent to support HTTP request redirection on a single port, and thus, a single virtual server. Yes, this can be done with the alias port zero, but that locks all other ports down unless you plan to build out a pretty extensive iRule to support the various services required for each port. This latter option is less than ideal. So what can be done?
TL;DR - only two steps required. First, check the Non-SSL Connections box in the SSL profile. Second, create an iRule to redirect non-SSL connections to SSL.
The Details
I have a test virtual server, appropriately called testvip, and on that testvip I have no iRules and a clientssl profile attached with only my certificate key chain changed from the parent profile.
ltm virtual testvip { destination 192.168.102.50:https ip-protocol tcp mask 255.255.255.255 pool testpool profiles { cssl { context clientside } http { } tcp { } } source 0.0.0.0/0 source-address-translation { type automap } translate-address enabled translate-port enabled vs-index 50 } ltm profile client-ssl cssl { app-service none cert myssl.crt cert-key-chain { myssl { cert myssl.crt key myssl.key } } chain none defaults-from clientssl inherit-certkeychain false key myssl.key passphrase none }
In this configuration, I expect that normal HTTPS requests will work just fine, and HTTP requests will fail. Let's take a look with curl, first with the working HTTPS and then the failing HTTP: (leaving curl details out for brevity here)
### HTTPS ### MY-MAC:~ rahm$ curl -v -s -k https://test.test.local/ 1> /dev/null -> HTTP/1.1 200 OK ### HTTP ### MY-MAC:~ rahm$ curl -v -s -k http://test.test.local:443 1> /dev/null -> Empty reply from server
Now that we have confirmed that the regular HTTP request is not working, let's enable non-SSL connections. Again, that's done with the checkbox in the clientssl profile:
Now let's try that second test again.
### HTTP ### MY-MAC:~ rahm$ curl -v -s -k http://test.test.local:443 1> /dev/null -> HTTP/1.1 200 OK
That's pretty cool, we successfully made an HTTP request on the HTTPS single-port virtual server! But that's not the endgame, and is most certainly not a desired state. Let's take the final step with the iRule to capture any non-SSL connections and redirect them. Here's the iRule:
when CLIENTSSL_HANDSHAKE { set https_state 1 } when HTTP_REQUEST { if { ![info exists https_state] } { HTTP::redirect https://[HTTP::host][HTTP::uri] } }
This is optimized slightly from the original iRule from gasch that inspired this article. First, we look at the CLIENTSSL_HANDSHAKE event and set a variable to true to capture that this connection is indeed an SSL connection. Then in the HTTP_REQUEST event, if that variable doesn't exist, we know that the connection is not SSL and we take the redirect action. Simple and sleek! Now, with that iRule applied, let's test again:
### HTTP ### MY-MAC:~ rahm$ curl -v -s -k http://test.test.local:443 1> /dev/null * Rebuilt URL to: http://test.test.local:443/ * Trying 192.168.102.50... * TCP_NODELAY set * Connected to test.test.local (192.168.102.50) port 443 (#0) > GET / HTTP/1.1 > Host: test.test.local:443 > User-Agent: curl/7.54.0 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 302 Found < Location: https://test.test.local:443/ < Server: BigIP * HTTP/1.0 connection set to keep alive! < Connection: Keep-Alive < Content-Length: 0 < * Connection #0 to host test.test.local left intact
And there it is! You can see the HTTP request is accepted as previously, but instead of the 200 OK status, you get the 302 Found redirect. Sometimes things like this are solutions looking for problems, but conserving ports, IP space, configuration objects, etc, could all be factors.