miloot.com blog

site news and other musings.

How to Setup Apache With Passanger + HTTPS and Rails

| Comments

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class HomeController < ApplicationController
  # this specifies that the test_ssl controller will be the only one using SSL
  force_ssl :only => [:test_ssl]
  
  # index will be served over HTTP
  def index
  
  end

  # test_ssl will be served over HTTPS
  def test_ssl

  end
end

Comments