Showing posts with label ssl. Show all posts
Showing posts with label ssl. Show all posts

Tuesday, January 31, 2012

My twelve-factor app development environment (Unicorn and Pound)

I've been hugely influenced by the twelve-factor app manifesto written by Heroku. I really like building apps on Heroku and think it's pretty great for prototyping things, because it totally abstracts the need to think about devops in the early stages of a project. I don't think Heroku is a panacea (I don't have experience running something large on it), but it's great for getting something going quickly.

That manifesto illustrates how you end up building your app if you launch it on Heroku. I've found the  principles greatly increase my productivity no matter what platforms I'm using. One thing that did take me a little while to figure out: most of my apps are SSL-only, and I always want to interact with them over SSL in my development environment because odd things can creep up in production if you never test SSL locally. (Most of the advice that Rails blogs give always point you towards shutting off SSL in development mode which seems crazy to me)

At first I couldn't quite figure out how to make SSL work while complying with factor 7, Port Binding. I was still using Phusion Passenger with SSL configured as I described in this very old article (which still gets a lot of traffic).

Now here's what I do. I run the Pound reverse proxy using this configuration:

# http://lvh.me
ListenHTTP
  Address 127.0.0.1
  Port    80

  Service
    BackEnd
      Address 127.0.0.1
      Port    8080
    End
  End
End

# https://lvh.me
ListenHTTPS
  Address 127.0.0.1
  Port    443
  Cert    "/usr/local/etc/pound.pem"
  AddHeader "X_FORWARDED_PROTO: https"

  Service
    BackEnd
      Address 127.0.0.1
      Port    8080
    End
  End
End

The /usr/local/etc/pound.pem file is a locally-generated, locally-signed SSL certificate.

When I want to view an SSL-only app in my local browser, I just start Unicorn (my app container of choice) which defaults to port 8080. Then I visit https://lvh.me which is helpfully set to the loopback address 127.0.01. That hits Pound, which terminates the SSL connection (after a warning about the self-signed certificate), and proxies the web request to Unicorn.

This is very similar to what happens when the app runs in production on Heroku's systems, except that they use a Procfile in the root directory of the app to configure Unicorn. Per factor 7, it allows Heroku to bind my Unicorn instance to any arbitrary port:

web: bundle exec unicorn -p $PORT -c config/unicorn.rb

Thursday, July 28, 2011

Getting SSL, Capybara, Rails 3, and Devise to work together

I've noticed a lot of Rails blog posts recommending that you only turn SSL on in production and don't try to use it in the test or development environments. This is a monumental mistake, a lesson I learned hard in the early days of OtherInbox: SSL enforcement has a lot of subtleties and corner cases that need to be exercised and tested on a routine basis. As a quick slightly embarrasing example, we had a bug at OIB where flash messages would get swallowed during redirects. It turned out we were accidentally redirecting people to an http:// page, where the flash would be available, but our SSL enforcement code redirected them quickly to https://, and that second redirect ate the flash message. Since we couldn't reproduce the bug on our own machines it was much harder to track down than you would think.

But I know why the bloggers recommend the naive approach though: getting SSL to work offline is a major pain! Right now I'm building a Rails app that enforces SSL on every page. It's all private data (the public-facing site is managed by a separate CMS) so I don't want any page to be accessible over http://. Below is a summary of the code changes I had to make to get this to work. I was mainly guided by this great Collective Idea blog post about Rails 3 SSL that I'd recommend you start with.

It's not hard to get SSL working in development mode if you use an app container like Phusion Passenger. I explained how to do that a few years ago with an Apache-Mongrel setup, but the basic ideas apply.

Update 1/31/2012: I wrote up an easy way to get SSL working with Unicorn and Pound instead of Passenger which is my new preferred way of working. Also check out the Devise wiki for more useful SSL info.

Wednesday, November 28, 2007

Testing Rails SSL Requirements on Your Development Machine

Update 1/31/2012: This advice is pretty old but should still basically work. I now prefer a more flexible solution using Unicorn and Pound.


I am building a Rails app that requires some portions of the site to use HTTPS, so naturally I'm using the SSL requirement plugin. The plugin works great, but if you're using Mongrel or WEBrick running out of script/server in your development environment, you now won't be able to talk to those parts of your site (since these servers do not include SSL encryption).

The solution is pretty easy, but it's not something I found written up elsewhere, so I thought I'd document it here. All you have to do is install your own local Apache server and have it proxy requests to the Mongrel or WEBrick instance, similar to how you would set up your production environment. For simplicity I didn't use a cluster of mongrels, or mod_balance, or anything like that, just a straight-through proxy. See "A Simple Single Mongrel Configuration" on the Mongrel site for details.

But there's a bit more you need to do in order to make things work with Rails and the SSL requirement plugin.  Below is a subset of my httpd.conf file; for clarity, I cut out all the default settings and just left what I added or changed.
Listen 80 # included in the default config
Listen 443 # Apache needs to know you want to accept connections over HTTPS
SSLCertificateFile /usr/local/apache/conf/newcert.pem
SSLCertificateKeyFile /usr/local/apache/conf/newkey.pem
# Below is optional, but was helpful to me in debugging this setup
CustomLog logs/ssl_request_log  "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
<VirtualHost *:80>ServerName localhost
 ServerAlias 127.0.0.1
 ProxyPass / http://localhost:3000/
 ProxyPassReverse / http://localhost:3000
 ProxyPreserveHost on</VirtualHost>
<VirtualHost *:443>SSLEngine On
ServerName localhost
ServerAlias 127.0.0.1
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000
ProxyPreserveHost on
RequestHeader set X_FORWARDED_PROTO 'https' # don't forget this line!
</VirtualHost>

<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
</IfModule>
What you're doing is setting up two different proxies through Apache to your Mongrel server; one via port 80, unencrypted, and one via port 443, encrypted with SSL.  If you don't include the X_FORWARDED_PROTO line in your 443 virtual host, Rails won't know that it's using SSL and the SSL requirement filter will fail.

The two SSLcertificate directives refer to the cryptographic data needed to encrypt the traffic. I just made some self-signed certificates (which are free).  There are a million tutorials out there; I used the one on Apple's site.  You'll need to pick a passphrase to protect your private key.  Pick something short (since this isn't the production site) because you'll need to enter it every time you want to reboot your server.

Start things up and you're ready to go!

wymanpark~ $ sudo httpd -k start
Apache/2.2.6 mod_ssl/2.2.6 (Pass Phrase Dialog)
Some of your private key files are encrypted for security reasons.
In order to read them you have to provide the pass phrases.

Server localhost:443 (RSA)
Enter pass phrase:

OK: Pass Phrase Dialog successful.
wymanpark~ $