8 min read
When working with a cloud database, such as AWS S3 or GCP Firebase, and using a secured MapR cluster, one might come across some difficulties while setting this kind of environment up.
One issue that has been showing up recently for us is related to the required certificates needed to communicate between all these different technologies. In particular, we are seeing the following exception:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target.
This article shows how to download the right certificates and place them in the right locations. For the sake of this post, we are going to use GCP Firebase, since the documentation is not clear enough, so we can light a spark in here.
By the end of this post, we will know how we configure the system so the right certificates are correctly located in the MapR ssl_truststore file.
The first step is to verify the location of the
ssl_truststore file that the MapR nodes are using. We can do this by viewing the contents of our ssl-server.xml file, which should be under the /opt/mapr/conf directory.
In our case, the ssl-server.xml tells us the ssl_truststore file is in /opt/mapr/conf. Also it tells us that the password to access the ssl_truststore is 'mapr123' (make sure to remember that).
<configuration> <property> <name>ssl.server.truststore.location</name> <value>/opt/mapr/conf/ssl_truststore</value> <description>Truststore to be used by webservers. Must be specified. </description> </property> <property> <name>ssl.server.truststore.password</name> <value>mapr123</value> <description>Optional. Default value is "". </description> </property> ...
Now if our jars or commands accessing Firebase have the option '-Djavax.net.ssl.trustStore' set, the option will override the TrustStore location in this ssl-server.xml. In these cases, be sure to treat that location as the location for which we add the certificates to instead of the location listed in the XML file.
Additionally, the TrustStore password will also not be what's described in the ssl-server.xml. Instead, we may need to consult the creator of that TrustStore.
After finding out where our TrustStore file is and its password, list the certificates in our ssl_truststore file by using the following command. Make sure to replace the location and password of our TrustStore with our TrustStore’s information in the command.
keytool -list -v -keystore /opt/mapr/conf/ssl_truststore -storepass mapr123
Note that we can still list our certificates without having a password (take out '-storepass mapr123' and enter no password when it prompts), but we will get a warning message: 'The integrity of the information stored in your keystore has not been verified!' Note that this does not mean our ssl_truststore has no password set – it's still the password from the ssl-server.xml file.
Now we can search for the number of certificates in our ssl_truststore that was listed from the keytool command. In our case, we only have one, and it's for our MapR cluster, my.cluster.com.
... Your keystore contains 1 entry Alias name: my.cluster.com Creation date: Jan 21, 2019 Entry type: trustedCertEntry ...
If our ssl_truststore has more than the MapR certificate, look for a certificate that matches this Firebase information: Owner: CN= firebaseio.com, O=Google LLC, L=Mountain View, ST=California, C=US. If we see this, then our error is not based on having the right certificates in this node's ssl_truststore. Search through the ssl-server.xml file on every node in our MapR cluster and verify they all point to a valid ssl_truststore file. We can also verify that our ssl_truststores are the same by listing the certificates in every node's ssl_truststore.
At this time, we've verified that the certificate for Firebase is not already in the ssl_truststore, so it's time to move to the next part – adding the Firebase certificate. There are two ways to add the certificate – OpenSSL or Java CAcerts.
The Firebase certificate can be hard to find. In order to get the Firebase base certification we need to first find the URL of the database that we are trying to write to. We can obtain the URL by looking through the errors, particularly for the ResourceAccessException.
Exception in thread "main" org.springframework.web.client.ResourceAccessException: I/O error on PUT request for "https://mapr-connected-driver.firebaseio.com/cars/2HML79P87JY00001 6.json": sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target ...
From our error logs, it looks like we were trying to write a JSON file into a table called “cars”: https://mapr-connected-driver.firebaseio.com/cars/2HML79P87JY000016.json. The database location is the hostname of that URL: https://mapr-connected-driver.firebaseio.com/.
Now that we have a database location, we need to use the OpenSSL library to download the certificate. Take off the 'https://' from your database location, and use port 443 (the default port for https). Also provide a name for our certificate file after the "-out" option – in our case we chose 'connected-driver-firebase-ssl.cert.' Running as sudo is important at this point, so we use sudo if we are not.
openssl s_client -connect mapr-connected-driver.firebaseio.com:443 | openssl x509 -out connected-driver-firebase-ssl.cert
We might be tempted to use Firefox to export our certificate instead of using OpenSSL. This isn't recommended because of how Firebase's certificates work within browsers – we will end up with a misc.google.com certificate, which won't be the right certificate.
Add the certificate to our ssl_truststore. We gave it the alias of 'connected_vehicle_firebase_certificate.' Again, let’s make sure we are root or we will need sudo access to execute the following command. We will be prompted for the ssl_truststore password; in our case it was 'mapr123,' the default.
keytool -import -alias connected_vehicle_firebase_certificate -file connected-driver-firebase-ssl.cert -keystore /opt/mapr/conf/ssl_truststore
We can instead import the cacerts TrustStore file from Java rather than using OpenSSL. It can be more convenient because it is a single step to get the file and add it to our ssl_truststore – we would only enter the single line of code below, and our TrustStore would have the right certificates.
However, adding the cacerts file can add quite a few unnecessary certificates and pollute our ssl_truststore file a bit. It also requires having to know the password of the cacerts file, i.e., the source password. The default password of the cacerts file should be 'changeit.' We will also be asked for the password of our ssl_truststore (which was 'mapr123' in our case).
Enter both passwords when prompted after running this command:
keytool -importkeystore -srckeystore $JAVA_HOME/lib/security/cacerts -destkeystore /opt/mapr/conf/ssl_truststore
We will need to download and add certificates in the same fashion to every node in our cluster. Or, if we have ClusterShell and are confident our ssl_truststores are in the same location across all nodes, we simply clush copy to all nodes using this command:
clush -a --copy /opt/mapr/conf/ssl_truststore
After following the described steps, we should be able to access our cloud database with all our MapR cluster nodes, so we can take advantage of these great technologies working together.
Even though this guide focuses on communicating MapR Data Platform and Firebase, a similar approach can be taken for any other database we shall use, since the work around CAcerts will be the same.
Stay ahead of the bleeding edge...get the best of Big Data in your inbox.