Communications link failure MySQL JDBC with TLS
Ran into an interesting situation trying to configure a MySQL JDBC driver to connect over TLS (though the driver may call it SSL, TLS is the name for more recent versions of the protocol).
The error I was getting was pretty generic:
Communications link failure The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
With the relevant parts of the stacktrace, also being non helpful:
at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174) at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64) at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:835) at com.mysql.cj.jdbc.ConnectionImpl.(ConnectionImpl.java:455) at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:240) at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:199)
With a bit of googling you will find that there are tons of reasons why you might get this particular error message. Here are some of the most common ones:
- Firewall is blocking the connection (it wasn't)
- The MySQL server is not configured to allow remote connections (it did allow them)
- Using an outdated JDBC driver (it wasn't)
- Java trying to use IPv6, instead of IPv4 (it wasn't)
I had tested that I was able to connect from the app server to the MySQL server using the
mysql --host=10.1.2.3 --user=user --password --ssl --ssl-ca=./ca.pem
So my JDBC connection string looked something like this:
I was certain that my
trustCertificateKeyStoreUrl was valid because I had used the same file to successfully connect over TLS on another server.
If you look closely you can see I was trying to force the use of TLS 1.2 as the TLS protocol to avoid using older less secure versions of the TLS / SSL protocols with
enabledTLSProtocols=TLSv1.2. After some testing I found that when I removed
enabledTLSProtocols=TLSv1.2 from the JDBC connection string it worked. After I did this I wanted to see what protocol I was using, so I ran a query:
SHOW SESSION STATUS
This showed that the
Ssl_version my connection was using was
TLSv1.1. Next I queried to see what TLS protocols the server supports:
SHOW GLOBAL VARIABLES
This query shows that the
tls_version enabled for this particular server was:
TLSv1,TLSv1.1 - so TLSv1.2 was not supported by this MySQL server, and that is why I was getting a Communications link failure error message.
This is a MySQL install from an Ubuntu server using MySQL community edition. Well it turns out that MySQL supports two TLS/SSL implementations and the community edition is usually built with yaSSL (which only supports TLS 1 and TLS 1.1), while if it is compiled with OpenSSL (such as with the Enterprise edition) it supports TLS 1.2 as well. This detail is important, and really easy to overlook.
This is probably a good reason to use MariaDB instead of MySQL, recent versions of MariaDB even support TLSv1.3.
- MySQL JDBC Load Balancing with MySQL Cluster - November 28, 2008
- Multiple Statements with MySQL and JDBC - May 16, 2005
- Tomcat Virtual Directory Howto
- Redirect www and non https in IIS using web.config
- Not authorized to perform: ssm:GetParameters
- What is the difference between ASCII Chr(10) and Chr(13)
- Fixinator and Foundeo Security Bundle
- Running CFML on AWS Lambda with FuseLess Slides
- Updating Java on ColdFusion or Lucee