Uploaded image for project: 'MariaDB Connector/J'
  1. MariaDB Connector/J
  2. CONJ-670

MariaDB java connector ability to Refresh SSL certificate

Details

    • Bug
    • Status: Closed (View Workflow)
    • Major
    • Resolution: Fixed
    • None
    • 1.8.0, 2.4.0
    • SSL
    • None

    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.

      Attachments

        Issue Links

          Activity

            diego dupin Diego Dupin added a comment -

            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
            diego dupin Diego Dupin added a comment - 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
            andrew.garner Andrew Garner added a comment - - edited
            • 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.

            andrew.garner Andrew Garner added a comment - - edited 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.
            staylor Steve Taylor added a comment -

            We think the issue is here.

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

            staylor Steve Taylor added a comment - We think the issue is here . Basically, the code returns the first x509 TrustManager it finds, ignoring any others that may be present.
            andrew.garner Andrew Garner added a comment -

            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.

            andrew.garner Andrew Garner added a comment - 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.
            diego dupin Diego Dupin added a comment - - edited

            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.

            diego dupin Diego Dupin added a comment - - edited 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.
            diego dupin Diego Dupin added a comment -

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

            diego dupin Diego Dupin added a comment - confirmed. The SSLContext will be initialized each time to permit trustStore / keyStore dynamically change.

            People

              diego dupin Diego Dupin
              andrew.garner Andrew Garner
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Git Integration

                  Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.