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
. Sorapidssl-intermediate.crt
when you set thessl_certificate_company
variable torapidssl
. - The filename for the Domain Certificate file is
#{fetch(:ssl_domain)}-#{fetch(:ssl_certificate_company)}.crt
. Soexceptiontrap.com-rapidssl.crt
when you set thessl_domain
toexceptiontrap.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.