/*
* 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.internal.cli.commands;
import static org.apache.geode.distributed.ConfigurationProperties.*;
import static org.apache.geode.management.internal.cli.i18n.CliStrings.*;
import static org.apache.geode.util.test.TestUtil.*;
import static org.junit.Assert.*;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.Properties;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.apache.geode.management.internal.cli.HeadlessGfsh;
import org.apache.geode.management.internal.cli.result.CommandResult;
import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
import org.apache.geode.test.junit.categories.DistributedTest;
import org.apache.geode.test.junit.categories.SecurityTest;
import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
/**
* @since GemFire 8.1
*/
@Category({DistributedTest.class, SecurityTest.class})
@RunWith(Parameterized.class)
@Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)
public class ConnectCommandWithHttpAndSSLDUnitTest extends CliCommandTestBase {
private static final ThreadLocal<Properties> sslInfoHolder = new ThreadLocal<>();
private File jks;
@Parameterized.Parameter
public String urlContext;
@Parameterized.Parameters
public static Collection<String> data() {
return Arrays.asList("/geode-mgmt", "/gemfire");
}
@Override
public final void postSetUpCliCommandTestBase() throws Exception {
this.jks = new File(getResourcePath(getClass(), "/ssl/trusted.keystore"));
}
@Override
protected final void preTearDownCliCommandTestBase() throws Exception {
destroyDefaultSetup();
}
@Override
public final void postTearDownCacheTestCase() throws Exception {
sslInfoHolder.set(null);
}
@Test
public void testMutualAuthentication() throws Exception {
Properties serverProps = new Properties();
serverProps.setProperty(HTTP_SERVICE_SSL_ENABLED, "true");
serverProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE, jks.getCanonicalPath());
serverProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE_PASSWORD, "password");
serverProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE_TYPE, "JKS");
serverProps.setProperty(HTTP_SERVICE_SSL_PROTOCOLS, "SSL");
serverProps.setProperty(HTTP_SERVICE_SSL_REQUIRE_AUTHENTICATION, "true");
serverProps.setProperty(HTTP_SERVICE_SSL_TRUSTSTORE, jks.getCanonicalPath());
serverProps.setProperty(HTTP_SERVICE_SSL_TRUSTSTORE_PASSWORD, "password");
Properties clientProps = new Properties();
clientProps.setProperty(CONNECT__KEY_STORE, jks.getCanonicalPath());
clientProps.setProperty(CONNECT__KEY_STORE_PASSWORD, "password");
clientProps.setProperty(CONNECT__SSL_PROTOCOLS, "SSL");
clientProps.setProperty(CONNECT__TRUST_STORE, jks.getCanonicalPath());
clientProps.setProperty(CONNECT__TRUST_STORE_PASSWORD, "password");
sslInfoHolder.set(clientProps);
setUpJmxManagerOnVm0ThenConnect(serverProps);
}
@Test
public void testSimpleSSL() throws Exception {
Properties serverProps = new Properties();
serverProps.setProperty(HTTP_SERVICE_SSL_ENABLED, "true");
serverProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE, jks.getCanonicalPath());
serverProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE_PASSWORD, "password");
serverProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE_TYPE, "JKS");
Properties clientProps = new Properties();
clientProps.setProperty(CONNECT__TRUST_STORE, jks.getCanonicalPath());
clientProps.setProperty(CONNECT__TRUST_STORE_PASSWORD, "password");
sslInfoHolder.set(clientProps);
setUpJmxManagerOnVm0ThenConnect(serverProps);
}
@Test
public void testSSLWithoutKeyStoreType() throws Exception {
Properties localProps = new Properties();
localProps.setProperty(HTTP_SERVICE_SSL_ENABLED, "true");
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE, jks.getCanonicalPath());
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE_PASSWORD, "password");
Properties clientProps = new Properties();
clientProps.setProperty(CONNECT__TRUST_STORE, jks.getCanonicalPath());
clientProps.setProperty(CONNECT__TRUST_STORE_PASSWORD, "password");
sslInfoHolder.set(clientProps);
setUpJmxManagerOnVm0ThenConnect(localProps);
}
@Test
public void testSSLWithSSLProtocol() throws Exception {
Properties localProps = new Properties();
localProps.setProperty(HTTP_SERVICE_SSL_ENABLED, "true");
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE, jks.getCanonicalPath());
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE_PASSWORD, "password");
localProps.setProperty(HTTP_SERVICE_SSL_PROTOCOLS, "SSL");
Properties clientProps = new Properties();
clientProps.setProperty(CONNECT__TRUST_STORE, jks.getCanonicalPath());
clientProps.setProperty(CONNECT__TRUST_STORE_PASSWORD, "password");
sslInfoHolder.set(clientProps);
setUpJmxManagerOnVm0ThenConnect(localProps);
}
@Test
public void testSSLWithTLSProtocol() throws Exception {
Properties localProps = new Properties();
localProps.setProperty(HTTP_SERVICE_SSL_ENABLED, "true");
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE, jks.getCanonicalPath());
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE_PASSWORD, "password");
localProps.setProperty(HTTP_SERVICE_SSL_PROTOCOLS, "TLS");
Properties clientProps = new Properties();
clientProps.setProperty(CONNECT__TRUST_STORE, jks.getCanonicalPath());
clientProps.setProperty(CONNECT__TRUST_STORE_PASSWORD, "password");
sslInfoHolder.set(clientProps);
setUpJmxManagerOnVm0ThenConnect(localProps);
}
@Test
public void testSSLWithTLSv11Protocol() throws Exception {
Properties localProps = new Properties();
localProps.setProperty(HTTP_SERVICE_SSL_ENABLED, "true");
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE, jks.getCanonicalPath());
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE_PASSWORD, "password");
localProps.setProperty(HTTP_SERVICE_SSL_PROTOCOLS, "TLSv1.1");
Properties clientProps = new Properties();
clientProps.setProperty(CONNECT__TRUST_STORE, jks.getCanonicalPath());
clientProps.setProperty(CONNECT__TRUST_STORE_PASSWORD, "password");
sslInfoHolder.set(clientProps);
setUpJmxManagerOnVm0ThenConnect(localProps);
}
@Test
public void testSSLWithTLSv12Protocol() throws Exception {
Properties localProps = new Properties();
localProps.setProperty(HTTP_SERVICE_SSL_ENABLED, "true");
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE, jks.getCanonicalPath());
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE_PASSWORD, "password");
localProps.setProperty(HTTP_SERVICE_SSL_PROTOCOLS, "TLSv1.2");
Properties clientProps = new Properties();
clientProps.setProperty(CONNECT__TRUST_STORE, jks.getCanonicalPath());
clientProps.setProperty(CONNECT__TRUST_STORE_PASSWORD, "password");
sslInfoHolder.set(clientProps);
setUpJmxManagerOnVm0ThenConnect(localProps);
}
@Test
public void testWithMultipleProtocol() throws Exception {
Properties localProps = new Properties();
localProps.setProperty(HTTP_SERVICE_SSL_ENABLED, "true");
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE, jks.getCanonicalPath());
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE_PASSWORD, "password");
localProps.setProperty(HTTP_SERVICE_SSL_PROTOCOLS, "SSL,TLSv1.2");
Properties clientProps = new Properties();
clientProps.setProperty(CONNECT__TRUST_STORE, jks.getCanonicalPath());
clientProps.setProperty(CONNECT__TRUST_STORE_PASSWORD, "password");
sslInfoHolder.set(clientProps);
setUpJmxManagerOnVm0ThenConnect(localProps);
}
@Ignore("TODO: disabled for unknown reason")
@Test
public void testSSLWithCipherSuite() throws Exception {
Properties localProps = new Properties();
localProps.setProperty(HTTP_SERVICE_SSL_ENABLED, "true");
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE, jks.getCanonicalPath());
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE_PASSWORD, "password");
localProps.setProperty(HTTP_SERVICE_SSL_PROTOCOLS, "TLSv1.2");
// Its bad to hard code here. But using SocketFactory.getDefaultCiphers() somehow is not working
// with the option
// "https.cipherSuites" which is required to restrict cipher suite with HttpsURLConnection
// Keeping the below code for further investigation on different Java versions ( 7 & 8) @TODO
/*
* SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
*
* sslContext.init(null, null, new java.security.SecureRandom()); String[] cipherSuites =
* sslContext.getSocketFactory().getSupportedCipherSuites();
*/
localProps.setProperty(HTTP_SERVICE_SSL_CIPHERS, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256");
Properties clientProps = new Properties();
clientProps.setProperty(CONNECT__TRUST_STORE, jks.getCanonicalPath());
clientProps.setProperty(CONNECT__TRUST_STORE_PASSWORD, "password");
clientProps.setProperty(CONNECT__SSL_CIPHERS, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256");
clientProps.setProperty(CONNECT__SSL_PROTOCOLS, "TLSv1.2");
sslInfoHolder.set(clientProps);
setUpJmxManagerOnVm0ThenConnect(localProps);
}
@Ignore("TODO: disabled for unknown reason")
@Test
public void testSSLWithMultipleCipherSuite() throws Exception {
System.setProperty("javax.net.debug", "ssl,handshake,failure");
Properties localProps = new Properties();
localProps.setProperty(HTTP_SERVICE_SSL_ENABLED, "true");
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE, jks.getCanonicalPath());
localProps.setProperty(HTTP_SERVICE_SSL_KEYSTORE_PASSWORD, "password");
localProps.setProperty(HTTP_SERVICE_SSL_PROTOCOLS, "TLSv1.2");
localProps.setProperty(HTTP_SERVICE_SSL_CIPHERS,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_EMPTY_RENEGOTIATION_INFO_SCSV");
Properties clientProps = new Properties();
clientProps.setProperty(CONNECT__TRUST_STORE, jks.getCanonicalPath());
clientProps.setProperty(CONNECT__TRUST_STORE_PASSWORD, "password");
clientProps.setProperty(CONNECT__SSL_PROTOCOLS, "TLSv1.2");
sslInfoHolder.set(clientProps);
setUpJmxManagerOnVm0ThenConnect(localProps);
}
@Override
protected void connect(final String host, final int jmxPort, final int httpPort,
final HeadlessGfsh shell) {
assertNotNull(host);
assertNotNull(shell);
final CommandStringBuilder command = new CommandStringBuilder(CONNECT);
String endpoint;
// This is for testing purpose only. If we remove this piece of code we will
// get a java.security.cert.CertificateException
// as matching hostname can not be obtained in all test environment.
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String string, SSLSession ssls) {
return true;
}
});
endpoint = "https://" + host + ":" + httpPort + urlContext + "/v1";
command.addOption(CONNECT__USE_HTTP, Boolean.TRUE.toString());
command.addOption(CONNECT__URL, endpoint);
command.addOption(CONNECT__USE_SSL, Boolean.TRUE.toString());
if (sslInfoHolder.get().getProperty(CONNECT__KEY_STORE) != null) {
command.addOption(CONNECT__KEY_STORE, sslInfoHolder.get().getProperty(CONNECT__KEY_STORE));
}
if (sslInfoHolder.get().getProperty(CONNECT__KEY_STORE_PASSWORD) != null) {
command.addOption(CONNECT__KEY_STORE_PASSWORD,
sslInfoHolder.get().getProperty(CONNECT__KEY_STORE_PASSWORD));
}
if (sslInfoHolder.get().getProperty(CONNECT__TRUST_STORE) != null) {
command.addOption(CONNECT__TRUST_STORE,
sslInfoHolder.get().getProperty(CONNECT__TRUST_STORE));
}
if (sslInfoHolder.get().getProperty(CONNECT__TRUST_STORE_PASSWORD) != null) {
command.addOption(CONNECT__TRUST_STORE_PASSWORD,
sslInfoHolder.get().getProperty(CONNECT__TRUST_STORE_PASSWORD));
}
if (sslInfoHolder.get().getProperty(CONNECT__SSL_PROTOCOLS) != null) {
command.addOption(CONNECT__SSL_PROTOCOLS,
sslInfoHolder.get().getProperty(CONNECT__SSL_PROTOCOLS));
}
if (sslInfoHolder.get().getProperty(CONNECT__SSL_CIPHERS) != null) {
command.addOption(CONNECT__SSL_CIPHERS,
sslInfoHolder.get().getProperty(CONNECT__SSL_CIPHERS));
}
CommandResult result = executeCommand(shell, command.toString());
if (!shell.isConnectedAndReady()) {
fail("Connect command failed to connect to manager " + endpoint + " result="
+ commandResultToString(result));
}
info("Successfully connected to managing node using HTTPS");
assertEquals(true, shell.isConnectedAndReady());
}
}