In this post I've documented the typical steps you need to enable a Java application to connect to a MongoDB database over SSL. Specifically, I show the so-called "one-way SSL" pattern, where the server is required to present a certificate to the client but the client is not required to present a certificate to the server. Regardless of authentication, communication between client and server is encrypted in both directions. In this example, the client actually authenticates with the database, albeit using a username/password rather than presenting a certificate. In addition, I also show how the client application can run operations against a database that has access control rules defined for it.
Note: Ensure you are running the
Enterprise version of MongoDB to be able to configure SSL.
1. Generate Key+Certificate and Configure With MongoDB For SSL
Create a key and certificate:
$ su -
$ cd /etc/ssl/
$ openssl req -new -x509 -days 365 -nodes -out mongodb-cert.crt -keyout mongodb-cert.key
$ cat mongodb-cert.key mongodb-cert.crt > mongodb.pem
$ exit
Add the following entries to
mongodb.conf (or equivalent if
setting command line options):
sslOnNormalPorts=true
sslPEMKeyFile=/etc/ssl/mongodb.pem
Start the
mongod database server.
Test the SSL connection from the Mongo shell.
$ mongo --ssl
2. Configure Java Client For SSL
Create a new Java trust store in the local application's root directory, importing the certificate generated from the previous section. This is necessary because in this example a self-signed certificate is used.
$ keytool -import -alias "MongoDB-cert" -file "/etc/ssl/mongodb-cert.crt" -keystore truststore.ts -noprompt -storepass "mypasswd"
$ keytool -list -keystore truststore.ts -storepass mypasswd
In the client application's Java code, add the "ssl=true" parameter to the MongoDB URI to tell it to use an SSL connection, eg:
MongoClientURI uri = new MongoClientURI("mongodb://localhost:27017/test?ssl=true");
Modify the command line or script that runs the 'Java' executable for the client application, and add the following JVM command line property. This will allow the application to validate the server's certificate against the new trust store, when using SSL.
-Djavax.net.ssl.trustStore=truststore.ts
Run the Java client application to test that the SSL connection works.
3. Configure MongoDB database with Access Control Rules
Using the Mongo shell, connect to the database, and define an 'administrator user', plus a 'regular' user with an access control rule defined to enable it to have read/write access to a database (called 'test' in this example).
$ mongo --ssl
> use admin
> db.addUser({user: "admin", pwd: "admin", roles: [ "userAdminAnyDatabase"]})
> use test
> db.addUser({user: "paul", pwd: "password", roles: ["readWrite", "dbAdmin"]})
Add the following entry to
mongodb.conf (or equivalent if
setting command line options):
auth=true
Restart the
mongod database server to pick up this change.
Test the SSL connection to the 'test' database with username/password authentication, from the Mongo shell.
$ mongo test --ssl -u paul -p password
If the user specified doesn't have permissions to read collections in the database, an error similar to below will occur when trying to run
db.mycollection,find(), for example.
error: { "$err" : "not authorized for query on test.mycollection", "code" : 13 }
4. Configure Java Client To Authenticate
Modify the client application code to include the username and password in the URL, eg:
MongoClientURI uri = new MongoClientURI("mongodb://paul:password@localhost:27017/test?ssl=true");
Run the Java client application to test that using SSL with username/password authentication works and has the rights to access the sample database.
If the user specified doesn't have correct permissions, an exception similar to below will occur.
Exception in thread "main" com.mongodb.MongoException: not authorized for query on test.system.namespaces
at com.mongodb.MongoException.parse(MongoException.java:82)
at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:292)
at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:273)
at com.mongodb.DB.getCollectionNames(DB.java:400)
at MongoTest.main(MongoTest.java:25)
Note: In a real Java application, you would invariably build the
URL dynamically, to avoid hard-coding a username and password in clear text in code.
Song for today: My Sister in 94 by The Paradise Motel