Set up HTTPS for RAILS

Introduction

To set up HTTPS for a Rails app that is served up by Nginx is quite easy, but there are a couple of little things that need to be configured. This writeup is intended to document these small yet crucial steps.

This document makes the following assumptions:

  • SSL has been obtained and placed in the Nginx configuration folders.
  • Most of the SSL flags in Nginx conf file have been set.

If that’s not true, google online to find out how to do that. But here are a couple of tricky things on both the Nginx and Rails configurations that need to be highlighted.

On the Nginx side…

Requesting a SSL certificate 

You need to follow the instructions from this web page: https://calnetweb.berkeley.edu/calnet-technologists/calnet-incommon-sectigo-certificate-service

Installing the Certificate

Once your request is approved, IST will send you an email with all the certificate download link. The only file you need to worry about is the Certificate (w/ chain), PEM encoded

After you have downloaded that file, follow the instructions on this web page: https://calnetweb.berkeley.edu/calnet-technologists/incommon-comodo-certificate-service/extended-incommon-ssl-certificate-example.

The most important instructions are here:

To examine the individual certificates provided (and to simplify the later creation of a new Root/Intermediate bundle file), open as a source file the test_training_berkeley_edu.cer certificate bundle file using your favorite text editor. Now save the text components corresponding to each certificate into three new files named cert1.cer (topmost certificate in the source file), cert2.cer (middle certificate in the source file), and cert3.cer (bottom certificate in the source file).

Note: cert2.cer – the middle section – should have two certificates

These are steps to get all the certificates ready (assuming that you are in a temporary working directory like ~/ssl/nginx/:

cp test_training_berkeley_edu cert1.cer # keep top cert
cp test_training_berkeley_edu cert2.cer # keep middle 2 certs
cp test_training_berkeley_edu cert3.cer # keep bottom cert
mv cert3.cer test_training_berkeley_edu_cert.cer
mv test_training_berkeley_edu_cert.cer /etc/ssl/nginx/
cat cert2.cer cert1.cer > CA-bundle.cer 
mv CA-bundle.cer /etc/ssl/nginx/
mv test.training.berkeley.edu.key /etc/ssl/private/
sudo chmod 0400 /etc/ssl/private/test.training.berkeley.edu.key
mv ssl_passwords.txt /etc/ssl/private/
sudo chmod 0400 /etc/ssl/private/ssl_passwords.txt

Once we have all the certificates and keys put in the right places, then we need to update the Nginx server configuration like this:

server {
   listen *:80;
   server_name test.training.berkeley.edu;
   return 301 https://$host$request_uri;
 }

server {
   server_name test.training.berkeley.edu;
   listen *:443;
   ssl on;
   ssl_prefer_server_ciphers on;
   ssl_certificate /etc/ssl/nginx/test_training_berkeley_edu_cert.cer;
   ssl_certificate_key /etc/ssl/private/test.training.berkeley.edu.key;
   ssl_password_file /etc/ssl/private/ssl_passwords.txt;
   # send intermediate certificate during new sessions
   ssl_trusted_certificate /etc/ssl/nginx/CA-bundle.cer;
   ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

   ssl_ciphers 'kEECDH+ECDSA+AES128 kEECDH+ECDSA+AES256 kEECDH+AES128 kEECDH+AES256 kEDH+AES128 kEDH+AES256 DES-CBC3-SHA +SHA !aNULL !eNULL !LOW !kECDH !DSS !MD5 !RC4 !E
 XP !PSK !SRP !CAMELLIA !SEED';
   ssl_dhparam /etc/ssl/nginx/dhparam2048.pem;
   ssl_stapling on;
   ssl_session_cache builtin:1000 shared:SSL:10m;

.... the rest of the server configuration

Note: dhparam2048.pem may not exists, so you need to generate it like this:

openssl dhparam -out /etc/ssl/nginx/dhparam2048.pem 2048

Additional Configuration for Nginx

We need to set proxy_set_header X-Forwarded-Proto https; in the @app location in the Nginx configuration. This is what ggkbase_production.conf looks like in that portion:

location @app {
  proxy_pass http://ggkbase_production_puma_server;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto https; # IF NOT SET, TOO MANY REDIRECT ERROR
  proxy_set_header Host $http_host;
  proxy_set_header X-Sendfile-Type X-Accel-Redirect;
  proxy_set_header X-Accel-Mapping /work/railsapps/ggkbase_production/=/__send_file_accel/;
  proxy_read_timeout 3000;
  proxy_redirect off;
}

On the Rails side…

In the app/config/environments/production.rb configuration file, set config.force_ssl = true

After that restart Nginx and Puma, and the site is all good to go with HTTPS.