Insight | Jun 10, 2015
How to encrypt database connection in Laravel on Heroku with ClearDB without putting SSL certifications or keys in source control
By Brandon Barnes
The Problem
We needed to have 100% SSL communication for a Laravel app on Heroku that uses a ClearDB MySQL database service. Laravel uses PDO which requires a physical path to the certs and keys. But we don't want these checked into source control as they contain sensitive information.
The Solution
We download the certification files after deployment from AWS S3 or similar service. To accomplish this we add a composer hook script that will call a custom Laravel artisan command we create.
Steps:
Get a copy of necessary .pem files
You will need to acquire these 3 files:
- Certificate authority (CA) certificate from database server, named
ca-cert.pem
- Client certificate, named
client-cert.pem
- Client private key, named
client-key.pem
You will probably need to remove the password on the private key because of the MySQL configuration used on Heroku. This can be done by running:
openssl rsa -in client-key.pem -out client-key-no-password.pem
Then delete client-key.pem
and rename client-key-no-password.pem
to client-key.pem
Decide where files should be downloaded
I decided to create a folder under the Laravel storage folder as app/storage/app/certs
.
Add Laravel custom artisan command
Under the laravel folder, run:
php artisan command:make CopyCerts --command=deploy:copy-certs
You'll find CopyCerts.php under app/commands/CopyCerts.php
. Open this up and edit the fire() function to download the 3 files into the app/storage/app/certs
folder.
Then, make the command available by adding it to app/start/artisan.php:
Artisan::add(new CopyCerts());
Add composer.json post install command
Update the composer.json to add a post-install-cmd
script entry to call this new artisan command:
"scripts": { ... "post-install-cmd": [ ... "php artisan deploy:copy-certs" ... ], ... },
Update database.php
Next you will update the
$cert_base = realpath(dirname(__FILE__)."/../storage/app/certs");
Since the path needs to be absolute, we can derive it from the database.php file itself. Assuming you have been using the same folder structure, you would put this at the top of your database.php:
$cert_base = realpath(dirname(__FILE__)."/../storage/app/certs");
Then, under your connections, edit your connection to add PDO options to point at the 3 three SSL files. It would looks similar to this:
'your_connection' => array( 'driver' => 'mysql', 'host' => $host, 'database' => $database, 'username' => $username, 'password' => $password, 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', 'options' => array( PDO::MYSQL_ATTR_SSL_KEY => $cert_base . '/client-key.pem', PDO::MYSQL_ATTR_SSL_CERT => $cert_base . '/client-cert.pem', PDO::MYSQL_ATTR_SSL_CA => $cert_base . '/ca-cert.pem' ), ),
Wrapping up
At this point, you could run php artisan deploy:copy-certs or composer install manually from your local environment and it should connect to the database if all the credentials are correct. If all goes well, check it in, deploy it on heroku, and relish in the satisfaction of having a more secure app!
Troubleshooting tips
- PHP 5.6 currently has a bug with SSL. Try using 5.5 instead if you run into issues. You can define this in your composer.json. More info: https://devcenter.heroku.com/articles/php-support#selecting-a-runtime-php
- Make sure folder
app/storage/app/certs
folder has the correct permissions.
Drop us a line
Have a project in mind?
Contacting Third and Grove may cause awesomeness. Side effects include a website too good to ignore. Proceed at your own risk.