Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for connecting to MongoDB using SSL #188

Merged
merged 2 commits into from
Nov 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions doc/INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ authorization for applications on Liberty, see [Configuring authorization for ap

### MongoDB configuration

If your MongoDB instance uses authentication or if other parameters, such as the MongoDB port,
are non-default then you may need to customize the properties in `bootstrap.properties`.
If your MongoDB instance uses authentication or SSL, or if other parameters such as the MongoDB port
are non-default then you will need to customize the properties in `bootstrap.properties`.

## Starting the server

Expand Down
3 changes: 2 additions & 1 deletion server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ def requiredSpecJars = [
'com.ibm.*.javaee.annotation.1.1_*.jar']

def requiredIbmJars = [
'com.ibm.*.passwordUtil_*.jar']
'com.ibm.*.passwordUtil_*.jar',
'com.ibm.*.ssl_*.jar']

def requiredThirdPartyJars = [
'com.ibm.websphere.appserver.thirdparty.jaxrs_*.jar']
Expand Down
21 changes: 17 additions & 4 deletions server/config/bootstrap.properties
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
# For a single mongodb server:
#### Basic configuration ####
# For a single MongoDB server:
#lars.mongo.hostname=localhost
#lars.mongo.port=27017

# For a mongodb cluster:
# Or for a MongoDB cluster add as many hostname/port pairs as needed:
#lars.mongo.hostname0=
#lars.mongo.port0=
#lars.mongo.hostname1=
#lars.mongo.port1=

# Name of the database (will be created if needed)
lars.mongo.dbname=larsDB

# Use slightly safer write concern than the default
lars.mongo.writeConcern=JOURNALED

#### Authentication ####
# If authentication is required:
#lars.mongo.user=
#lars.mongo.pass.encoded=
# If a different db is used for authentication:
#lars.mongo.authdb=admin

# Use slightly safer write concern than the default
lars.mongo.writeConcern=JOURNALED
#### SSL Configuration ####
# To connect to MongoDB using SSL (or TLS)
#lars.mongo.sslEnabled=true
# Use this to specify custom SSL settings, for example if a trust store
# is required. The value of this property refers to an <ssl> entry in
# the server.xml
#lars.mongo.sslConfig=mongoSSLConfig
11 changes: 8 additions & 3 deletions server/config/server.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,17 @@ limitations under the License.
<!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
<httpEndpoint httpPort="9080" httpsPort="9443" id="defaultHttpEndpoint"/>

<!-- This library must be used by the WAR app in order for both the app and the MongoDB feature to see the
same MongoDB driver classes.
-->
<!-- This library must be used by the WAR app in order to see the MongoDB driver classes. -->
<library id="mongo-lib" apiTypeVisibility="spec,ibm-api,api,third-party">
<fileset dir="${shared.resource.dir}/libs" includes="mongo-java-driver*.jar" />
</library>

<!-- Uncomment this to specify a custom trust store if SSL is required for connecting to MongoDB -->
<!--
<ssl id="mongoSSLConfig" trustStoreRef="mongoTrustStore" keyStoreRef="mongoTrustStore" />
<keyStore id="mongoTrustStore" password="somePassword location="mongoStore.p12" type="PKCS12" />
-->

