Create and install SSL certificates with ease – a Capistrano recipe

How to generate the Private Key, what's the correct chaining order, or how to create a PEM Certificate? We all know it and we hate it: Installing or renewing the SSL certificates for our application.

And after you finally figured it out, you'll unlearn the progress till you need it again. So I wrote a small Capistrano recipe to help you with this annoying job.

(Find the revised Capistrano 3 version here.)

What it does

The recipe ships with three common tasks:

Generate the Private Key and CSR (Certificate Signature Request) files. You need them to purchase your SSL certificate.

Create a Chained Certificate file. No need to remember the right order of the certificates anymore. It also creates a PEM Certificate file.

Upload the certificates to your server and set the correct permissions.

To keep things simple there are a few conventions:

The certificate folder is YOUR_APP/config/certs/

The filename for the Intermediate Certificate file is #{fetch(:ssl_certificate_company)}-intermediate.crt . So rapidssl-intermediate.crt when you set the ssl_certificate_company variable to rapidssl .

. So when you set the variable to . The filename for the Domain Certificate file is #{fetch(:ssl_domain)}-#{fetch(:ssl_certificate_company)}.crt . So exceptiontrap.com-rapidssl.crt when you set the ssl_domain to exceptiontrap.com .

Installation & Configuration

First, grab the recipe.

# config/recipes/ssl.rb namespace :ssl do desc "Install (upload) Certificates" task :install do upload_certificate domain_certificate_key_filename upload_certificate chained_certificate_filename upload_certificate domain_certificate_pem_filename end desc "Generate the Chained Certificate and the PEM Certificate files. To use them on the webserver" task :chain_certificates do # Chained CRT Certificate generate_chained_certificate ( domain_certificate_filename , intermediate_certificate_filename , chained_certificate_filename ) # PEM Certificate generate_chained_certificate ( domain_certificate_key_filename , chained_certificate_filename , domain_certificate_pem_filename ) end desc "Generate Private Key and CSR files" task :generate_private_key_and_csr do generate_private_key_and_csr_files end end # Upload a certificate to the remote server def upload_certificate ( filename ) destination = "/var/certs/ #{ filename } " upload ( certificate_file_for ( filename ), destination ) run " #{ sudo } chown root #{ destination } " run " #{ sudo } chmod 600 #{ destination } " end # Chains the certificates to a new file def generate_chained_certificate ( certificate1 , certificate2 , chained_certificate ) run_locally "sed -i '' -e '$a \\ ' #{ certificate_file_for ( certificate1 ) } " # Add newline to file unless there is one run_locally "cat #{ certificate_file_for ( certificate1 ) } #{ certificate_file_for ( certificate2 ) } > #{ certificate_file_for ( chained_certificate ) } " end def generate_private_key_and_csr_files run_locally "openssl req -nodes -newkey rsa:2048 -sha256 -keyout #{ certificate_file_for ( domain_certificate_key_filename ) } -out #{ certificate_file_for ( domain_certificate_csr_filename ) } " end # Get the full path of a certificate file def certificate_file_for ( filename ) File . expand_path ( "config/certs/ #{ filename } " ) end # Filenames of the different certificates def chained_certificate_filename " #{ fetch ( :ssl_domain ) } - #{ fetch ( :ssl_certificate_company ) } -chain.crt" end def domain_certificate_filename " #{ fetch ( :ssl_domain ) } - #{ fetch ( :ssl_certificate_company ) } .crt" end def domain_certificate_pem_filename " #{ fetch ( :ssl_domain ) } - #{ fetch ( :ssl_certificate_company ) } .pem" end def domain_certificate_csr_filename " #{ fetch ( :ssl_domain ) } - #{ fetch ( :ssl_certificate_company ) } .csr" end def domain_certificate_key_filename " #{ fetch ( :ssl_domain ) } - #{ fetch ( :ssl_certificate_company ) } .key" end def intermediate_certificate_filename " #{ fetch ( :ssl_certificate_company ) } -intermediate.crt" end

Then load and configure it.

# config/deploy.rb set :ssl_domain , "exceptiontrap.com" # Your domain goes here set :ssl_certificate_company , "rapidssl" # Name of your CA company load "config/recipes/ssl"

Please Note: We shouldn't store private keys in source control. So add config/certs/ to your .gitignore file.

How to use it

cap ssl:generate_private_key_and_csr to generate the Private Key and CSR. Order your certificate. Copy your Domain Certificate to e.g. exceptiontrap.com-rapidssl.crt Copy the CAs Intermediate Certificate to e.g. rapidssl-intermediate.crt cap ssl:chain_certificates to create the Chained Certificate and PEM Certificate. Nginx needs the Chained Certificate file, Apache doesn't. cap ssl:install to upload the certificate files to your web server.

Let me know

Did this work for you or do you use another approach? Just ping me at @tbuehl

This is a Nuts & Bolts Series post – join the mailing list below to get more tips & tricks.

← Back to Overview