Getting HTTPS going on your local POW instance using Nginx

August 26th, 2011 by jason.nah

Security

Security. We’ve all heard and read about it. But many of us forget to do something about it, until something nasty happens. In Oct 2010, the blogosphere erupted with the demonstration of Firesheep pointing out that one of the most basic things any web developer can do to improve their application security is to use HTTPS.

For a Rails developer, isn’t this just a matter of adding a gem to your Gemfile, and running bundler?

If only it were that simple.

For this post, I’ll go through some of the steps that I took to get HTTPS going on a Rails application running locally under POW. Hopefully, it’ll be of use to somebody.

What’s POW?

So you have a rails application, which you typically fire up using:

rails s
open http://localhost:3000

POW

What happens if you have multiple rails applications? Or for that matter sinatra, merb or any other Rack-based application? Do you fire them all up with separate ports?

Here’s where POW comes in really handy. POW! is a zero-config rack server for Mac OS X. It’s dead easy to install and get running, and makes it possible to host multiple Ruby Rack applications without each Rack application having to run in a separate web server on a separate port.

POWer me up!

To install POW, run the following in the terminal:

echo "export POW_DOMAINS=dev,local" > ~/.powconfig
curl get.pow.cx | sh
cd ~/.pow
ln -s /path/to/myapp myapp

open http://myapp.dev

Here’s a trap that I found. The documentation for POW typically tell you to download and install POW with one command, namely; curl piped to sh. However, in my experience, this did not (for some reason) configure OS X’s system resolver properly so requests were not being forwarded to POW. I had to specify the Top Level Domains (TLD) in a config file and reinstall POW. So, save yourself the trouble and set it first with the POW_DOMAINS variable in the .powconfig file before you install.

Keep the POWer on

When you start using POW, you may notice that some refreshes take a really long time to come back. This happens because POW tends to spin down the rails instance after a timeout, making it painful when you refresh your page as this causes POW to reinitialise the rails application from scratch (and we all know how long rails 3 takes to initialise). To fix this, edit your ~/.powconfig to include the following:

export POW_TIMEOUT=30000
export POW_WORKERS=3

The POW_TIMEOUT sets POW to keep the process running for a long time, which is typically what you want, given that in development, rails will automatically reload (mostly) the altered files.

So now, you can throw away the rails s command as POW will fire up the instance when you need it.

How Do I Bolt On HTTPS To POW?

Nginx Logo
AFAIK – you can’t. I looked, and I couldn’t find any way to install a certificate to get HTTPS going. So, the easiest way to bolt on HTTPS is to setup Nginx as a reverse proxy. And although it sounds hard, it isn’t.

Nginx is fast HTTP server. And like any capable HTTP server, it is able to accept HTTPS connections and can be configured to proxy requests through to POW.

Bolt In Nginx

To install Nginx on OS X, you have two choices:

  1. Install it via Brew
  2. Install it the old fashion way

Brew me an Nginx

The easy way is to use Brew. Just run

brew install nginx

Done.

I Don’t Have Brew

If you’re like me, and you’re already using MacPorts and haven’t made the switch to brew, then you have to do things the hard way. I followed instructions on Kevin Worthington’s Blog but specifically, I had to do the following:

  1. Install XCode(I already had XCode installed previously so I didn’t have to do anything). Installing XCode is a matter of downloading it and installing it. In fact, if you’re running Snow Leopard or Lion, you can download it and install it directly via the Mac App Store.
  2. Install PCREThis required me to download the source and install it manually. The steps I took were as follows:
      sudo mkdir -p /usr/local/src
      cd /usr/local/src
      sudo wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.12.tar.gz
      tar xvfz pcre-8.12.tar.gz
      cd pcre-8.12
      ./configure --prefix=/usr/local
      make
      sudo make install
      
  3. Install NginxInstalling Nginx is a matter of downloading the source, building and installing it. I followed the following steps:
      cd /usr/local/src
      sudo wget http://nginx.org/download/nginx-1.0.4.tar.gz
      tar xvfz nginx-1.0.4.tar.gz
      cd nginx-1.0.4
      ./configure --prefix=/usr/local --with-http_ssl_module
      make
      sudo make install
      

Intermission. Time For Certificates.

Ok. Time for a break. Time to break out openssl to generate some certificates.

