We’re migrating some servers from our own colo to a managed infrastructure and one change we’re dealing with is the switch from passing through SSL to the web server vs. terminating it at an F5 Load Balancer. Many load balancers offer this capability and while the parameter below is F5-specific, this approach will work for most makes and models. In this setup, all connections, including those originally secured by SSL, reach the web server as plain HTTP. The advantage is reduced overhead on the web/application server for more throughput.
The question becomes, how do you tell if the connection was secure? For payments or other sensitive data requirements we might want to or need to require SSL. Typically in a ColdFusion application you would check the CGI variable “HTTPS” or “SERVER_PORT_SECURE”:
<cfif NOT cgi.server_port_secure>
// redirect to secure version of the URL
</cfif>
In the case of the F5 BIG IP, it sets a CGI parameter called “BIGIPSSL” that we can check in ColdFusion, so all is not lost:
<cfif NOT cgi.bigipssl>
// redirect to secure version of the URL
</cfif>
But there’s a rub (of course!) We also have DNS that points to each individual server for debugging purposes and we might want to access them securely. That means that we’ll be hitting the sites via SSL directly as well as proxied through the load balancer. What happens when we want to check a CGI parameter that doesn’t exist?
<cfif NOT cgi.server_port_secure
AND cgi.bigipssl NEQ true>
// redirect to secure version of the URL
</cfif>
Turn out, nothing! CF just returns a blank string when you check a CGI parameter that doesn’t exist so the above code will safely work for all possible scenarios: secure and insecure connections in front of and behind an F5 load balancer.
Bonus hack
If you wanted to only rely on one variable, you could add the following line (assuming mod_env is enabled in your Apache setup) to your HTTPS VirtualHost config:
SetEnv bigipssl true
This will cause any SSL connections that bypass the load balancer to also set the bigipssl value which can be checked in the CGI scope. This technique can be used for any number of helpful host-specific setup values.
brian said:
on December 29, 2010 at 7:29 pm
If you wanted to provide maximum type safety, I’d recommend using the following:
if (isBoolean(cgi.server_port_secure) AND cgi.server_port_secure) OR (isBoolean(cgi.bigipssl) AND cgi.bigipssl)
Kurt Bonnet said:
on December 30, 2010 at 11:08 am
Great post. Just wanted to post another technique I’ve used before for doing this. Much like your SetEnv in Apache, you can use the mod_headers module and set a request header and read it in from CF.
i.e.
Apache Config:
RequestHeader set isSsl 1
Check in CF (In my Application.cfm/cfc):
headers = getHttpRequestData().headers;
request.a.isSslRequest = 0;
if(structKeyExists(headers, “isSsl”)) {
request.a.isSslRequest = val(headers.isSsl);
}
…
if( NOT request.a.isSslRequest ) {
redirect to secure location…
}
I’ve also used this technique to help determine which actual OS/app instance installation my app is running within. In some of my apps I need to ensure scripts are only allowed to run on a particular OS/app installation so I’ll do something like:
RequestHeader set AppInstance “WS1″
And then the “WS1″ value/code corresponds to a field in my “app_servers” database table which has varying permissions/configuration values for each of my app server instances. This has made it easy to deal with a single code base that operates across multiple servers that are configured slightly different from one another and that have slight behavioral differences. Hope that makes sense.
Congrats on your recent racing win.
brian said:
on December 30, 2010 at 12:23 pm
@Kurt – thanks, that’s another great use of the Apache modules to provide assistance to CF. I have often struggled with how to have a cluster of identically configured (from a CF perspective) machines but also not run the same scheduled tasks on every box. Your header example above made me think that or the setenv approach might be a good way of doing it:
SetEnv SCHEDULED_TASKS_PERMITTED 1
The only problem with both the Env and the Header is that it can be spoofed by the user unless you delete it first using mod_rewrite, correct?
Kurt Bonnet said:
on December 30, 2010 at 10:06 pm
@Brian – I’m not sure about SetEnv, but according to the documentation for mod_headers if someone were to spoof the isSsl request header, and you specified a value for the isSsl header (that was spoofed) in your Apache config using:
RequestHeader set isSsl 1
the “set” command will replace any previously existing header with that name (isSsl) and apply the new value to it (from the config file), thus over-riding any spoofed values.
I haven’t tested this out explicitly, but I’m pretty sure it works as stated.
Apache mod_headers docs:
http://httpd.apache.org/docs/2.2/mod/mod_headers.html#requestheader