blob: 15918e0011e59d8c0bf35860a93fa92899704709 [file] [log] [blame]
/*
* 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.geode.management;
import static org.apache.geode.distributed.ConfigurationProperties.ENABLE_CLUSTER_CONFIGURATION;
import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_PORT;
import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_SSL_CIPHERS;
import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_SSL_ENABLED;
import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_SSL_KEYSTORE;
import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_SSL_KEYSTORE_PASSWORD;
import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_SSL_KEYSTORE_TYPE;
import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_SSL_PROTOCOLS;
import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_SSL_TRUSTSTORE;
import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_SSL_TRUSTSTORE_PASSWORD;
import static org.apache.geode.distributed.ConfigurationProperties.SSL_CIPHERS;
import static org.apache.geode.distributed.ConfigurationProperties.SSL_ENABLED_COMPONENTS;
import static org.apache.geode.distributed.ConfigurationProperties.SSL_JMX_ALIAS;
import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE;
import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE_PASSWORD;
import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE_TYPE;
import static org.apache.geode.distributed.ConfigurationProperties.SSL_PROTOCOLS;
import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE;
import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE_PASSWORD;
import static org.apache.geode.test.dunit.VM.getVM;
import static org.apache.geode.test.util.ResourceUtils.createTempFileFromResource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.management.MalformedObjectNameException;
import javax.rmi.ssl.SslRMIClientSocketFactory;
import com.google.common.collect.Maps;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.RuleChain;
import org.apache.geode.internal.AvailablePortHelper;
import org.apache.geode.internal.security.SecurableCommunicationChannel;
import org.apache.geode.test.dunit.rules.CleanupDUnitVMsRule;
import org.apache.geode.test.dunit.rules.ClusterStartupRule;
import org.apache.geode.test.dunit.rules.DistributedRule;
import org.apache.geode.test.junit.categories.JMXTest;
import org.apache.geode.test.junit.rules.MBeanServerConnectionRule;
/**
* All the non-ssl enabled locators need to be in a different VM than the ssl enabled locators in
* these tests, otherwise, some tests would fail. Seems like dunit vm tear down did not clean up the
* ssl settings cleanly.
*/
@Category(JMXTest.class)
@SuppressWarnings("serial")
public class JMXMBeanDUnitTest implements Serializable {
private final ClusterStartupRule clusterStartupRule = new ClusterStartupRule();
private final transient MBeanServerConnectionRule mBeanServerConnectionRule =
new MBeanServerConnectionRule();
@Rule
public transient RuleChain ruleChain =
RuleChain.outerRule(new DistributedRule()).around(new CleanupDUnitVMsRule())
.around(clusterStartupRule).around(
mBeanServerConnectionRule);
private static Properties legacySSLProperties;
private static Properties sslProperties;
private static Properties sslPropertiesWithMultiKey;
private static String singleKeystore;
private static String multiKeystore;
private static String multiKeyTruststore;
private int jmxPort;
private Properties locatorProperties;
@BeforeClass
public static void beforeClass() {
singleKeystore =
createTempFileFromResource(JMXMBeanDUnitTest.class, "/ssl/trusted.keystore")
.getAbsolutePath();
multiKeystore =
createTempFileFromResource(JMXMBeanDUnitTest.class,
"/org/apache/geode/internal/net/multiKey.jks").getAbsolutePath();
multiKeyTruststore =
createTempFileFromResource(JMXMBeanDUnitTest.class,
"/org/apache/geode/internal/net/multiKeyTrust.jks").getAbsolutePath();
// setting up properties used to set the ssl properties used by the locators
legacySSLProperties = new Properties();
legacySSLProperties.setProperty(JMX_MANAGER_SSL_CIPHERS, "any");
legacySSLProperties.setProperty(JMX_MANAGER_SSL_PROTOCOLS, "any");
legacySSLProperties.setProperty(JMX_MANAGER_SSL_ENABLED, "true");
legacySSLProperties.setProperty(JMX_MANAGER_SSL_KEYSTORE, singleKeystore);
legacySSLProperties.setProperty(JMX_MANAGER_SSL_KEYSTORE_PASSWORD, "password");
legacySSLProperties.setProperty(JMX_MANAGER_SSL_KEYSTORE_TYPE, "JKS");
legacySSLProperties.setProperty(JMX_MANAGER_SSL_TRUSTSTORE, singleKeystore);
legacySSLProperties.setProperty(JMX_MANAGER_SSL_TRUSTSTORE_PASSWORD, "password");
sslProperties = new Properties();
sslProperties.setProperty(SSL_CIPHERS, "any");
sslProperties.setProperty(SSL_KEYSTORE_PASSWORD, "password");
sslProperties.setProperty(SSL_TRUSTSTORE_PASSWORD, "password");
sslProperties.setProperty(SSL_KEYSTORE, singleKeystore);
sslProperties.setProperty(SSL_KEYSTORE_TYPE, "JKS");
sslProperties.setProperty(SSL_TRUSTSTORE, singleKeystore);
sslProperties.setProperty(SSL_ENABLED_COMPONENTS,
SecurableCommunicationChannel.JMX.getConstant());
sslProperties.setProperty(SSL_PROTOCOLS, "TLSv1.2,TLSv1.1");
sslPropertiesWithMultiKey = new Properties();
sslPropertiesWithMultiKey.putAll(Maps.fromProperties(sslProperties));
sslPropertiesWithMultiKey.setProperty(SSL_KEYSTORE, multiKeystore);
sslPropertiesWithMultiKey.setProperty(SSL_TRUSTSTORE, multiKeyTruststore);
sslPropertiesWithMultiKey.setProperty(SSL_JMX_ALIAS, "jmxkey");
}
@Before
public void before() {
jmxPort = AvailablePortHelper.getRandomAvailableTCPPort();
locatorProperties = new Properties();
locatorProperties.put(JMX_MANAGER_PORT, jmxPort + "");
locatorProperties.setProperty(ENABLE_CLUSTER_CONFIGURATION, "false");
}
@Test
public void testJMXOverNonSSL() throws Exception {
clusterStartupRule.startLocatorVM(0, locatorProperties);
mBeanServerConnectionRule.connect(jmxPort);
validateJmxConnection(mBeanServerConnectionRule);
}
@Test
public void testJMXOverNonSSLWithClientUsingIncorrectPort() {
assertThat(jmxPort).isNotEqualTo(9999);
clusterStartupRule.startLocatorVM(0, locatorProperties);
assertThatThrownBy(() -> mBeanServerConnectionRule.connect(9999))
.hasRootCauseExactlyInstanceOf(java.net.ConnectException.class);
}
@Test
public void testJMXOverSSL() {
locatorProperties.putAll(Maps.fromProperties(sslProperties));
clusterStartupRule.startLocatorVM(0, locatorProperties);
remotelyValidateJmxConnection(false);
}
@Test
public void testJMXOverSSLWithMultiKey() {
locatorProperties.putAll(Maps.fromProperties(sslPropertiesWithMultiKey));
clusterStartupRule.startLocatorVM(0, locatorProperties);
remotelyValidateJmxConnection(true);
}
@Test
public void testJMXOverLegacySSL() {
locatorProperties.putAll(Maps.fromProperties(legacySSLProperties));
clusterStartupRule.startLocatorVM(0, locatorProperties);
remotelyValidateJmxConnection(false);
}
private void remotelyValidateJmxConnection(boolean withAlias) {
getVM(2).invoke(() -> {
beforeClass();
MBeanServerConnectionRule jmx = new MBeanServerConnectionRule();
Map<String, Object> env = getClientEnvironment(withAlias);
jmx.connect(jmxPort, env);
validateJmxConnection(jmx);
});
}
private Map<String, Object> getClientEnvironment(boolean withAlias) {
System.setProperty("javax.net.ssl.keyStore", withAlias ? multiKeystore : singleKeystore);
System.setProperty("javax.net.ssl.keyStoreType", "JKS");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
System.setProperty("javax.net.ssl.trustStore", withAlias ? multiKeyTruststore : singleKeystore);
System.setProperty("javax.net.ssl.trustStoreType", "JKS");
System.setProperty("javax.net.ssl.trustStorePassword", "password");
Map<String, Object> environment = new HashMap<>();
environment.put("com.sun.jndi.rmi.factory.socket", new SslRMIClientSocketFactory());
return environment;
}
private void validateJmxConnection(MBeanServerConnectionRule mBeanServerConnectionRule)
throws IOException, MalformedObjectNameException {
// Get MBean proxy instance that will be used to make calls to registered MBean
DistributedSystemMXBean distributedSystemMXBean =
mBeanServerConnectionRule.getProxyMXBean(DistributedSystemMXBean.class);
assertEquals(1, distributedSystemMXBean.getMemberCount());
assertEquals(1, distributedSystemMXBean.getLocatorCount());
}
}