HTTPS sits on top of Secure Sockets Layer (SSL). For this to work, the encrypted communication requires certificates to be installed at the web server end. In production, you would purchase the certificate from a Certificate Authority (CA) like Verisign or Digicert. The CA’s role is to issue you with a certificate that verifies the identity of the entity the certificate is for (e.g. the domain name owner).

But for development, you don’t need a certificate from a CA. All you need is openssl to generate a certificate which is self-signed. There are countless articles on the web describing this process in detail. www.akadia.com’s article seems to be pretty self explanatory.

Once you’re done generating the certificates, you will need to either copy or symlink them to the appropriate Nginx location so that Nginx can find them. To do this, I did the following:

cd /usr/local/conf
mkdir ssl
cp server.crt ./ssl
cp server.key ./ssl

I Have Nginx. Now to configure it.

If you followed my install instructions above, then Nginx’s conf file is located in

/usr/local/conf/nginx.conf

You will need to edit the nginx.conf file to configure Nginx to:

  1. Use the certificates you generated
  2. Listen to port 443 to handle the HTTPS protocol

Here’s what I added to the nginx.conf file to configure Nginx as a reverse proxy.

server {
    listen       443 ssl;
    server_name  myapp.dev;

    ssl                  on;
    ssl_certificate      ssl/server.crt;
    ssl_certificate_key  ssl/server.key;

    keepalive_timeout 60;

    ssl_session_timeout  5m;

    ssl_protocols  SSLv2 SSLv3 TLSv1;
    ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
    ssl_prefer_server_ciphers   on;

    location / {
        proxy_pass http://127.0.0.1;
        ### force timeouts if one of backend is died ##
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;

        ### Set headers
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        ### Most PHP, Python, Rails, Java Apps can use this header
        proxy_set_header X-Forwarded-Proto https;

        ### By default we don't want to redirect it
        proxy_redirect off;
    }
}

The things that you may need to alter are:

  • server_name – enter the domain that matches what you would enter to hit POW
  • ssl_certificate – enter the location of your server.crt file. If you copied the certificates
    to /usr/local/conf/ssl, then enter ssl/server.crt since the
    nginx.conf resides in /usr/local/conf
  • ssl_certificate_key – enter the location of your server.key file. This should be similar
    to that for ssl_certificate

The rest of the configuration tells Nginx to proxy the request to localhost on port 80, which is where POW will
kick in and handle the request for you.

Configured!

Once you’re done, fire up Nginx using /usr/local/sbin/nginx and test it by navigating to https://myapp.dev.

“You gotta blog about this man…” cried Alan after I checked in. In acknowledgement, I nodded. Fast forward four weeks later, after CrowdHired has launched… I now have some breathing space to really blog about it. So hopefully this has been of help to you and saved you some time along the way.

  • Twitter
  • Digg
  • del.icio.us
  • Reddit
  • StumbleUpon
  • Posterous
  • DZone
  • Technorati
  • LinkedIn
  • Facebook
  • email
  • RSS

Tags: , , ,

2 Responses to “Getting HTTPS going on your local POW instance using Nginx”

  1. Stephen says:

    Indirectly, why POW over, say foreman (http://ddollar.github.com/foreman/)?

  2. jason.nah says:

    If you’re talking about https://github.com/ddollar/foreman then POW and foreman are two different things and serve two different purposes.

    Foreman is a gem to interpret a Procfile and start up the various processes. So, effectively it is a way to easily manage multiple processes for your application. In fact @rbates asked the following question on twitter recently:

    "I have a Rails app where I need to start about 5 servers/daemons to get it working in development. Any suggestions for making that easier?" ~ @rbates

    To which, he discovered foreman:
    " Thank you all who suggested Foreman, looks like that does what I need! http://blog.daviddollar.org/2011/05/06/introducing-foreman.html " ~ @rbates

    And subsequently did a screencast on it.

    POW isn’t geared for this. POW’s real purpose is to make it super easy to host multiple Rack applications on your development machine. It hooks into OS X’s DNS resolver to route specific domain names (eg. myapp.dev) to your app. POW does nothing to help you fire up background workers or your own custom daemons/processes/dependencies that your application may require.

    Hope this helps clarify your question.

Leave a Reply