简体   繁体   中英

How to connect to SQL DB from Java JDBC with mutual TLS/SSL encryption

Cloud databases are very prevalent today and sometimes we need to connect to one from a laptop or similar. Usually there is an option for an unencrypted connection but that's not very secure. So how do I connect to a mutual TLS mysql server (like google cloud SQL)?

From mysql cli it's rather straight-forward after downloading the server ca cert, client cert and key from google cloud console:

mysql -u <user-name> -h <server-ip> -p --ssl-ca=server-ca.pem --ssl-cert=client-cert.pem --ssl-key=client-key.pem

Note: for Google cloud you can also use the Cloud SQL Proxy but I want to connect without that

Logstash 7 and jvm 9+ below.

OK, I finally figured it out piecing together information from different sources. This solution is tested with google cloud SQL and hibernate but should work in other setups as well.

The solution requires using java keystore (client cert/key) and truststore (server-ca cert) and passing some jdbc URL params and JVM options.

First off, lets create a truststore from the server-ca.pem file:

keytool -importcert -alias gcpRoot -file server-ca.pem -keystore truststore.jks -storepass <chose a password and remember>

EDIT: Or add CA to jvms existing cacerts file (to ensure other https calls will work), copy cacerts it rename to truststore.jks and run:

OR: keytool -importcert -alias gcpRoot -file server-ca.pem -keystore truststore.jks -storepass changeit

Secondly, we need to import the client cert and key to a keystore file in two steps (I used my SQL username as alias but I don't think it matters)

openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem -out keystore.p12 -name "<keystore-alias>" -passout pass:<chose a password and remember>
keytool -importkeystore -alias <keystore-alias> -srckeystore keystore.p12  -srcstoretype pkcs12 -destkeystore keystore.jks -srcstorepass <insert same password here> -deststoretype JKS -deststorepass <password-for-output-jks-keystore>

Next, we need to modify the jdbc connection URL (NOTE! Some IDEs - like intelliJ - requires & to be escaped and be replaced with &amp;

jdbc:mysql://<server-ip>/<db-name>?verifyServerCertificate=true&useSSL=true&requireSSL=true

Finally we need to provide location and passwords for keystore and truststore as JVM parameters:

-Djavax.net.ssl.trustStore="truststore.jks"
-Djavax.net.ssl.trustStorePassword="<password>"
-Djavax.net.ssl.keyStore="keystore.jks"
-Djavax.net.ssl.keyStorePassword="<password>"

Update: If you are running on a jvm version 9 or above (like logstash docker images for 7+) there are some class-loading issues to be worked around to get the jdbc driver to even load and you need a recent version of the mysql driver for TLS to work.

First you apparently have to put the .jar file of the jdbc driver in <logstash-dir>/logstash-core/lib/jars/mysql-connector-java-8.0.17.jar (this will cause the jar to be loaded automatically). And we also need to add the following to the input config:

jdbc_driver_library => ""
jdbc_driver_class => "com.mysql.cj.jdbc.Driver"

For some reason I still get the warning Loading class com.mysql.jdbc.Driver. This is deprecated. The new driver class is Loading class com.mysql.jdbc.Driver. This is deprecated. The new driver class is Loading class com.mysql.jdbc.Driver. This is deprecated. The new driver class is com.mysql.cj.jdbc.Driver'` but it works despite this warning.

While not is not exactly an answer to the question I will add this example of how to make this work with logstash jdbc plugin deployed on kubernetes since it was not completely straight-forward and it might be useful for similar systems.

This solution is based on the first solution but the truststore must be based on the full cacerts of the jvm.

In your input definition add the parameters to the jdbc URL:

input {
  jdbc {
    ...
    jdbc_connection_string => "jdbc:mysql://<server-ip>:3306/<db>?verifyServerCertificate=true&useSSL=true&requireSSL=true"
    jdbc_password => "${MYSQL_PASSWORD}"
    ...
  }
}

Then create a secret from the trust- and keystore and for mysql password:

kubectl create secret generic java-tls-creds --from-file=./keystore.jks --from-file=./truststore.jks
kubectl create secret generic mysql-password --from-literal='password=<password>'

Then we modify the deployment yaml to mount the credentials and add the LS_JAVA_OPTS to point to them:

apiVersion: apps/v1
kind: Deployment
... 
        env:
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-password
              key: password
        - name: LS_JAVA_OPTS
          value: -Djavax.net.ssl.keyStore=/java-tls-creds/keystore.jks -Djavax.net.ssl.keyStorePassword=<password> -Djavax.net.ssl.trustStore=/java-tls-creds/truststore.jks -Djavax.net.ssl.trustStorePassword=changeit
...
        volumeMounts:
        - name: java-tls-creds
          mountPath: "/java-tls-creds"
          readOnly: true
...
      volumes:
      - name: java-tls-creds
        secret:
          secretName: java-tls-creds

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM