[CONJ-670] MariaDB java connector ability to Refresh SSL certificate Created: 2019-01-04  Updated: 2019-02-08  Resolved: 2019-01-14

Status: Closed
Project: MariaDB Connector/J
Component/s: SSL
Affects Version/s: None
Fix Version/s: 1.8.0, 2.4.0

Type: Bug Priority: Major
Reporter: Andrew Garner Assignee: Diego Dupin
Resolution: Fixed Votes: 0
Labels: None

Attachments: Text File app_debug.log    
Issue Links:
Relates
relates to MDEV-16266 Ability to Refresh SSL Cert / CRL Wit... Closed

 Description   

We have some applications that run in a Cloudfoundry environment using the cloudfoundry java buildpack. In that environment, a security provider loads certificates from /etc/ssl/certs/ca-certificates.crt. When using mysql-connector-j, we find that CA certificates from this location are automatically trusted by our app and TLS connections are successfully established.

However, when using the mariadb java connector, we always get an "unknown ca" error:

2018-10-18T11:50:32.00-0500 [APP/PROC/WEB/0] OUT java.sql.SQLNonTransientConnectionException: Could not connect to q-n3s3y1.q-g651.bosh:3306 : Received fatal alert: unknown_ca
2018-10-18T11:50:32.00-0500 [APP/PROC/WEB/0] OUT at org.mariadb.jdbc.internal.util.exceptions.ExceptionMapper.get(ExceptionMapper.java:234) ~[mariadb-java-client-2.3.0.jar!/:na]
2018-10-18T11:50:32.00-0500 [APP/PROC/WEB/0] OUT at org.mariadb.jdbc.internal.util.exceptions.ExceptionMapper.getException(ExceptionMapper.java:165) ~[mariadb-java-client-2.3.0.jar!/:na]

It seems that the MariaDB java connector is not finding the CA certificates loaded by the security provider and this does not affect mysql-connector-j.

After some experimentation, we found that we could work around this problem by hardcoding the CA certificates location in our jdbcURL via the serverSslCert=/etc/ssl/certs/ca-certificates.crt (or similar). However, we would much prefer that the CA certificate is automatically trusted via the Cloudfoundry java buildpack's security provider rather than explicitly twiddling our jdbcURLs.



 Comments   
Comment by Diego Dupin [ 2019-01-07 ]

Hi andrew,

If you can provide some additional information to pinpoint the exact issue :

  • is it possible to run java command with ssl debug information ? with "-Djavax.net.debug=all" / setting System.setProperty("javax.net.debug", "true")
  • the server version
  • the connection string to know if you use one or 2 way ssl authentication
  • is the connection user configured to have ssl mandatory ? (i.e. result of the query 'select host, user, ssl_type from mysql.user where user='<myuser>')
  • which version of mysql driver do you use when working ? if i remember that right, some old mysql driver was permitting connection with SSL not enable even when SSL option is set
Comment by Andrew Garner [ 2019-01-08 ]
  • is it possible to run java command with ssl debug information ? with "-Djavax.net.debug=all" / setting System.setProperty("javax.net.debug", "true")

I've attached application logs. app_debug.log

Curiously when I enable javax.net.debug=all I no longer see the "unknown_ca", but rather a Broken Pipe error. However, this seems to be caused by the SSL handshake failing, and it does go away once we explicitly set the serverSslCert.

  • the server version

Percona Server 5.7.23-23

  • the connection string to know if you use one or 2 way ssl authentication

jdbc:mysql://q-n3s3y1.q-g799.bosh:3306/service_instance_db?user=a2ef9950e97b418d8e85a13b8f8ff2a9\u0026useSSL=true\u0026requireSSL=true

  • is the connection user configured to have ssl mandatory ? (i.e. result of the query 'select host, user, ssl_type from mysql.user where user='<myuser>')

We are not using mutual TLS or strictly requiring TLS on the mysql user level:

mysql> select ssl_type from mysql.user where User = 'a2ef9950e97b418d8e85a13b8f8ff2a9';
+----------+
| ssl_type |
+----------+
|          |
+----------+
1 row in set (0.00 sec)

  • which version of mysql driver do you use when working ? if i remember that right, some old mysql driver was permitting connection with SSL not enable even when SSL option is set

When our SSL connections work, we are using mysql-connector-java-5.1.45. We verify that we're making a SSL connection by checking performance_schema.threads:

mysql> select connection_type from performance_schema.threads where processlist_user = 'a2ef9950e97b418d8e85a13b8f8ff2a9';
+-----------------+
| connection_type |
+-----------------+
| SSL/TLS         |
+-----------------+
1 row in set (0.00 sec)

Additionally we've verified that that MySQL connector was actually verifying our server certificate by reconfiguring the MySQL server with a certificated signed by an untrusted CA and seeing validation failures.

Comment by Steve Taylor [ 2019-01-09 ]

We think the issue is here.

Basically, the code returns the first x509 TrustManager it finds, ignoring any others that may be present.

Comment by Andrew Garner [ 2019-01-10 ]

This change appears to work in our environment:

diff --git a/src/main/java/org/mariadb/jdbc/internal/protocol/AbstractConnectProtocol.java b/src/main/java/org/mariadb/jdbc/internal/protocol/AbstractConnectProtocol.java
index 871402a4..c12044af 100644
--- a/src/main/java/org/mariadb/jdbc/internal/protocol/AbstractConnectProtocol.java
+++ b/src/main/java/org/mariadb/jdbc/internal/protocol/AbstractConnectProtocol.java
@@ -340,12 +340,12 @@ public abstract class AbstractConnectProtocol implements Protocol {
   }
 
   private SSLSocketFactory getSslSocketFactory() throws SQLException {
-    if (!options.trustServerCertificate
-        && options.serverSslCert == null
-        && options.trustStore == null
-        && options.keyStore == null) {
-      return (SSLSocketFactory) SSLSocketFactory.getDefault();
-    }
+    // if (!options.trustServerCertificate
+    //     && options.serverSslCert == null
+    //     && options.trustStore == null
+    //     && options.keyStore == null) {
+    //   return (SSLSocketFactory) SSLSocketFactory.getDefault();
+    // }
 
     TrustManager[] trustManager = null;
     KeyManager[] keyManager = null;

With this change, we seem to be hitting the path where we initialize the SSLContext:

      SSLContext sslContext = SSLContext.getInstance("TLS");
      sslContext.init(keyManager, trustManager, null);
      return sslContext.getSocketFactory();

I think with non-MariaDB connectors, there is a similar code path (SSLContext -> init()). This seems to explain why we only observe this behavior with the MariaDB Connector.

Comment by Diego Dupin [ 2019-01-11 ]

This part :

    if (!options.trustServerCertificate
        && options.serverSslCert == null
        && options.trustStore == null
        && options.keyStore == null) {
      return (SSLSocketFactory) SSLSocketFactory.getDefault();
    }

is to just for "fast path" execution.

It's probably due to the fact that SSLSocketFactory.getDefault() has been already used and storing default manager factory in static variables.
And that, before CloudFoundryContainerProvider reset the default manager factory to CloudFoundryContainerTrustManagerFactory.

I need to test that.

Comment by Diego Dupin [ 2019-01-14 ]

confirmed.
The SSLContext will be initialized each time to permit trustStore / keyStore dynamically change.

Generated at Thu Feb 08 03:17:27 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.