<!-- Uncomment this to override the base URL (may be useful if lars is behind a reverse proxy -->
<!-- <jndiEntry id="lars/URLBase" jndiName="lars/URLBase" value="http://my.external.domain/repo/" /> -->

Expand All @@ -76,4 +80,5 @@ limitations under the License.
</application-bnd>
-->
</webApplication>

</server>
54 changes: 47 additions & 7 deletions server/src/main/java/com/ibm/ws/lars/rest/mongo/MongoProducer.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import javax.enterprise.inject.Produces;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.net.ssl.SSLContext;

import com.ibm.websphere.crypto.PasswordUtil;
import com.mongodb.MongoClient;
Expand All @@ -39,9 +40,12 @@ public class MongoProducer {
private static final Logger logger = Logger.getLogger(MongoProducer.class.getCanonicalName());

private String dbName = null;
private String authDbName = null;
private String user = null;
private String encodedPass = null;
private String requestedWriteConcern = null;
private boolean sslEnabled = false;
private String sslConfig = null;
private ArrayList<ServerAddress> servers = new ArrayList<ServerAddress>(2);

@PostConstruct
Expand All @@ -54,10 +58,22 @@ private void readConfig() {
// user and password (optional - if not set, use unauthenticated access)
user = sysprops.getProperty("lars.mongo.user");
encodedPass = sysprops.getProperty("lars.mongo.pass.encoded");
authDbName = sysprops.getProperty("lars.mongo.authdb");
if(authDbName == null) {
authDbName = dbName;
}

// writeConcern (optional - if not set use the default "ACKNOWLEDGED")
requestedWriteConcern = sysprops.getProperty("lars.mongo.writeConcern");

// sslEnabled (optional - if not set, assume false)
if("true".equalsIgnoreCase(sysprops.getProperty("lars.mongo.sslEnabled","false"))) {
sslEnabled = true;
}

// sslConfig (optional, only used if ssl is enabled)
sslConfig = sysprops.getProperty("lars.mongo.sslConfig");

// look for all lars.mongo.hostname* properties, in alphabetical order
Enumeration keysEnum = sysprops.keys();
Vector<String> keyList = new Vector<String>();
Expand Down Expand Up @@ -89,7 +105,9 @@ private void readConfig() {

@Produces
public MongoClient createMongo() {
MongoClientOptions opts;
MongoClientOptions.Builder builder = MongoClientOptions.builder();

// set the WriteConcern, if specified
if(requestedWriteConcern != null) {
WriteConcern wc;
switch(requestedWriteConcern)
Expand Down Expand Up @@ -131,26 +149,48 @@ public MongoClient createMongo() {
wc = WriteConcern.ACKNOWLEDGED;
logger.warning("No WriteConcern named " + requestedWriteConcern + " found. Using default WriteConcern of ACKNOWLEDGED.");
}
opts = new MongoClientOptions.Builder().writeConcern(wc).build();
builder = builder.writeConcern(wc);
logger.info("createMongo: using write concern " + requestedWriteConcern);
} else {
opts = new MongoClientOptions.Builder().build();
logger.info("createMongo: using default write concern");
}

if(encodedPass == null) {
logger.info("createMongo: connecting to database "+dbName+" using unauthenticated access");
// Configure SSL
if(sslEnabled) {
try {
SSLContext sslContext;
if(sslConfig == null) {
sslContext = SSLContext.getDefault();
} else {
sslContext = com.ibm.websphere.ssl.JSSEHelper.getInstance().getSSLContext(sslConfig, Collections.emptyMap(), null);
}
logger.info("createMongo: SSL enabled");
builder = builder.sslEnabled(sslEnabled).sslContext(sslContext);
} catch(com.ibm.websphere.ssl.SSLException ex) {
logger.severe("createMongo: Failed to initialize SSL: "+ex.getMessage());
return null;
} catch(java.security.NoSuchAlgorithmException ex) {
logger.severe("createMongo: Failed to initialize SSL: "+ex.getMessage());
return null;
}
}
MongoClientOptions opts = builder.build();

// Configure credentials, and connect
if(encodedPass == null) {
logger.info("createMongo: connecting using unauthenticated access");
return new MongoClient(servers, opts);
} else {
String password = PasswordUtil.passwordDecode(encodedPass);
MongoCredential creds = MongoCredential.createCredential(user, dbName, password.toCharArray());
logger.info("createMongo: connecting to database "+dbName+" as user "+user);
MongoCredential creds = MongoCredential.createCredential(user, authDbName, password.toCharArray());
logger.info("createMongo: connecting using user "+user+" and authentication database "+authDbName);
return new MongoClient(servers, creds, opts);
}
}

@Produces
public DB createDB(MongoClient client) {
logger.info("createMongo: connecting to database "+dbName);
return client.getDB(dbName);
}

Expand Down