This closes #2923
This commit is contained in:
commit
73156cb79d
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.api.core;
|
||||
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
public interface TrustManagerFactoryPlugin {
|
||||
|
||||
/**
|
||||
* @return the TrustManagerFactory used when invoking javax.net.ssl.TrustManagerFactory#getTrustManagers() to initialize the SSLContext
|
||||
*/
|
||||
TrustManagerFactory getTrustManagerFactory();
|
||||
}
|
|
@ -222,6 +222,8 @@ public class NettyConnector extends AbstractConnector {
|
|||
|
||||
private String sslProvider;
|
||||
|
||||
private String trustManagerFactoryPlugin;
|
||||
|
||||
private boolean verifyHost;
|
||||
|
||||
private boolean trustAll;
|
||||
|
@ -371,6 +373,8 @@ public class NettyConnector extends AbstractConnector {
|
|||
kerb5Config = ConfigurationHelper.getStringProperty(TransportConstants.SSL_KRB5_CONFIG_PROP_NAME, TransportConstants.DEFAULT_SSL_KRB5_CONFIG, configuration);
|
||||
|
||||
useDefaultSslContext = ConfigurationHelper.getBooleanProperty(TransportConstants.USE_DEFAULT_SSL_CONTEXT_PROP_NAME, TransportConstants.DEFAULT_USE_DEFAULT_SSL_CONTEXT, configuration);
|
||||
|
||||
trustManagerFactoryPlugin = ConfigurationHelper.getStringProperty(TransportConstants.TRUST_MANAGER_FACTORY_PLUGIN_PROP_NAME, TransportConstants.DEFAULT_TRUST_MANAGER_FACTORY_PLUGIN, configuration);
|
||||
} else {
|
||||
keyStoreProvider = TransportConstants.DEFAULT_KEYSTORE_PROVIDER;
|
||||
keyStorePath = TransportConstants.DEFAULT_KEYSTORE_PATH;
|
||||
|
@ -385,6 +389,7 @@ public class NettyConnector extends AbstractConnector {
|
|||
trustAll = TransportConstants.DEFAULT_TRUST_ALL;
|
||||
sniHost = TransportConstants.DEFAULT_SNIHOST_CONFIG;
|
||||
useDefaultSslContext = TransportConstants.DEFAULT_USE_DEFAULT_SSL_CONTEXT;
|
||||
trustManagerFactoryPlugin = TransportConstants.DEFAULT_TRUST_MANAGER_FACTORY_PLUGIN;
|
||||
}
|
||||
|
||||
tcpNoDelay = ConfigurationHelper.getBooleanProperty(TransportConstants.TCP_NODELAY_PROPNAME, TransportConstants.DEFAULT_TCP_NODELAY, configuration);
|
||||
|
@ -634,6 +639,7 @@ public class NettyConnector extends AbstractConnector {
|
|||
.setTruststorePassword(truststorePassword)
|
||||
.setTrustAll(trustAll)
|
||||
.setCrlPath(crlPath)
|
||||
.setTrustManagerFactoryPlugin(trustManagerFactoryPlugin)
|
||||
.createContext();
|
||||
}
|
||||
Subject subject = null;
|
||||
|
@ -675,6 +681,7 @@ public class NettyConnector extends AbstractConnector {
|
|||
.setTruststorePassword(truststorePassword)
|
||||
.setSslProvider(sslProvider)
|
||||
.setTrustAll(trustAll)
|
||||
.setTrustManagerFactoryPlugin(trustManagerFactoryPlugin)
|
||||
.createNettyClientContext();
|
||||
|
||||
Subject subject = null;
|
||||
|
|
|
@ -122,6 +122,8 @@ public class TransportConstants {
|
|||
|
||||
public static final String SSL_PROVIDER = "sslProvider";
|
||||
|
||||
public static final String TRUST_MANAGER_FACTORY_PLUGIN_PROP_NAME = "trustManagerFactoryPlugin";
|
||||
|
||||
public static final String NETTY_VERSION;
|
||||
|
||||
/**
|
||||
|
@ -218,6 +220,8 @@ public class TransportConstants {
|
|||
|
||||
public static final boolean DEFAULT_TRUST_ALL = false;
|
||||
|
||||
public static final String DEFAULT_TRUST_MANAGER_FACTORY_PLUGIN = null;
|
||||
|
||||
public static final boolean DEFAULT_FORCE_SSL_PARAMETERS = false;
|
||||
|
||||
public static final boolean DEFAULT_USE_DEFAULT_SSL_CONTEXT = false;
|
||||
|
@ -364,6 +368,7 @@ public class TransportConstants {
|
|||
allowableAcceptorKeys.add(TransportConstants.CRL_PATH_PROP_NAME);
|
||||
allowableAcceptorKeys.add(TransportConstants.HANDSHAKE_TIMEOUT);
|
||||
allowableAcceptorKeys.add(TransportConstants.SSL_PROVIDER);
|
||||
allowableAcceptorKeys.add(TransportConstants.TRUST_MANAGER_FACTORY_PLUGIN_PROP_NAME);
|
||||
allowableAcceptorKeys.add(TransportConstants.SHUTDOWN_TIMEOUT);
|
||||
allowableAcceptorKeys.add(TransportConstants.QUIET_PERIOD);
|
||||
|
||||
|
@ -413,6 +418,7 @@ public class TransportConstants {
|
|||
allowableConnectorKeys.add(TransportConstants.NETTY_CONNECT_TIMEOUT);
|
||||
allowableConnectorKeys.add(TransportConstants.USE_DEFAULT_SSL_CONTEXT_PROP_NAME);
|
||||
allowableConnectorKeys.add(TransportConstants.SSL_PROVIDER);
|
||||
allowableConnectorKeys.add(TransportConstants.TRUST_MANAGER_FACTORY_PLUGIN_PROP_NAME);
|
||||
allowableConnectorKeys.add(TransportConstants.HANDSHAKE_TIMEOUT);
|
||||
allowableConnectorKeys.add(TransportConstants.CRL_PATH_PROP_NAME);
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ import io.netty.handler.ssl.SslContext;
|
|||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import org.apache.activemq.artemis.api.core.TrustManagerFactoryPlugin;
|
||||
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
|
||||
import org.apache.activemq.artemis.utils.ClassloadingUtil;
|
||||
|
||||
|
@ -63,6 +64,7 @@ public class SSLSupport {
|
|||
private String crlPath = TransportConstants.DEFAULT_CRL_PATH;
|
||||
private String sslProvider = TransportConstants.DEFAULT_SSL_PROVIDER;
|
||||
private boolean trustAll = TransportConstants.DEFAULT_TRUST_ALL;
|
||||
private String trustManagerFactoryPlugin = TransportConstants.DEFAULT_TRUST_MANAGER_FACTORY_PLUGIN;
|
||||
|
||||
public String getKeystoreProvider() {
|
||||
return keystoreProvider;
|
||||
|
@ -145,6 +147,15 @@ public class SSLSupport {
|
|||
return this;
|
||||
}
|
||||
|
||||
public String getTrustManagerFactoryPlugin() {
|
||||
return trustManagerFactoryPlugin;
|
||||
}
|
||||
|
||||
public SSLSupport setTrustManagerFactoryPlugin(String trustManagerFactoryPlugin) {
|
||||
this.trustManagerFactoryPlugin = trustManagerFactoryPlugin;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SSLContext createContext() throws Exception {
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
KeyManager[] keyManagers = loadKeyManagers();
|
||||
|
@ -190,7 +201,9 @@ public class SSLSupport {
|
|||
|
||||
// Private -------------------------------------------------------
|
||||
private TrustManagerFactory loadTrustManagerFactory() throws Exception {
|
||||
if (trustAll) {
|
||||
if (trustManagerFactoryPlugin != null) {
|
||||
return AccessController.doPrivileged((PrivilegedAction<TrustManagerFactory>) () -> ((TrustManagerFactoryPlugin) ClassloadingUtil.newInstanceFromClassLoader(SSLSupport.class, trustManagerFactoryPlugin)).getTrustManagerFactory());
|
||||
} else if (trustAll) {
|
||||
//This is useful for testing but not should be used outside of that purpose
|
||||
return InsecureTrustManagerFactory.INSTANCE;
|
||||
} else if (truststorePath == null && (truststoreProvider == null || !"PKCS11".equals(truststoreProvider.toUpperCase()))) {
|
||||
|
|
|
@ -177,6 +177,8 @@ public class NettyAcceptor extends AbstractAcceptor {
|
|||
|
||||
private final boolean verifyHost;
|
||||
|
||||
private final String trustManagerFactoryPlugin;
|
||||
|
||||
private final String kerb5Config;
|
||||
|
||||
private String sniHost;
|
||||
|
@ -303,6 +305,8 @@ public class NettyAcceptor extends AbstractAcceptor {
|
|||
sslProvider = ConfigurationHelper.getStringProperty(TransportConstants.SSL_PROVIDER, TransportConstants.DEFAULT_SSL_PROVIDER, configuration);
|
||||
|
||||
sniHost = ConfigurationHelper.getStringProperty(TransportConstants.SNIHOST_PROP_NAME, TransportConstants.DEFAULT_SNIHOST_CONFIG, configuration);
|
||||
|
||||
trustManagerFactoryPlugin = ConfigurationHelper.getStringProperty(TransportConstants.TRUST_MANAGER_FACTORY_PLUGIN_PROP_NAME, TransportConstants.DEFAULT_TRUST_MANAGER_FACTORY_PLUGIN, configuration);
|
||||
} else {
|
||||
keyStoreProvider = TransportConstants.DEFAULT_KEYSTORE_PROVIDER;
|
||||
keyStorePath = TransportConstants.DEFAULT_KEYSTORE_PATH;
|
||||
|
@ -318,6 +322,7 @@ public class NettyAcceptor extends AbstractAcceptor {
|
|||
verifyHost = TransportConstants.DEFAULT_VERIFY_HOST;
|
||||
sslProvider = TransportConstants.DEFAULT_SSL_PROVIDER;
|
||||
sniHost = TransportConstants.DEFAULT_SNIHOST_CONFIG;
|
||||
trustManagerFactoryPlugin = TransportConstants.DEFAULT_TRUST_MANAGER_FACTORY_PLUGIN;
|
||||
}
|
||||
|
||||
tcpNoDelay = ConfigurationHelper.getBooleanProperty(TransportConstants.TCP_NODELAY_PROPNAME, TransportConstants.DEFAULT_TCP_NODELAY, configuration);
|
||||
|
@ -580,6 +585,7 @@ public class NettyAcceptor extends AbstractAcceptor {
|
|||
.setTruststorePath(trustStorePath)
|
||||
.setTruststorePassword(trustStorePassword)
|
||||
.setCrlPath(crlPath)
|
||||
.setTrustManagerFactoryPlugin(trustManagerFactoryPlugin)
|
||||
.createContext();
|
||||
} catch (Exception e) {
|
||||
IllegalStateException ise = new IllegalStateException("Unable to create NettyAcceptor for " + host + ":" + port);
|
||||
|
@ -619,6 +625,7 @@ public class NettyAcceptor extends AbstractAcceptor {
|
|||
.setTruststorePath(trustStorePath)
|
||||
.setTruststorePassword(trustStorePassword)
|
||||
.setSslProvider(sslProvider)
|
||||
.setTrustManagerFactoryPlugin(trustManagerFactoryPlugin)
|
||||
.createNettyContext();
|
||||
} catch (Exception e) {
|
||||
IllegalStateException ise = new IllegalStateException("Unable to create NettyAcceptor for " + host + ":" + port);
|
||||
|
|
|
@ -472,6 +472,23 @@ additional properties:
|
|||
When used on a `connector` the `sniHost` value is used for the `server_name`
|
||||
extension on the SSL connection.
|
||||
|
||||
- `trustManagerFactoryPlugin`
|
||||
|
||||
This is valid on either an `acceptor` or `connector`. It defines the name
|
||||
of the class which implements `org.apache.activemq.artemis.api.core.TrustManagerFactoryPlugin`.
|
||||
This is a simple interface with a single method which returns a
|
||||
`javax.net.ssl.TrustManagerFactory`. The `TrustManagerFactory` will be used
|
||||
when the underlying `javax.net.ssl.SSLContext` is initialized. This allows
|
||||
fine-grained customization of who/what the broker & client trusts.
|
||||
|
||||
This value takes precedence of all other SSL parameters which apply to the
|
||||
trust manager (i.e. `trustAll`, `truststoreProvider`, `truststorePath`,
|
||||
`truststorePassword`, `crlPath`).
|
||||
|
||||
Any plugin specified will need to be placed on the
|
||||
[broker's classpath](using-server.md#adding-runtime-dependencies).
|
||||
|
||||
|
||||
### Configuring Netty HTTP
|
||||
|
||||
Netty HTTP tunnels packets over the HTTP protocol. It can be useful in
|
||||
|
|
|
@ -255,6 +255,36 @@ public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase {
|
|||
Assert.assertEquals(text, m.getBodyBuffer().readString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneWaySSLwithTrustManagerPlugin() throws Exception {
|
||||
createCustomSslServer(null, null, false, null, TestTrustManagerFactoryPlugin.class.getName());
|
||||
String text = RandomUtil.randomString();
|
||||
|
||||
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
||||
tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType);
|
||||
tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE);
|
||||
tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD);
|
||||
|
||||
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc));
|
||||
ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator));
|
||||
|
||||
assertTrue(TestTrustManagerFactoryPlugin.triggered.get());
|
||||
|
||||
ClientSession session = addClientSession(sf.createSession(false, true, true));
|
||||
session.createQueue(CoreClientOverOneWaySSLTest.QUEUE, CoreClientOverOneWaySSLTest.QUEUE, false);
|
||||
ClientProducer producer = addClientProducer(session.createProducer(CoreClientOverOneWaySSLTest.QUEUE));
|
||||
|
||||
ClientMessage message = createTextMessage(session, text);
|
||||
producer.send(message);
|
||||
|
||||
ClientConsumer consumer = addClientConsumer(session.createConsumer(CoreClientOverOneWaySSLTest.QUEUE));
|
||||
session.start();
|
||||
|
||||
ClientMessage m = consumer.receive(1000);
|
||||
Assert.assertNotNull(m);
|
||||
Assert.assertEquals(text, m.getBodyBuffer().readString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneWaySSLwithURL() throws Exception {
|
||||
createCustomSslServer();
|
||||
|
@ -880,6 +910,14 @@ public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase {
|
|||
String protocols,
|
||||
boolean useVerifiedKeystore,
|
||||
String sniHost) throws Exception {
|
||||
createCustomSslServer(null, null, useVerifiedKeystore, null, null);
|
||||
}
|
||||
|
||||
private void createCustomSslServer(String cipherSuites,
|
||||
String protocols,
|
||||
boolean useVerifiedKeystore,
|
||||
String sniHost,
|
||||
String trustManagerFactoryPlugin) throws Exception {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
||||
params.put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType);
|
||||
|
@ -904,6 +942,10 @@ public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase {
|
|||
params.put(TransportConstants.ENABLED_PROTOCOLS_PROP_NAME, protocols);
|
||||
}
|
||||
|
||||
if (trustManagerFactoryPlugin != null) {
|
||||
params.put(TransportConstants.TRUST_MANAGER_FACTORY_PLUGIN_PROP_NAME, trustManagerFactoryPlugin);
|
||||
}
|
||||
|
||||
ConfigurationImpl config = createBasicConfig().addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, params, "nettySSL"));
|
||||
server = createServer(false, config);
|
||||
server.start();
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.tests.integration.ssl;
|
||||
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import org.apache.activemq.artemis.api.core.TrustManagerFactoryPlugin;
|
||||
|
||||
public class TestTrustManagerFactoryPlugin implements TrustManagerFactoryPlugin {
|
||||
|
||||
public static AtomicBoolean triggered = new AtomicBoolean(false);
|
||||
|
||||
@Override
|
||||
public TrustManagerFactory getTrustManagerFactory() {
|
||||
triggered.set(true);
|
||||
return InsecureTrustManagerFactory.INSTANCE;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue