3 min read

Why Pound is awesome in front of Varnish

Why Pound is awesome in front of Varnish

We all know Varnish is awesome. I went as far as presenting a topic on Varnish then writing about it. This is a known fact.

However, what happens to all that caching goodness when you want to run your entire site over SSL? Out of the box, Varnish doesn't support it. While I've heard some mention that not supporting SSL is an oversight, there exists some very sound reasoning for why not.

So how do people terminate SSL?

What is Pound?

Without copying exactly what's on the Pound documentation, or the Wikipedia entry about Pound, it's essentially a reverse proxy, SSL terminator and load balancer but NOT a webserver. It's small, easy enough to install and has minimal configuration. Stunnel is similarly simple, but since I have quite extensive experience using Stunnel, I decided to learn something new.

On my load balancing servers, Pound listens on port 443 and Varnish listens on port 80. When traffic comes in on port 443, it hits Pound, gets decrypted using my server certificate and then gets passed to Varnish on port 80. By putting all traffic through Varnish, I'm able to take advantage of its caching ability for both HTTP and HTTPS traffic.

It's almost, that simple. I had to make some minor changes to my VCL to receive and cache mixed mode traffic. Prior to these changes, I would sometimes deliver resources using the HTTP schema to pages delivered over HTTPS. This had the understandable effect of causing my browser to complain about insecure resources.

Getting Varnish and Pound to play nicely

Realising that we need to handle HTTP/HTTPS traffic differently in Varnish, even though it all comes in on port 80, I decided to use a separate cache hash key for each. Varnish uses hashes of the URI as a key to store and look up data by. My VCL implements the vcl_hash subroutine to detect HTTPS traffic and alter the hash key. We add a header in Pound to tell Varnish that the traffic came in over SSL and then watch the magic happen.

pound.cfg

ListenHTTPS
  Address 0.0.0.0
  Port 443
  HeadRemove "X-Forwarded-Proto"
  AddHeader "X-Forwarded-Proto: https"
  Cert "/etc/ssl/certs/adammalone.net.pem"
End

Service
  HeadRequire "Host:.*adammalone.net.*"
    Backend Address 127.0.0.1
    Port 80
  End
End

default.vcl

sub vcl_hash {
  hash_data(req.url);
  if (req.http.host) {
    hash_data(req.http.host);
  } else {
    hash_data(server.ip);
  }
  # Use special internal SSL hash for https content
  # X-Forwarded-Proto is set to https by Pound
  if (req.http.X-Forwarded-Proto ~ "https") {
    hash_data(req.http.X-Forwarded-Proto);
  }
  return (hash);
}

The hash_data function allows us to add further information to the hash. By adding 'https' to the host and uri information, we're altering the hash in such a way that it is different from just the host + uri that an http request would use.

I've also attached a downloadable copy of my full Pound config and the puppet manifest that generates it for people who are interested in replicating this functionality. I'm using my Pound puppet class located at typhonius/puppet-pound, a fork of mrintegrity/puppet-pound.

Drupal configuration

The final thing to do is to inform Drupal it needs to be in SSL mixed mode and to enter a small snippet in my settings.php so it can be turned on or off based on the incoming request. If Varnish is running on the same server as your Drupal installation, you'll need to replace www.xxx.yyy.zzz with 127.0.0.1. Otherwise it'll be the IP of your load balancing server.

// Varnish Settings
$conf['reverse_proxy'] = TRUE;
$conf['reverse_proxy_addresses'] = array('www.xxx.yyy.zzz'); $conf['reverse_proxy_header'] = 'HTTP_X_FORWARDED_FOR'; $conf['page_cache_invoke_hooks'] = FALSE;
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
  $_SERVER['HTTPS'] = 'on';
}

This is how I allow SSL through Varnish, if you do it differently, add a comment!