It’s always interesting to see how one can replicate the functionality of a known development environment ( the world of Java Web Apps in my case ) inside of a new environment, Rails. One of the exercises I was trying to mimic in Rails was having some traffic go over HTTP and some go over HTTPS.

There is a strong movement to have all traffic go over HTTPS but since it is a bit more costly ( ex: having SSL certs for a CDN so your images don’t pitch “unsecure” errors ) it is still nice to segregate the secure from the non secure parts of your application.

Below is a step-by-step guide to getting this configuration up and running. Pardon some of the brevity but some basic amount of Rails, Passenger and Apache knowledge is required.

Step-by-step guide

Update your Gemfile for your test app with Passenger server

1 2 3 4 group :development do # passenger server instead of webbrick gem "passenger", "~> 4.0.20" end

Install the Passenger gem

1 sudo gem install passenger

Install the Apache mods so Passenger can be nicely integrated. More information can be found here “Working with the Apache configuration file”

1 rvmsudo passenger-install-apache2-module

Add the VirtualHost entries pointing to your app. This way we can reference things by quasi real url. Modify your host entires ( /etc/hosts or %SystemRoot%\system32\drivers\etc\hosts ) to include the following

1 127.0.0.1 test-domain.com www.test-domain.com

Create self signed certs for connecting over HTTPS. For this you will need to make sure openssl is installed This is easy to to in linux and OSX comes with it preinstalled Linux ( ubuntu )

1 sudo apt-get install openssl

Linux ( redhat / centos )

1 sudo yum install openssl

OSX using homebrew ( a default should be installed )

1 sudo brew install openssl

Create directory for self signed certs

1 2 3 4 5 cd /projects mkdir ssl cd ssl mkdir test-domain cd test-domain

Generate a host key

1 sudo ssh-keygen -f test-domain.key

This should generate the following similar output ( the image will vary based on the passphrase )

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Generating public/private rsa key pair. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in test-domain.key. Your public key has been saved in test-domain.key.pub. The key fingerprint is: a3:cd:70:7a:f5:1d:7b:44:ea:b1:b8:4b:19:ac:0e:5b root@user.local The key's randomart image is: +--[ RSA 2048]----+ | | | | | .| | . o | | . S . o + .| | B o o * * | | o = E = = .| | . = . . . | | . . o. | +-----------------+

Generate certificate request file

1 sudo openssl req -new -key test-domain.key -out test-domain_request.csr

Create the SSL certificate

1 sudo openssl x509 -req -days 365 -in test-domain_request.csr -signkey test-domain.key -out test-domain.crt

Create a nopass key version so Apache does not prompt you for a password every time you restart. Or if you want that functionality just use the “test-domain.key” file instead.

1 sudo openssl rsa -in test-domain.key -out test-domain.nopass.key

Configure Apache Modify the apache httpd.conf file or create a new *.conf file just for these settings Add the Passenger load statements to your httpd.conf file

Obviously you will need to modify the paths that match your system

1 2 3 4 # TEST-DOMAIN CONFIG LoadModule passenger_module /Users/[useraccount]/.rvm/gems/ruby-2.0.0-p247/gems/passenger-4.0.20/buildout/apache2/mod_passenger.so PassengerRoot /Users/[useraccount]/.rvm/gems/ruby-2.0.0-p247/gems/passenger-4.0.20 PassengerDefaultRuby /Users/[useraccount]/.rvm/wrappers/ruby-2.0.0-p247/ruby

Then load the required modules for SSL

1 LoadModule ssl_module libexec/apache2/mod_ssl.so

Then create the VirtualHost entries for both port 80 and 443

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 # TEST-DOMAIN CONFIG NameVirtualHost *:80 NameVirtualHost *:443 # handle Apache connection to Passenger <VirtualHost *:80> # define server details ServerName test-domain.com ServerAlias www.test-domain.com # be sure to point to 'public'! DocumentRoot /projects/test-domain/public # run in dev mode RackEnv development # some passenger config details PassengerMinInstances 1 PassengerPreStart http://test-domain.com/ PassengerHighPerformance on <Directory "/projects/test-domain/public"> Options -MultiViews Allow from all </Directory> </VirtualHost> # Provide an HTTPS entry point as well but one that will not spin up a second rails instance # but rather redirect traffic accordingly <VirtualHost _default_:443> # to run in dev mode RailsEnv development # Be sure to point to 'public'! DocumentRoot /projects/test-domain/public # define server details ServerName test-domain ServerAlias www.test-domain.com # ssl details SSLEngine on SSLProtocol all -SSLv2 SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL SSLCertificateFile /projects/ssl/test-domain/test-domain.crt SSLCertificateKeyFile /projects/ssl/test-domain/test-domain.nopass.key # rails needs the header for its own processing RequestHeader set X_FORWARDED_PROTO 'https' # this is just passing a proxy to a localhost server ProxyRequests Off ProxyPreserveHost On <Proxy *> Order deny,allow Allow from all </Proxy> ProxyPass / http://localhost/ ProxyPassReverse / http://localhost/ </VirtualHost>

Make sure to restart Apache after these changes.

That’s it. You should now be able to access your rails application over HTTP and HTTPS by going to http://test-domain.com or https://test-domain.com

You will get errors in your browser about the HTTPS connection being untrusted but that is because you are using a self signed cert. If you were to substitute a proper one this issue would go away.

Now that you have HTTPS running you can do things like integrate Devise into it so all your authentication is handled over a secure connection.

Here is a quick example of setting all Devise functionality to go over HTTPS

Make sure to include the Devise gem in your app

1 2 # authentication gem 'devise'

Now install it

1 rails generate devise:install

And finally generate a user model to go with it

1 rails generate devise MODEL

Once you have that done you can force Devise to HTTPS traffic by adding the following to your development.rb or .rb files

1 2 3 4 # for enforcing SSL on various user areas config.to_prepare { Devise::SessionsController.force_ssl } config.to_prepare { Devise::RegistrationsController.force_ssl } config.to_prepare { Devise::PasswordsController.force_ssl }

Also if you want to force a specific controller to go over SSL you can use the force_ssl method