Tuesday, September 16, 2014

AWS RDS MySQL SSL

Following my previous post of SSL with ELB's and instances I'd like to write a quick post about SSL and RDS MySQL. This is worth another blog post because it behaves slightly differently than your normal apache http or tomcat servers. 

This is because the SSL is enforced at the user level and not at the server port level. Typically when you disable insecure traffic to a particular server you will disable the port that it is listening on for insecure traffic - such as port 80 for the apache web server. 

MySQL is different, you still connect through the default port (3306), or whatever port you have configured it to run, but the difference is the way you connect. In order to ensure secure communication you must first create a user which requires SSL to connect:

First connect to the RDS instance as the root user:
mysql -h myinstance.123456789012.us-east-1.rds.amazonaws.com -P 3306 -u root -p
You can then create users in the specific way:
GRANT SELECT, INSERT, UPDATE, DELETE on db_name.* to 'encrypted_user'@'%' IDENTIFIED BY 'suprsecret' REQUIRE SSL;FLUSH PRIVILEGES;
You can now test this with the mysql client that you used earlier to connect to the database earlier:
mysql -h myinstance.123456789012.us-east-1.rds.amazonaws.com -u encrypted_user -p --ssl_ca=mysql-ssl-ca-cert.pem --ssl-verify-server-cert
The above should prompt you for the password that you set up above, and allow you to connect to the database securely. 

This is the database server end complete. The next part is to configure your application to use SSL to connect to the database securely. There are a number of ways in which you can complete this, for this example I'm going to configure a java application.

For this example we're going to configure the JDK to allow the secure connection. There are a number of options here, including application container specific configurations but this way has the advantage that all java applications (container or otherwise) will be able to connect.

First get the RDS MySQL server certificate from AWS:
wget https://rds.amazonaws.com/doc/mysql-ssl-ca-cert.pem
Now import this into the java trust store (replace $JAVA_HOME as necessary):
$JAVA_HOME/bin/keytool -importcert -alias rds -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeme -file ./auspost-root-ca.cer -noprompt
Connecting to the MySQL database is the same as before however you now specify three new options in your connection string which connect using SSL:
jdbc:mysql://myinstance.123456789012.us-east-1.rds.amazonaws.com:3306?useSSL=true&verifyServerCertificate=true&requireSSL=true
For brevity I will not include the rest of the code that you use in java in order to connect.

4 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. I am able to connect using the mySQL client but using the jdbc driver I get the following error
    Could you please help with this this error:
    sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target.


    On seeing the SSL logs, I found this error as well:
    ***
    %% Invalidated: [Session-1, TLS_RSA_WITH_AES_256_CBC_SHA]
    main, SEND TLSv1 ALERT: fatal, description = certificate_unknown
    main, WRITE: TLSv1 Alert, length = 2

    ReplyDelete
    Replies
    1. Hey Ravi,

      Sounds like you don't have the RDS SSL certificate in your java truststore. My above example assumes this, probably incorrectly. You can get the ssl root certificate from amazon here:

      https://s3.amazonaws.com/rds-downloads/rds-ca-2015-root.pem

      Which you can then import into your truststore. The above example uses the default java trust store which is in $JAVA_HOME/jre/lib/security/cacerts

      Hope this helps.

      Delete
  3. This comment has been removed by the author.

    ReplyDelete