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_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_CLIENT_IDLE_PROP_NAME = "httpClientIdleTime";
|
||||
|
@ -396,6 +400,7 @@ public class TransportConstants {
|
|||
static {
|
||||
Set<String> allowableAcceptorKeys = new HashSet<>();
|
||||
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_SERVER_SCAN_PERIOD_PROP_NAME);
|
||||
allowableAcceptorKeys.add(TransportConstants.HTTP_UPGRADE_ENABLED_PROP_NAME);
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.io.PrintWriter;
|
|||
import java.io.StringWriter;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.AccessController;
|
||||
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.RoutingType;
|
||||
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.core.client.impl.ClientSessionFactoryImpl;
|
||||
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.ActiveMQThreadPoolExecutor;
|
||||
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.ReusableLatch;
|
||||
import org.apache.activemq.artemis.utils.SecurityFormatter;
|
||||
|
@ -220,6 +223,10 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -3361,6 +3368,25 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
|||
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()) {
|
||||
|
@ -3374,6 +3400,26 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
|||
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
|
||||
public void installMirrorController(MirrorController mirrorController) {
|
||||
logger.debug("Mirror controller is being installed");
|
||||
|
|
|
@ -291,6 +291,11 @@ sslEnabled::
|
|||
Must be `true` to enable SSL.
|
||||
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::
|
||||
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