ARTEMIS-4164: add acceptor sslAutoReload option to have the broker watch key and trust store paths for modifications and automatically call reload
This commit is contained in:
parent
8195394eca
commit
348763e14a
|
@ -36,6 +36,10 @@ public class TransportConstants {
|
||||||
|
|
||||||
public static final String SSL_ENABLED_PROP_NAME = "sslEnabled";
|
public static final String SSL_ENABLED_PROP_NAME = "sslEnabled";
|
||||||
|
|
||||||
|
public static final String SSL_AUTO_RELOAD_PROP_NAME = "sslAutoReload";
|
||||||
|
|
||||||
|
public static final boolean DEFAULT_SSL_AUTO_RELOAD = false;
|
||||||
|
|
||||||
public static final String HTTP_ENABLED_PROP_NAME = "httpEnabled";
|
public static final String HTTP_ENABLED_PROP_NAME = "httpEnabled";
|
||||||
|
|
||||||
public static final String HTTP_CLIENT_IDLE_PROP_NAME = "httpClientIdleTime";
|
public static final String HTTP_CLIENT_IDLE_PROP_NAME = "httpClientIdleTime";
|
||||||
|
@ -396,6 +400,7 @@ public class TransportConstants {
|
||||||
static {
|
static {
|
||||||
Set<String> allowableAcceptorKeys = new HashSet<>();
|
Set<String> allowableAcceptorKeys = new HashSet<>();
|
||||||
allowableAcceptorKeys.add(TransportConstants.SSL_ENABLED_PROP_NAME);
|
allowableAcceptorKeys.add(TransportConstants.SSL_ENABLED_PROP_NAME);
|
||||||
|
allowableAcceptorKeys.add(TransportConstants.SSL_AUTO_RELOAD_PROP_NAME);
|
||||||
allowableAcceptorKeys.add(TransportConstants.HTTP_RESPONSE_TIME_PROP_NAME);
|
allowableAcceptorKeys.add(TransportConstants.HTTP_RESPONSE_TIME_PROP_NAME);
|
||||||
allowableAcceptorKeys.add(TransportConstants.HTTP_SERVER_SCAN_PERIOD_PROP_NAME);
|
allowableAcceptorKeys.add(TransportConstants.HTTP_SERVER_SCAN_PERIOD_PROP_NAME);
|
||||||
allowableAcceptorKeys.add(TransportConstants.HTTP_UPGRADE_ENABLED_PROP_NAME);
|
allowableAcceptorKeys.add(TransportConstants.HTTP_UPGRADE_ENABLED_PROP_NAME);
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
|
@ -63,6 +64,7 @@ import org.apache.activemq.artemis.api.core.Pair;
|
||||||
import org.apache.activemq.artemis.api.core.QueueConfiguration;
|
import org.apache.activemq.artemis.api.core.QueueConfiguration;
|
||||||
import org.apache.activemq.artemis.api.core.RoutingType;
|
import org.apache.activemq.artemis.api.core.RoutingType;
|
||||||
import org.apache.activemq.artemis.api.core.SimpleString;
|
import org.apache.activemq.artemis.api.core.SimpleString;
|
||||||
|
import org.apache.activemq.artemis.api.core.management.AcceptorControl;
|
||||||
import org.apache.activemq.artemis.api.core.management.ResourceNames;
|
import org.apache.activemq.artemis.api.core.management.ResourceNames;
|
||||||
import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryImpl;
|
import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryImpl;
|
||||||
import org.apache.activemq.artemis.core.config.BridgeConfiguration;
|
import org.apache.activemq.artemis.core.config.BridgeConfiguration;
|
||||||
|
@ -202,6 +204,7 @@ import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoader;
|
||||||
import org.apache.activemq.artemis.utils.ActiveMQThreadFactory;
|
import org.apache.activemq.artemis.utils.ActiveMQThreadFactory;
|
||||||
import org.apache.activemq.artemis.utils.ActiveMQThreadPoolExecutor;
|
import org.apache.activemq.artemis.utils.ActiveMQThreadPoolExecutor;
|
||||||
import org.apache.activemq.artemis.utils.CompositeAddress;
|
import org.apache.activemq.artemis.utils.CompositeAddress;
|
||||||
|
import org.apache.activemq.artemis.utils.ConfigurationHelper;
|
||||||
import org.apache.activemq.artemis.utils.ExecutorFactory;
|
import org.apache.activemq.artemis.utils.ExecutorFactory;
|
||||||
import org.apache.activemq.artemis.utils.ReusableLatch;
|
import org.apache.activemq.artemis.utils.ReusableLatch;
|
||||||
import org.apache.activemq.artemis.utils.SecurityFormatter;
|
import org.apache.activemq.artemis.utils.SecurityFormatter;
|
||||||
|
@ -220,6 +223,10 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import static java.util.stream.Collectors.groupingBy;
|
import static java.util.stream.Collectors.groupingBy;
|
||||||
|
import static org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants.DEFAULT_SSL_AUTO_RELOAD;
|
||||||
|
import static org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants.KEYSTORE_PATH_PROP_NAME;
|
||||||
|
import static org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants.SSL_AUTO_RELOAD_PROP_NAME;
|
||||||
|
import static org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants.TRUSTSTORE_PATH_PROP_NAME;
|
||||||
import static org.apache.activemq.artemis.utils.collections.IterableStream.iterableOf;
|
import static org.apache.activemq.artemis.utils.collections.IterableStream.iterableOf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3361,6 +3368,25 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
||||||
PropertiesLoader.reload();
|
PropertiesLoader.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// track tls resources on acceptors and reload via remoting server
|
||||||
|
configuration.getAcceptorConfigurations().forEach((acceptorConfig) -> {
|
||||||
|
Map<String, Object> config = acceptorConfig.getCombinedParams();
|
||||||
|
if (ConfigurationHelper.getBooleanProperty(SSL_AUTO_RELOAD_PROP_NAME, DEFAULT_SSL_AUTO_RELOAD, config)) {
|
||||||
|
URL pathUrl = fileUrlFrom(config.get(KEYSTORE_PATH_PROP_NAME));
|
||||||
|
if (pathUrl != null) {
|
||||||
|
reloadManager.addCallback(pathUrl, (uri) -> {
|
||||||
|
reloadNamedAcceptor(acceptorConfig.getName());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pathUrl = fileUrlFrom(config.get(TRUSTSTORE_PATH_PROP_NAME));
|
||||||
|
if (pathUrl != null) {
|
||||||
|
reloadManager.addCallback(pathUrl, (uri) -> {
|
||||||
|
reloadNamedAcceptor(acceptorConfig.getName());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasBrokerPlugins()) {
|
if (hasBrokerPlugins()) {
|
||||||
|
@ -3374,6 +3400,26 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void reloadNamedAcceptor(String name) {
|
||||||
|
// preference for Control to capture consistent audit logging
|
||||||
|
if (managementService != null) {
|
||||||
|
Object targetControl = managementService.getResource(ResourceNames.ACCEPTOR + name);
|
||||||
|
if (targetControl instanceof AcceptorControl) {
|
||||||
|
((AcceptorControl) targetControl).reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private URL fileUrlFrom(Object o) {
|
||||||
|
if (o instanceof String) {
|
||||||
|
try {
|
||||||
|
return new File((String) o).toURI().toURL();
|
||||||
|
} catch (MalformedURLException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installMirrorController(MirrorController mirrorController) {
|
public void installMirrorController(MirrorController mirrorController) {
|
||||||
logger.debug("Mirror controller is being installed");
|
logger.debug("Mirror controller is being installed");
|
||||||
|
|
|
@ -291,6 +291,11 @@ sslEnabled::
|
||||||
Must be `true` to enable SSL.
|
Must be `true` to enable SSL.
|
||||||
Default is `false`.
|
Default is `false`.
|
||||||
|
|
||||||
|
sslAutoReload::
|
||||||
|
Must be `true` to have the broker 'watch' an acceptors keyStorePath and/or trustStorePath and invoke reload() on update.
|
||||||
|
The watch period is controlled by xref:config-reload.adoc#configuration-reload[the configuration reload feature].
|
||||||
|
Default is `false`.
|
||||||
|
|
||||||
keyStorePath::
|
keyStorePath::
|
||||||
When used on an `acceptor` this is the path to the SSL key store on the server which holds the server's certificates (whether self-signed or signed by an authority).
|
When used on an `acceptor` this is the path to the SSL key store on the server which holds the server's certificates (whether self-signed or signed by an authority).
|
||||||
+
|
+
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* 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 java.io.File;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.activemq.artemis.api.core.TransportConfiguration;
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ServerLocator;
|
||||||
|
import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl;
|
||||||
|
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
|
||||||
|
import org.apache.activemq.artemis.core.server.ActiveMQServer;
|
||||||
|
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
|
||||||
|
import org.apache.activemq.artemis.utils.Wait;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the tests/security-resources/build.sh script for details on the security resources used.
|
||||||
|
*/
|
||||||
|
public class SSLAutoReloadTest extends ActiveMQTestBase {
|
||||||
|
|
||||||
|
private final String PASSWORD = "securepass";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOneWaySSLWithAutoReload() throws Exception {
|
||||||
|
|
||||||
|
File parentDir = new File(temporaryFolder.getRoot(), "sub");
|
||||||
|
parentDir.mkdirs();
|
||||||
|
|
||||||
|
// reference keystore from temp location that we can update
|
||||||
|
final File keyStoreToReload = new File(parentDir, "server-ks.p12");
|
||||||
|
copyRecursive(new File(this.getClass().getClassLoader().getResource("unknown-server-keystore.p12").getFile()), keyStoreToReload);
|
||||||
|
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put(TransportConstants.SSL_AUTO_RELOAD_PROP_NAME, true);
|
||||||
|
params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
||||||
|
params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, keyStoreToReload.getAbsolutePath());
|
||||||
|
params.put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD);
|
||||||
|
params.put(TransportConstants.HOST_PROP_NAME, "localhost");
|
||||||
|
|
||||||
|
ConfigurationImpl config = createBasicConfig().addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, params, "nettySSL"));
|
||||||
|
ActiveMQServer server = createServer(false, config);
|
||||||
|
server.getConfiguration().setConfigurationFileRefreshPeriod(50);
|
||||||
|
server.start();
|
||||||
|
waitForServerToStart(server);
|
||||||
|
|
||||||
|
String url = "tcp://127.0.0.1:61616?sslEnabled=true;trustStorePath=server-ca-truststore.p12;trustStorePassword=" + PASSWORD;
|
||||||
|
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocator(url)).setCallTimeout(3000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
createSessionFactory(locator);
|
||||||
|
fail("Creating session here should fail due to SSL handshake problems.");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the server side keystore
|
||||||
|
copyRecursive(new File(this.getClass().getClassLoader().getResource("server-keystore.p12").getFile()), keyStoreToReload);
|
||||||
|
|
||||||
|
// expect success after auto reload, which we wait for
|
||||||
|
Wait.waitFor(() -> {
|
||||||
|
try {
|
||||||
|
addSessionFactory(createSessionFactory(locator));
|
||||||
|
return true;
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}, 5000, 100);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue