Communications link failure MySQL JDBC with TLS

July 01, 2019
databasesjava

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 command:

mysql --host=10.1.2.3 --user=user --password --ssl --ssl-ca=./ca.pem

So my JDBC connection string looked something like this:

jdbc:mysql://10.1.2.3:3306/dbname?useSSL=true&requireSSL=true&trustCertificateKeyStoreUrl=file:///path/to/mysql-ca-truststore.p12&trustCertificateKeyStoreType=PKCS12&trustCertificateKeyStorePassword=pwd&enabledTLSProtocols=TLSv1.2

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.


Like this? Follow me ↯


You might also like:

2 people found this page useful, what do you think?

Post a Comment




  






Foundeo Inc.