From 51a2c3e1d08baf72ebcd3bc54c11a88e92ebe7e3 Mon Sep 17 00:00:00 2001 From: Srikanth Manvi Date: Mon, 20 May 2019 12:28:20 -0400 Subject: [PATCH] SGF-852 - Add support to EnableSsl for ssl-use-default-context and ssl-endpoint-identification-enabled. --- .../gemfire/config/annotation/EnableSsl.java | 23 ++ .../config/annotation/SslConfiguration.java | 9 +- ...urationDefaultContextIntegrationTests.java | 315 ++++++++++++++++++ .../EnableSslConfigurationUnitTests.java | 13 +- 4 files changed, 357 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/springframework/data/gemfire/config/annotation/EnableSslConfigurationDefaultContextIntegrationTests.java diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/EnableSsl.java b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableSsl.java index 04e4362d..d9724ff5 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/EnableSsl.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableSsl.java @@ -200,6 +200,29 @@ public @interface EnableSsl { */ boolean webRequireAuthentication() default false; + /** + * If {@literal true} then allows the use of default SSL context and sets + * ssl-endpoint-identification-enabled to true. + * + * Defaults to {@literal false}. + * + * Use the {@literal spring.data.gemfire.security.ssl.use-default-context} + * in {@literal application.properties}. + */ + boolean sslUseDefaultContext() default false; + + /** + * If {@literal true} clients (GemFire servers in cause of p2p) to validate server's + * hostname using server`s certificate. + * + * Defaults to {@literal false}. Set to {@literal true} if + * {@literal useSSLDefaultDefaultContext} is true. + * + * Use the {@literal spring.data.gemfire.security.ssl.endpoint-identification-enabled} + * in {@literal application.properties}. + */ + boolean sslEndpointIdentificationEnabled() default false; + enum Component { ALL(SecurableCommunicationChannels.ALL), diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/SslConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/SslConfiguration.java index ef0d1f2a..13fa5e74 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/SslConfiguration.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/SslConfiguration.java @@ -103,7 +103,14 @@ public class SslConfiguration extends EmbeddedServiceConfigurationSupport { .setProperty("ssl-web-require-authentication", resolveProperty(sslProperty("web-require-authentication"), - annotationAttributes.getBoolean("webRequireAuthentication"))); + annotationAttributes.getBoolean("webRequireAuthentication"))) + + .setProperty("ssl-use-default-context", resolveProperty(sslProperty("use-default-context"), + annotationAttributes.getBoolean("sslUseDefaultContext"))) + + .setProperty("ssl-endpoint-identification-enabled", resolveProperty(sslProperty("endpoint-identification-enabled"), + annotationAttributes.getBoolean("sslEndpointIdentificationEnabled"))); + configureComponentCertificateAliases(annotationAttributes, gemfireProperties); diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableSslConfigurationDefaultContextIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableSslConfigurationDefaultContextIntegrationTests.java new file mode 100644 index 00000000..cf92249d --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableSslConfigurationDefaultContextIntegrationTests.java @@ -0,0 +1,315 @@ +/* + * Copyright 2018-2019 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.data.gemfire.config.annotation; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Optional; +import java.util.Properties; + +import org.junit.After; +import org.junit.Test; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.data.gemfire.util.ArrayUtils; +import org.springframework.mock.env.MockPropertySource; +import org.springframework.util.StringUtils; + +import org.apache.geode.cache.GemFireCache; + +/** + * Integration tests for {@link EnableSsl} and {@link SslConfiguration}. + * + * Integration tests in this class create a ClientCache or PeerCache and query Gemfire + * to see if the GemFire properties set via spring-data-geode are correctly set in GemFire. + * + * @see Test + * @see org.apache.geode.cache.GemFireCache + * @see EnableSsl + * @see SslConfiguration + * @since 2.1.0 + */ +public class EnableSslConfigurationDefaultContextIntegrationTests { + + private ConfigurableApplicationContext applicationContext; + + @After + public void tearDown() { + Optional.ofNullable(this.applicationContext).ifPresent(ConfigurableApplicationContext::close); + } + + private ConfigurableApplicationContext newApplicationContext(PropertySource testPropertySource, + Class... annotatedClasses) { + + AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); + + MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources(); + + propertySources.addFirst(testPropertySource); + + applicationContext.register(annotatedClasses); + applicationContext.registerShutdownHook(); + applicationContext.refresh(); + + return applicationContext; + } + + @Test + public void sslAnnotationBasedClientConfigurationIsCorrect() { + + this.applicationContext = newApplicationContext(new MockPropertySource("TestPropertySource"), + SslAnnotationBasedClientConfiguration.class); + + assertThat(this.applicationContext).isNotNull(); + assertThat(this.applicationContext.containsBean("gemfireCache")); + assertThat(this.applicationContext.containsBean("gemfireProperties")); + + GemFireCache clientCache = + this.applicationContext.getBean("gemfireCache", GemFireCache.class); + + //Get Properties from GemFire + Properties gemfireProperties = clientCache.getDistributedSystem().getProperties(); + + assertThat(clientCache).isNotNull(); + + assertGemFirePropertiesCorrectlySet(gemfireProperties); + } + + @Test + public void sslPropertyBasedClientConfigurationIsCorrect() { + + PropertySource testPropertySource = setSdgProperties(); + + this.applicationContext = newApplicationContext(testPropertySource, SslPropertyBasedClientConfiguration.class); + + assertThat(this.applicationContext).isNotNull(); + assertThat(this.applicationContext.containsBean("gemfireCache")); + assertThat(this.applicationContext.containsBean("gemfireProperties")); + + GemFireCache clientCache = + this.applicationContext.getBean("gemfireCache", GemFireCache.class); + + assertThat(clientCache).isNotNull(); + + //Get Properties from GemFire + Properties gemfireProperties = clientCache.getDistributedSystem().getProperties(); + + String sslEnabledComponents = Optional.ofNullable(gemfireProperties.getProperty("ssl-enabled-components")) + .filter(StringUtils::hasText) + .map(it -> StringUtils.arrayToCommaDelimitedString( + ArrayUtils.sort(StringUtils.commaDelimitedListToStringArray(it)))) + .orElse(null); + + assertThat(gemfireProperties).isNotNull(); + assertThat(gemfireProperties.getProperty("ssl-ciphers")).isEqualTo("Scream SEAL SNOW"); + assertThat(sslEnabledComponents).isEqualTo("gateway,locator,server"); + assertThat(gemfireProperties.getProperty("ssl-default-alias")).isEqualTo("MockCert"); + assertThat(gemfireProperties.getProperty("ssl-gateway-alias")).isEqualTo("WanCert"); + assertThat(gemfireProperties.getProperty("ssl-server-alias")).isEqualTo("ServerCert"); + assertThat(gemfireProperties.getProperty("ssl-keystore")).isEqualTo("~/test/app/keystore.jks"); + assertThat(gemfireProperties.getProperty("ssl-keystore-password")).isEqualTo("0p3nS@y5M3"); + assertThat(gemfireProperties.getProperty("ssl-keystore-type")).isEqualTo("R2D2"); + assertThat(gemfireProperties.getProperty("ssl-protocols")).isEqualTo("IP TCP/IP UDP"); + assertThat(gemfireProperties.getProperty("ssl-require-authentication")).isEqualTo("false"); + assertThat(gemfireProperties.getProperty("ssl-truststore")).isEqualTo("relative/path/to/trusted.keystore"); + assertThat(gemfireProperties.getProperty("ssl-truststore-password")).isEqualTo("kn0ckKn0ck"); + assertThat(gemfireProperties.getProperty("ssl-truststore-type")).isEqualTo("C3PO"); + assertThat(gemfireProperties.getProperty("ssl-web-require-authentication")).isEqualTo("true"); + assertThat(gemfireProperties.getProperty("ssl-use-default-context")).isEqualTo("true"); + assertThat(gemfireProperties.getProperty("ssl-endpoint-identification-enabled")).isEqualTo("true"); + } + + @Test + public void sslAnnotationBasedPeerConfigurationIsCorrect(){ + + this.applicationContext = newApplicationContext(new MockPropertySource("TestPropertySource"), + SslAnnotationBasedPeerConfiguration.class); + + assertThat(this.applicationContext).isNotNull(); + assertThat(this.applicationContext.containsBean("gemfireCache")); + assertThat(this.applicationContext.containsBean("gemfireProperties")); + + GemFireCache peerCache = + this.applicationContext.getBean("gemfireCache", GemFireCache.class); + assertThat(peerCache).isNotNull(); + + //Get Properties from GemFire + Properties gemfireProperties = peerCache.getDistributedSystem().getProperties(); + assertGemFirePropertiesCorrectlySet(gemfireProperties); + } + + @Test + public void sslPropertyBasedPeerConfigurationIsCorrect() { + + PropertySource testPropertySource = setSdgProperties(); + + this.applicationContext = newApplicationContext(testPropertySource, SslPropertyBasedPeerConfiguration.class); + + assertThat(this.applicationContext).isNotNull(); + assertThat(this.applicationContext.containsBean("gemfireCache")); + assertThat(this.applicationContext.containsBean("gemfireProperties")); + + GemFireCache peerCache = + this.applicationContext.getBean("gemfireCache", GemFireCache.class); + + assertThat(peerCache).isNotNull(); + + //Get Properties from GemFire + Properties gemfireProperties = peerCache.getDistributedSystem().getProperties(); + + String sslEnabledComponents = Optional.ofNullable(gemfireProperties.getProperty("ssl-enabled-components")) + .filter(StringUtils::hasText) + .map(it -> StringUtils.arrayToCommaDelimitedString( + ArrayUtils.sort(StringUtils.commaDelimitedListToStringArray(it)))) + .orElse(null); + + assertThat(gemfireProperties).isNotNull(); + assertThat(gemfireProperties.getProperty("ssl-ciphers")).isEqualTo("Scream SEAL SNOW"); + assertThat(sslEnabledComponents).isEqualTo("gateway,locator,server"); + assertThat(gemfireProperties.getProperty("ssl-default-alias")).isEqualTo("MockCert"); + assertThat(gemfireProperties.getProperty("ssl-gateway-alias")).isEqualTo("WanCert"); + assertThat(gemfireProperties.getProperty("ssl-server-alias")).isEqualTo("ServerCert"); + assertThat(gemfireProperties.getProperty("ssl-keystore")).isEqualTo("~/test/app/keystore.jks"); + assertThat(gemfireProperties.getProperty("ssl-keystore-password")).isEqualTo("0p3nS@y5M3"); + assertThat(gemfireProperties.getProperty("ssl-keystore-type")).isEqualTo("R2D2"); + assertThat(gemfireProperties.getProperty("ssl-protocols")).isEqualTo("IP TCP/IP UDP"); + assertThat(gemfireProperties.getProperty("ssl-require-authentication")).isEqualTo("false"); + assertThat(gemfireProperties.getProperty("ssl-truststore")).isEqualTo("relative/path/to/trusted.keystore"); + assertThat(gemfireProperties.getProperty("ssl-truststore-password")).isEqualTo("kn0ckKn0ck"); + assertThat(gemfireProperties.getProperty("ssl-truststore-type")).isEqualTo("C3PO"); + assertThat(gemfireProperties.getProperty("ssl-web-require-authentication")).isEqualTo("true"); + assertThat(gemfireProperties.getProperty("ssl-use-default-context")).isEqualTo("true"); + assertThat(gemfireProperties.getProperty("ssl-endpoint-identification-enabled")).isEqualTo("true"); + } + + + @ClientCacheApplication(logLevel = "error") + @EnableSsl( + ciphers = { "FISH", "Scream", "SEAL", "SNOW" }, + components = { EnableSsl.Component.SERVER, EnableSsl.Component.GATEWAY }, + componentCertificateAliases = { + @EnableSsl.ComponentAlias(component = EnableSsl.Component.GATEWAY, alias = "WanCert") + }, + defaultCertificateAlias = "TestCert", + keystore = "/path/to/keystore.jks", + keystorePassword = "s3cr3t!", + protocols = { "TCP/IP", "HTTP" }, + truststore = "/path/to/truststore.jks", + truststorePassword = "p@55w0rd!", + truststoreType = "PKCS11", + webRequireAuthentication = true, + sslUseDefaultContext = true, + sslEndpointIdentificationEnabled = true) + static class SslAnnotationBasedClientConfiguration { } + + @ClientCacheApplication(logLevel = "error") + @EnableSsl + static class SslPropertyBasedClientConfiguration { } + + @PeerCacheApplication(logLevel = "error") + @EnableSsl( + ciphers = { "FISH", "Scream", "SEAL", "SNOW" }, + components = { EnableSsl.Component.SERVER, EnableSsl.Component.GATEWAY }, + componentCertificateAliases = { + @EnableSsl.ComponentAlias(component = EnableSsl.Component.GATEWAY, alias = "WanCert") + }, + defaultCertificateAlias = "TestCert", + keystore = "/path/to/keystore.jks", + keystorePassword = "s3cr3t!", + protocols = { "TCP/IP", "HTTP" }, + truststore = "/path/to/truststore.jks", + truststorePassword = "p@55w0rd!", + truststoreType = "PKCS11", + webRequireAuthentication = true, + sslUseDefaultContext = true, + sslEndpointIdentificationEnabled = true) + static class SslAnnotationBasedPeerConfiguration{ } + + @PeerCacheApplication(logLevel = "error") + @EnableSsl + static class SslPropertyBasedPeerConfiguration { } + + private void assertGemFirePropertiesCorrectlySet(Properties gemfireProperties) { + assertThat(gemfireProperties).isNotNull(); + assertThat(gemfireProperties.getProperty("ssl-ciphers")) + .isEqualTo("FISH Scream SEAL SNOW"); + assertThat(gemfireProperties.getProperty("ssl-enabled-components")) + .isEqualTo("server,gateway"); + assertThat(gemfireProperties.getProperty("ssl-default-alias")) + .isEqualTo("TestCert"); + assertThat(gemfireProperties.getProperty("ssl-gateway-alias")).isEqualTo("WanCert"); + assertThat(gemfireProperties.getProperty("ssl-keystore")) + .isEqualTo("/path/to/keystore.jks"); + assertThat(gemfireProperties.getProperty("ssl-keystore-password")) + .isEqualTo("s3cr3t!"); + assertThat(gemfireProperties.getProperty("ssl-keystore-type")).isEqualTo("JKS"); + assertThat(gemfireProperties.getProperty("ssl-protocols")).isEqualTo("TCP/IP HTTP"); + assertThat(gemfireProperties.getProperty("ssl-require-authentication")) + .isEqualTo("true"); + assertThat(gemfireProperties.getProperty("ssl-truststore")) + .isEqualTo("/path/to/truststore.jks"); + assertThat(gemfireProperties.getProperty("ssl-truststore-password")) + .isEqualTo("p@55w0rd!"); + assertThat(gemfireProperties.getProperty("ssl-truststore-type")) + .isEqualTo("PKCS11"); + assertThat(gemfireProperties.getProperty("ssl-web-require-authentication")) + .isEqualTo("true"); + assertThat(gemfireProperties.getProperty("ssl-use-default-context")) + .isEqualTo("true"); + assertThat(gemfireProperties.getProperty("ssl-endpoint-identification-enabled")) + .isEqualTo("true"); + } + + private PropertySource setSdgProperties() { + return new MockPropertySource("TestPropertySource") + .withProperty("spring.data.gemfire.security.ssl.ciphers", + "Scream,SEAL,SNOW") + .withProperty("spring.data.gemfire.security.ssl.components", + "locator, server, gateway") + .withProperty("spring.data.gemfire.security.ssl.certificate.alias.default", + "MockCert") + .withProperty("spring.data.gemfire.security.ssl.certificate.alias.gateway", + "WanCert") + .withProperty("spring.data.gemfire.security.ssl.certificate.alias.server", + "ServerCert") + .withProperty("spring.data.gemfire.security.ssl.keystore", + "~/test/app/keystore.jks") + .withProperty("spring.data.gemfire.security.ssl.keystore.password", + "0p3nS@y5M3") + .withProperty("spring.data.gemfire.security.ssl.keystore.type", + "R2D2") + .withProperty("spring.data.gemfire.security.ssl.protocols", + "IP,TCP/IP,UDP") + .withProperty("spring.data.gemfire.security.ssl.require-authentication", + "false") + .withProperty("spring.data.gemfire.security.ssl.truststore", + "relative/path/to/trusted.keystore") + .withProperty("spring.data.gemfire.security.ssl.truststore.password", + "kn0ckKn0ck") + .withProperty("spring.data.gemfire.security.ssl.truststore.type", + "C3PO") + .withProperty("spring.data.gemfire.security.ssl.web-require-authentication", + "true") + .withProperty("spring.data.gemfire.security.ssl.use-default-context", + "true") + .withProperty("spring.data.gemfire.security.ssl.endpoint-identification-enabled", + "true"); + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableSslConfigurationUnitTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableSslConfigurationUnitTests.java index 41ff6ed0..e06f21ac 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableSslConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableSslConfigurationUnitTests.java @@ -99,6 +99,8 @@ public class EnableSslConfigurationUnitTests { assertThat(gemfireProperties.getProperty("ssl-truststore-password")).isEqualTo("p@55w0rd!"); assertThat(gemfireProperties.getProperty("ssl-truststore-type")).isEqualTo("PKCS11"); assertThat(gemfireProperties.getProperty("ssl-web-require-authentication")).isEqualTo("true"); + assertThat(gemfireProperties.getProperty("ssl-use-default-context")).isEqualTo("true"); + assertThat(gemfireProperties.getProperty("ssl-endpoint-identification-enabled")).isEqualTo("true"); } @Test @@ -118,7 +120,10 @@ public class EnableSslConfigurationUnitTests { .withProperty("spring.data.gemfire.security.ssl.truststore", "relative/path/to/trusted.keystore") .withProperty("spring.data.gemfire.security.ssl.truststore.password", "kn0ckKn0ck") .withProperty("spring.data.gemfire.security.ssl.truststore.type", "C3PO") - .withProperty("spring.data.gemfire.security.ssl.web-require-authentication", "true"); + .withProperty("spring.data.gemfire.security.ssl.web-require-authentication", "true") + .withProperty("spring.data.gemfire.security.ssl.use-default-context", "true") + .withProperty("spring.data.gemfire.security.ssl.endpoint-identification-enabled", "true"); + this.applicationContext = newApplicationContext(testPropertySource, SslPropertyBasedConfiguration.class); @@ -154,6 +159,8 @@ public class EnableSslConfigurationUnitTests { assertThat(gemfireProperties.getProperty("ssl-truststore-password")).isEqualTo("kn0ckKn0ck"); assertThat(gemfireProperties.getProperty("ssl-truststore-type")).isEqualTo("C3PO"); assertThat(gemfireProperties.getProperty("ssl-web-require-authentication")).isEqualTo("true"); + assertThat(gemfireProperties.getProperty("ssl-use-default-context")).isEqualTo("true"); + assertThat(gemfireProperties.getProperty("ssl-endpoint-identification-enabled")).isEqualTo("true"); } @EnableGemFireMockObjects @@ -171,7 +178,9 @@ public class EnableSslConfigurationUnitTests { truststore = "/path/to/truststore.jks", truststorePassword = "p@55w0rd!", truststoreType = "PKCS11", - webRequireAuthentication = true + webRequireAuthentication = true, + sslUseDefaultContext = true, + sslEndpointIdentificationEnabled = true ) static class SslAnnotationBasedConfiguration { }