You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by cl...@apache.org on 2019/12/19 22:54:00 UTC

[activemq-artemis] branch master updated: ARTEMIS-2580 support pluggable SSL TrustManagerFactory

This is an automated email from the ASF dual-hosted git repository.

clebertsuconic pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git


The following commit(s) were added to refs/heads/master by this push:
     new 8d8eaeb  ARTEMIS-2580 support pluggable SSL TrustManagerFactory
     new 73156cb  This closes #2923
8d8eaeb is described below

commit 8d8eaebb65b4a3be7f27fb004301b5b0e76a421c
Author: Justin Bertram <jb...@apache.org>
AuthorDate: Thu Dec 19 09:03:57 2019 -0600

    ARTEMIS-2580 support pluggable SSL TrustManagerFactory
---
 .../api/core/TrustManagerFactoryPlugin.java        | 28 +++++++++++++++
 .../core/remoting/impl/netty/NettyConnector.java   |  7 ++++
 .../remoting/impl/netty/TransportConstants.java    |  6 ++++
 .../artemis/core/remoting/impl/ssl/SSLSupport.java | 15 +++++++-
 .../core/remoting/impl/netty/NettyAcceptor.java    |  7 ++++
 docs/user-manual/en/configuring-transports.md      | 17 +++++++++
 .../ssl/CoreClientOverOneWaySSLTest.java           | 42 ++++++++++++++++++++++
 .../ssl/TestTrustManagerFactoryPlugin.java         | 36 +++++++++++++++++++
 8 files changed, 157 insertions(+), 1 deletion(-)

diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/TrustManagerFactoryPlugin.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/TrustManagerFactoryPlugin.java
new file mode 100644
index 0000000..707c698
--- /dev/null
+++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/TrustManagerFactoryPlugin.java
@@ -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();
+}
diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java
index 29cc9cd..ecccf00 100644
--- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java
+++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java
@@ -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;
diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java
index 5b06415..d9d2d12 100644
--- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java
+++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java
@@ -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);
 
diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java
index 507f8ea..9e2d319 100644
--- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java
+++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java
@@ -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()))) {
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java
index 0bc99c6..a528099 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java
@@ -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);
diff --git a/docs/user-manual/en/configuring-transports.md b/docs/user-manual/en/configuring-transports.md
index 00df421..c941735 100644
--- a/docs/user-manual/en/configuring-transports.md
+++ b/docs/user-manual/en/configuring-transports.md
@@ -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
diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverOneWaySSLTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverOneWaySSLTest.java
index c6457c6..26c84e2 100644
--- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverOneWaySSLTest.java
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverOneWaySSLTest.java
@@ -256,6 +256,36 @@ public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase {
    }
 
    @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();
       String text = RandomUtil.randomString();
@@ -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();
diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/TestTrustManagerFactoryPlugin.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/TestTrustManagerFactoryPlugin.java
new file mode 100644
index 0000000..b5391ba
--- /dev/null
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/TestTrustManagerFactoryPlugin.java
@@ -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;
+   }
+}