AWS DocumentDB TLS connection with Java - aws-documentdb

I am facing problems connecting with my DocumentDB cluster with plain Java over TLS/SSL
The procedure I followed as per the AWS docs is this:
I downloaded the .pem file from AWS and copied in the root of my java project, from where I executed:
keytool -importcert -trustcacerts -file rds-combined-ca-bundle.pem -alias certAlias -keystore rds-ca-certs -storepass keyStorePassword
This has created the rds-ca-certsat the root of my project which now looks like this:
And, the java code in Main.java is:
package com.example.documentdb;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.ServerAddress;
import com.mongodb.MongoException;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoCollection;
import org.bson.Document;
public final class Main {
private Main() {
}
public static void main(String[] args) {
String template = "mongodb://%s:%s#%s/test?ssl=true&replicaSet=rs0&readpreference=%s";
String username = "myUsernameInCluster";
String password = "myPasswordInCluster";
String clusterEndpoint = "mycluster.node.us-east-1.docdb.amazonaws.com:27017";
String readPreference = "secondaryPreferred";
String connectionString = String.format(template, username, password, clusterEndpoint, readPreference);
String keystore = "rds-ca-certs";
String keystorePassword = "keyStorePassword";
System.setProperty("javax.net.ssl.trustStore", keystore);
System.setProperty("javax.net.ssl.trustStorePassword", keystorePassword);
MongoClientURI clientURI = new MongoClientURI(connectionString);
MongoClient mongoClient = new MongoClient(clientURI);
MongoDatabase testDB = mongoClient.getDatabase("test");
MongoCollection<Document> numbersCollection = testDB.getCollection("numbers");
Document doc = new Document("name", "pi").append("value", 3.14159);
numbersCollection.insertOne(doc);
MongoCursor<Document> cursor = numbersCollection.find().iterator();
try {
while (cursor.hasNext()) {
System.out.println(cursor.next().toJson());
}
} finally {
cursor.close();
}
}
}
It gives me this ugly error:
com.mongodb.MongoSocketWriteException: Exception sending message
Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Interestingly, when I use the mongo CLI to connect over SSL/TLS, like this:
mongo --ssl --host mycluster.node.eu-central-1.docdb.amazonaws.com:27017 --sslCAFile rds-combined-ca-bundle.pem --username myUsernameInCluster --password myPasswordInCluster
It works perfectly, so I have discarded a networking issue and I think the problem is in the Java code itself or in the keytool command executed.
Additionally, with TLS disabled in the cluster this java code provided by AWS (that only differs in the keystore configuration) works.
PD: I know there are a bunch of other questions regarding SSL/TLS connection with AWS DocumentDB, but none of them address my issue specifically. Moreover, I have also checked plain mongodb TLS/SSL connection questions and the process differ so they are not valid for me.

The issue seems to be related to the process of importing the certificates within the bundle, under the same alias.
So I have had to stop using the bundled option (rds-combined-ca-bundle.pem) and start using this one rds-ca-2019-root.pem
After importing the keystore using the following command:
keytool -importcert -trustcacerts -file rds-ca-2019-root.pem -alias rds19 -keystore rds-ca-certs -storepass keyStorePassword
Connectivity with the database under TLS was achieved.

You can add the AWS provided certificate to the java's trust store then java will trust requests to AWS service by default.
You will have to find your java cacerts file. Depending on your OS and java version it should be in ..lib/security/cacerts.
I am using OSX and it was in
/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/security/cacerts
Then you cause keytool to import it the default java keystore password is change it:
keytool -import -trustcacerts -keystore /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/security/cacerts -storepass changeit -noprompt -alias aws-rds -file rds-combined-ca-bundle.pem
To check if the certificate was imported you can use:
keytool -list -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/security/cacerts | grep aws
Post back if that worked.

https://github.com/hungbang/spring-boot-aws-docdb
I have created a sample to connect to amazon docdb from spring boot with SSL enabled.

Related

MongoDB connection URI for SSL

I'm working on JDK8 and using mongo-java-driver(v3.5.0) to connect MongoDB(v3.6.3).
I've enabled SSL by following this article. I don't have /etc/mongod.conf file, instead I've /etc/mongodb.conf file; so I've updated the SSL settings in that file:
# SSL options
# Enable SSL on normal ports
sslOnNormalPorts = true
# SSL Key file and password
sslPEMKeyFile = /etc/ssl/mongodb.pem
sslPEMKeyPassword = PASSWORD
I'm able to access mongo via mongo shell using:
mongo --ssl --sslCAFile /etc/ssl/rootCA.pem --sslPEMKeyFile /etc/ssl/mongodb.pem --host localhost
I want to connect MongoDB using Java driver. I initially tried the following JDBC connection string:
mongodb://USER:PASSWORD#localhost:27017/?ssl=true&sslAllowInvalidCertificates=true&sslPEMKeyFile=/etc/ssl/mongodb.pem
but as per documentation, there are no such options available. Also, I get error:
The connection string contains an invalid host 'localhost:27017/?ssl=true&sslAllowInvalidCertificates=true&sslPEMKeyFile=/etc/ssl'. The port '27017/?ssl=true&sslAllowInvalidCertificates=true&sslPEMKeyFile=/etc/ssl' is not a valid, it must be an integer between 0 and 65535
And when I try with the following connection string:
url=mongodb://USER:PASSWORD#localhost:27017/?ssl=true
I get following error:
com.mongodb.MongoSocketWriteException: Exception sending message
at com.mongodb.connection.InternalStreamConnection.translateWriteException(InternalStreamConnection.java:445) ~[mongo-java-driver-3.5.0.jar:?]
.
...
Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
.
...
Here's the code that I've used:
String url = "mongodb://USER:PASSWORD#localhost/?authSource=admin&ssl=true"
MongoClientURI connectionURI = new MongoClientURI(url)
mongoClient = new MongoClient(connectionURI)
Could someone help me with what JDBC connection string I need to configure to connect successfully. Thank you.

How to connect the Rust MongoDB driver with Invalid Tls Certificates?

