Skip to content

Commit

Permalink
[ARTEMIS-2704]: Provide a SPI to manage and cache SSLContext.
Browse files Browse the repository at this point in the history
 * Adding a new SPI to allow for SSLContext reuse accross the broker.
 * Providing a default behaviour similar to the existing one.

[ARTEMIS-2718]: Take advantage of ARTEMIS-2704 to cache SSLContexts.
* Adding a cache for SSLContexts and reusing them accross acceptors and
  connectors.

Issue: https://issues.apache.org/jira/browse/ARTEMIS-2704
Issue: https://issues.apache.org/jira/browse/ARTEMIS-2718
  • Loading branch information
ehsavoie authored and clebertsuconic committed May 15, 2020
1 parent 79fef2f commit a88815d
Show file tree
Hide file tree
Showing 10 changed files with 319 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@
import io.netty.util.ResourceLeakDetector.Level;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.core.client.ActiveMQClientLogger;
import org.apache.activemq.artemis.core.client.ActiveMQClientMessageBundle;
Expand All @@ -123,6 +122,9 @@

import static org.apache.activemq.artemis.utils.Base64.encodeBytes;

import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.spi.core.remoting.ssl.SSLContextFactoryProvider;

public class NettyConnector extends AbstractConnector {

public static String NIO_CONNECTOR_TYPE = "NIO";
Expand Down Expand Up @@ -674,22 +676,10 @@ private SSLEngine loadJdkSslEngine(String keystoreProvider,
String truststoreProvider,
String truststorePath,
String truststorePassword) throws Exception {
SSLContext context;
if (useDefaultSslContext) {
context = SSLContext.getDefault();
} else {
context = new SSLSupport()
.setKeystoreProvider(keystoreProvider)
.setKeystorePath(keystorePath)
.setKeystorePassword(keystorePassword)
.setTruststoreProvider(truststoreProvider)
.setTruststorePath(truststorePath)
.setTruststorePassword(truststorePassword)
.setTrustAll(trustAll)
.setCrlPath(crlPath)
.setTrustManagerFactoryPlugin(trustManagerFactoryPlugin)
.createContext();
}
SSLContext context = SSLContextFactoryProvider.getSSLContextFactory().getSSLContext(configuration,
keystoreProvider, keystorePath, keystorePassword,
truststoreProvider, truststorePath, truststorePassword,
crlPath, trustManagerFactoryPlugin, trustAll);
Subject subject = null;
if (kerb5Config != null) {
LoginContext loginContext = new LoginContext(kerb5Config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public class TransportConstants {

private static final Logger logger = Logger.getLogger(TransportConstants.class);

public static final String SSL_CONTEXT_PROP_NAME = "sslContext";

public static final String SSL_ENABLED_PROP_NAME = "sslEnabled";

public static final String SSL_KRB5_CONFIG_PROP_NAME = "sslKrb5Config";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2020 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.core.remoting.impl.ssl;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.SSLContext;
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
import org.apache.activemq.artemis.utils.ConfigurationHelper;

/**
* SSLContextFactory providing a cache of SSLContext.
* Since SSLContext should be reused instead of recreated and are thread safe.
* To activate it uou need to allow this Service to be discovered by having a
* <code>META-INF/services/org.apache.activemq.artemis.spi.core.remoting.ssl.SSLContextFactory</code>
* file with <code> org.apache.activemq.artemis.core.remoting.impl.ssl.CachingSSLContextFactory</code>
* as value.
*/
public class CachingSSLContextFactory extends DefaultSSLContextFactory {

private static final Map<String, SSLContext> SSL_CONTEXTS = Collections.synchronizedMap(new HashMap<>());

@Override
public void clearSSLContexts() {
SSL_CONTEXTS.clear();
}

@Override
public SSLContext getSSLContext(Map<String, Object> configuration,
String keystoreProvider, String keystorePath, String keystorePassword,
String truststoreProvider, String truststorePath, String truststorePassword,
String crlPath, String trustManagerFactoryPlugin, boolean trustAll) throws Exception {
String sslContextName = getSSLContextName(configuration, keystorePath, keystoreProvider, truststorePath, truststoreProvider);
if (!SSL_CONTEXTS.containsKey(sslContextName)) {
SSL_CONTEXTS.put(sslContextName, createSSLContext(configuration,
keystoreProvider, keystorePath, keystorePassword,
truststoreProvider, truststorePath, truststorePassword,
crlPath, trustManagerFactoryPlugin, trustAll));
}
return SSL_CONTEXTS.get(sslContextName);
}

/**
* Obtain the sslContextName :
* - if available the 'sslContext' from the configuration
* - otherwise if available the keyStorePath + '_' + keystoreProvider
* - otherwise the truststorePath + '_' + truststoreProvider.
* @param configuration
* @param keyStorePath
* @param keystoreProvider
* @param truststorePath
* @param truststoreProvider
* @return the ley associated to the SSLContext.
*/
protected String getSSLContextName(Map<String, Object> configuration, String keyStorePath, String keystoreProvider, String truststorePath, String truststoreProvider) {
String sslContextName = ConfigurationHelper.getStringProperty(TransportConstants.SSL_CONTEXT_PROP_NAME, null, configuration);
if (sslContextName == null) {
if (keyStorePath != null) {
return keyStorePath + '_' + keystoreProvider;
}
return truststorePath + '_' + truststoreProvider;
}
return sslContextName;
}

@Override
public int getPriority() {
return 10;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2020 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.core.remoting.impl.ssl;

import java.util.Map;
import javax.net.ssl.SSLContext;
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
import org.apache.activemq.artemis.spi.core.remoting.ssl.SSLContextFactory;
import org.apache.activemq.artemis.utils.ConfigurationHelper;

/**
* Simple SSLContextFactory for use in NettyConnector and NettyAcceptor.
*/
public class DefaultSSLContextFactory implements SSLContextFactory {


@Override
public SSLContext getSSLContext(Map<String, Object> configuration,
String keystoreProvider, String keystorePath, String keystorePassword,
String truststoreProvider, String truststorePath, String truststorePassword,
String crlPath, String trustManagerFactoryPlugin, boolean trustAll) throws Exception {
return createSSLContext(configuration,
keystoreProvider, keystorePath, keystorePassword,
truststoreProvider, truststorePath, truststorePassword,
crlPath, trustManagerFactoryPlugin, trustAll);
}

protected SSLContext createSSLContext(Map<String, Object> configuration,
String keystoreProvider, String keystorePath, String keystorePassword,
String truststoreProvider, String truststorePath, String truststorePassword,
String crlPath, String trustManagerFactoryPlugin, boolean trustAll) throws Exception {
if (log.isDebugEnabled()) {
final StringBuilder builder = new StringBuilder();
configuration.forEach((k, v) -> builder.append("\r\n").append(k).append("=").append(v));
log.debugf("Creating SSL context with configuration %s", builder.toString());
}
boolean useDefaultSslContext = ConfigurationHelper.getBooleanProperty(TransportConstants.USE_DEFAULT_SSL_CONTEXT_PROP_NAME, TransportConstants.DEFAULT_USE_DEFAULT_SSL_CONTEXT, configuration);
if (useDefaultSslContext) {
return SSLContext.getDefault();
}
return new SSLSupport()
.setKeystoreProvider(keystoreProvider)
.setKeystorePath(keystorePath)
.setKeystorePassword(keystorePassword)
.setTruststoreProvider(truststoreProvider)
.setTruststorePath(truststorePath)
.setTruststorePassword(truststorePassword)
.setTrustAll(trustAll)
.setCrlPath(crlPath)
.setTrustManagerFactoryPlugin(trustManagerFactoryPlugin)
.createContext();
}
@Override
public int getPriority() {
return 5;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2020 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.spi.core.remoting.ssl;

import java.util.Map;
import javax.net.ssl.SSLContext;
import org.jboss.logging.Logger;

/**
* Service interface to create a SSLContext for a configuration.
* This is NOT used by OpenSSL.
* To create and use your own implementation you need to create a file
* <code>META-INF/services/org.apache.activemq.artemis.spi.core.remoting.ssl.SSLContextFactory</code>
* in your jar and fill it with the full qualified name of your implementation.
*/
public interface SSLContextFactory extends Comparable<SSLContextFactory> {
Logger log = Logger.getLogger(SSLContextFactory.class);

/**
* Obtain a SSLContext from the configuration.
* @param configuration
* @param keystoreProvider
* @param keystorePath
* @param keystorePassword
* @param truststoreProvider
* @param truststorePath
* @param truststorePassword
* @param crlPath
* @param trustManagerFactoryPlugin
* @param trustAll
* @return a SSLContext instance.
* @throws Exception
*/
SSLContext getSSLContext(Map<String, Object> configuration,
String keystoreProvider, String keystorePath, String keystorePassword,
String truststoreProvider, String truststorePath, String truststorePassword,
String crlPath, String trustManagerFactoryPlugin, boolean trustAll) throws Exception;

default void clearSSLContexts() {
}

/**
* The priority for the SSLContextFactory when resolving the service to get the implementation.
* This is used when selecting the implementation when several implementations are loaded.
* The highest priority implementation will be used.
* @return the priority.
*/
int getPriority();

@Override
default int compareTo(SSLContextFactory other) {
return this.getPriority() - other.getPriority();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2020 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.spi.core.remoting.ssl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ServiceLoader;

/**
* Provider that loads the SSLContextFactory services and return the one with the highest priority.
* This is only used to provide SSLContext, so it doesn't support OpenSSL.
*/
public class SSLContextFactoryProvider {
private static final SSLContextFactory factory;
static {
ServiceLoader<SSLContextFactory> loader = ServiceLoader.load(SSLContextFactory.class, Thread.currentThread().getContextClassLoader());
final List<SSLContextFactory> factories = new ArrayList<>();
loader.forEach(factories::add);
Collections.sort(factories);
factory = factories.get(factories.size() - 1);
}
/**
* @return the SSLContextFactory with the higher priority.
*/
public static SSLContextFactory getSSLContextFactory() {
return factory;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.apache.activemq.artemis.core.remoting.impl.ssl.DefaultSSLContextFactory
Loading

0 comments on commit a88815d

Please sign in to comment.