Updating Amazon RDS SSL/TLS Certificates for Laravel application on CentOS

I decided not to wait until March 5, 2020 and test these steps within a development environment before implementing them in production. Detailed instructions to start testing and updating RDS database instances are available on Rotating Your SSL/TLS Certificate page. Instructions are very clear except the following warning.

Before you update your DB instances to use the new CA certificate, make sure that you update your clients or applications connecting to your RDS databases.

Also there is a warning for step 7.

Important

When you schedule this operation, make sure that you have updated your client-side trust store beforehand.

In one of my projects I am running PHP application using Laravel framework on CentOS 7. In the development and production environments switching CA from rds-ca-2015 to rds-ca-2019 without updating the client-side was successful. I started researching why.

Determine if application is using database connection over SSL

First I checked whether application continues to use secure connection over SSL after switching to the new certificate.

PostgreSQL:


=# SHOW ssl;
 ssl 
-----
 on
(1 row)

MySQL:


mysql> SHOW STATUS LIKE 'Ssl_version';
+---------------+---------+
| Variable_name | Value   |
+---------------+---------+
| Ssl_version   | TLSv1.1 |
+---------------+---------+

In case with Laravel these commands can be run from Tinker shell using DB::select() function, for example: DB::select("SHOW ssl") or DB::select("SHOW STATUS LIKE 'Ssl_version'").

Why client-side update was not needed

After reading MySQL/PostgreSQL documentation and some answers on StackOverflow I realized that the reason why no update was necessary is because the application was not verifying validity of the certificate installed on the database. However the application was able to establish secure connection with the database after updating the CA certificate.

With both MySQL and PostgreSQL there is such thing as SSL mode. In MySQL it is defined by –ssl-mode connection option. In PostgreSQL it is sslmode connection option. In both systems, if SSL mode is not defined it defaults to similar setting (PREFERRED in MySQL and prefer in PostgreSQL) which means that secure connection will be established if possible but validity of the server CA certificate will not be checked.

When client-side update is needed

If you have SSL mode set to VERIFY_CA or VERIFY_IDENTITY with MySQL or verify-ca or verify-full with PostgreSQL, you would need to do provide updated Amazon CA certificate as a connection option.

Since documentation mentions updating client-side trust store, I initially thought that the solution would be adding the certificate to the list of the trusted certificates in the OS.

Update client-side trust store

On CentOS 7 I tried the following commands to update client-side trust store.


sudo curl https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem --output /etc/pki/ca-trust/source/anchors/rds-combined-ca-bundle.pem
sudo update-ca-trust extract

But in case with PostgreSQL database still requires root certificate to be provided as a connection option.

Use CA certificate as a connection option

Laravel

Download the rds-combined-ca-bundle.pem file from https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem for MySQL or rds-ca-2019-root.pem file from https://s3.amazonaws.com/rds-downloads/rds-ca-2019-root.pem for PostgreSQL and place it in a new or existing sub-folder in your application folder, for example /storage.

Amazon’s documentation recommends to use both the intermediate and root certificates rds-combined-ca-bundle.pem with MySQL (source) but only root certificate rds-ca-2019-root.pem with PostgreSQL (source). I suspect that using rds-ca-2019-root.pem should be enough for both MySQL and PostgreSQL but it may depend on other factors.

Then use downloaded certificate in your app/database.php configuration file.

MySQL:


    'connections' => [
        'mysql' => [
            /* ... skipped ... */
            'options' => array(    
                PDO::MYSQL_ATTR_SSL_CA => storage_path() . '/rds-combined-ca-bundle.pem'
            ),
        ],
    ],

PostgreSQL:


    'connections' => [
        'pgsql' => [
            /* ... skipped ... */
            'sslmode' => 'verify-ca', // Use 'require', 'verify-ca', or 'verify-full'
            'sslrootcert' => storage_path() . '/rds-ca-2019-root.pem',
        ],
    ],

You May Also Like

Leave a Reply

You may use simple HTML to add links or lists to your comment. Also use <pre><code class="language-*">...</code></pre> to mark up code snippets. We support language-js, language-markup and language-css for comments.
(Optional)

This site uses Akismet to reduce spam. Learn how your comment data is processed.