I'm unable to connect to the MongoDB with Tls Allow Invalid Certificate option with the Rust driver.
let uri = "MongoDB://user:pwd#host:port/database_name?tls=true&tlsAllowInvalidCertificates=true"
let client_options = ClientOptions::parse(uri)?;
let client = Client::with_options(client_options).expect("");
let db = client.database("database_name");
I can see that the client_options has correctly parsed the uri with:
tls: Some(Enabled(TlsOptions{allow_invalid_certificates: Some(true) ...}))
However I get the following non-recoverable error:
Error: invalid peer certificate contents: invalid peer certificate: UnsupportedCertVersion
Server MongoDB: 4.2.18
Client Mongosh: 1.5.0
Rust Driver: 2.3.0
P.s. I'm able to connect to MongoDB by the mongosh shell with the following command:
mongosh server_dns:server_port/database_name -u username -p --tls --tlsAllowInvalidCertificates
(It's also possible to connect with described options with pymongo)
What solved the issue was to add the feature "openssl-tls" to Cargo.toml
[dependencies.mongodb]
version = "2.3.0"
default-features = false
features = ["openssl-tls"]

How to successfully connect to kerberos authenticated mongod instance from java application?I'm getting exception initializing GSSAPI Credentials

I've started mongod instance in kerberos auth mode.I'm able to connect from mongo shell.While connecting from java application,I get the following exception:
Key for the principal mongodb/****#**** not available in ***.keytab
[Krb5LoginModule] authentication failed
Unable to obtain password from user
My gss-jaas.conf is
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
useTicketCache=false
principal="mongodb/***#***"
doNotPrompt=true
keyTab="D:\***.keytab"
debug=true;};
I've used ktuil and executed following commands to write principal to keytab file.Can anyone help me to find out what's going wrong?
ktutil: add_entry -password -p mongodb/***#*** -k 1 -e des-cbc-md4
Password for mongodb/***#***:
ktutil: wkt /tmp/***.keytab
ktutil: quit
I've entered the password and also tried by skipping it with enter.In both the cases,I'm getting the above exception.I've used the following system properties in java application:
System.setProperty("java.security.krb5.conf","D:\\krb5.conf");
System.setProperty("java.security.auth.login.config","D:\\gss-jaas.conf");
System.setProperty("javax.security.auth.useSubjectCredsOnly","false");
System.setProperty("java.security.krb5.realm","****");
System.setProperty("java.security.krb5.kdc","*****");
kerberosCredential = MongoCredential.createGSSAPICredential(userName);

MongoDB Jmeter with SSL File

So I'm following the guide over on BlazeMeter about how to load test MongoDB with Jmeter here
https://www.blazemeter.com/blog/mongodb-performance-testing-with-jmeter/
In my case I do have a connection string because our Mongo Deployment is using a replicaset. No biggie I just hardcoded it into the MongoClient section as I was having issues with passing it through as a variable.
But here is the deal. Our Mongo servers use a SSL file as a part of the authentication mechanism.
Is there a way I can just plug the SSL into a variable inside of jmeter and then have it be set properly in the database connection portion of the load test?
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
import com.mongodb.MongoClientSettings;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import java.util.Arrays;
try {
MongoClient mongoClient = MongoClients.create("MY_CONNECTION_STRING");
MongoDatabase database = mongoClient.getDatabase(vars.get("databaseName"));
MongoCollection<Document> collection = database.getCollection(vars.get("collectionName"));
vars.putObject("collection", collection);
return "Connected to " + vars.get("collectionName");
}
catch (Exception e) {
SampleResult.setSuccessful(false);
SampleResult.setResponseCode("500");
SampleResult.setResponseMessage("Exception: " + e);
}```
As per MongoDB Driver Reference Connecting SSL
JVM system properties
A typical application will need to set several JVM system properties to ensure that the client is able to validate the SSL certificate presented by the server:
javax.net.ssl.trustStore: the path to a trust store containing the certificate of the signing authority
javax.net.ssl.trustStorePassword: the password to access this trust store
The aforementioned properties can be defined either in system.properties file (lives in "bin" folder of your JMeter installation) or passed to JMeter startup script via -D command-line arguments
Also make sure to have ?ssl=true directive in your MY_CONNECTION_STRING JMeter Variable

How to configure SASL enabled memcached username and password on mac

I installed memcached version 1.4.34 on my mac using homebrew. I wanted to configure a username and password to enable SASL support when interacting with memcache. Can you point me the right direction for this?
Ran below command to install memcache on mac.
brew install memcached --enable-sasl-pwdb
Above command installed memcache with sasl support.
echo "mech_list: plain" > memcached.conf
echo "myuser:mypass" > /tmp/memcached-sasl-db
export MEMCACHED_SASL_PWDB=/tmp/memcached-sasl-db
export SASL_CONF_PATH=`pwd`/memcached.conf
memcached -u myuser -m 1024 -p 8010 -S -B binary -vvv
Initialized SASL.
Now when I connect via memcache client it says Password verification failed on the terminal.
mech: ``PLAIN'' with 15 bytes of data
INFO: User <myuser#mylocal-macbook.local> failed to authenticate
SASL (severity 2): Password verification failed
sasl result code: -20
Unknown sasl response: -20
Here's the java code I'm using:
public class MemcacheTest {
public static void main(String[] args) {
System.setProperty("net.spy.memcached.auth.AuthThreshold", "10");
AuthDescriptor ad = new AuthDescriptor(new String[] { "PLAIN" },
new PlainCallbackHandler(
"myuser", "mypass"));
ConnectionFactory connFactory = new ConnectionFactoryBuilder()
.setProtocol(ConnectionFactoryBuilder.Protocol.BINARY)
.setAuthWaitTime(10000)
.setOpTimeout(10000)
.setShouldOptimize(true)
.setAuthDescriptor(ad).build();
List<InetSocketAddress> servers = AddrUtil
.getAddresses("localhost:8010");
MemcachedClient cacheClient = null;
try {
cacheClient = new MemcachedClient(connFactory, servers);
cacheClient.set("foo", 50000, "bar");
System.out.println("Value: " + cacheClient.get("foo"));
} catch (IOException iox) {
iox.printStackTrace();
}
}
}
Here are my intellij logs:
2017-02-23 15:19:04.223 INFO net.spy.memcached.MemcachedConnection: Reconnection due to exception handling a memcached operation on {QA sa=localhost/127.0.0.1:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=1}. This may be due to an authentication failure.
OperationException: SERVER: Auth failure.
at net.spy.memcached.protocol.BaseOperationImpl.handleError(BaseOperationImpl.java:192)
at net.spy.memcached.protocol.binary.OperationImpl.finishedPayload(OperationImpl.java:204)
at net.spy.memcached.protocol.binary.SASLBaseOperationImpl.finishedPayload(SASLBaseOperationImpl.java:98)
at net.spy.memcached.protocol.binary.OperationImpl.readPayloadFromBuffer(OperationImpl.java:196)
at net.spy.memcached.protocol.binary.OperationImpl.readFromBuffer(OperationImpl.java:139)
at net.spy.memcached.MemcachedConnection.readBufferAndLogMetrics(MemcachedConnection.java:861)
at net.spy.memcached.MemcachedConnection.handleReads(MemcachedConnection.java:840)
at net.spy.memcached.MemcachedConnection.handleReadsAndWrites(MemcachedConnection.java:720)
at net.spy.memcached.MemcachedConnection.handleIO(MemcachedConnection.java:683)
at net.spy.memcached.MemcachedConnection.handleIO(MemcachedConnection.java:436)
at net.spy.memcached.MemcachedConnection.run(MemcachedConnection.java:1446)
Yay! I got the issue. Couch base client automatically adds hostname at the end of the username. In my case when I set the username as myuser:mypassword but when I pass myuser in my java code, memcacheclient pass that information as myuser#mylocalhost-mac and that's where the mismatch was happening. To solve that I added the complete username with local host of the ssl db.
Instead of below line
echo "myuser:mypass" > /tmp/memcached-sasl-db
is replaced by
echo "myuser#mylocalhostMacBook-Pro-2.local:mypass" > /tmp/memcached-sasl-db
Now in java client I just pass myuser and client add the hostname automatically.