You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by bb...@apache.org on 2020/08/19 22:50:58 UTC
[geode-native] branch develop updated: GEODE-8398: Add SNI support
to .NET API (#634)
This is an automated email from the ASF dual-hosted git repository.
bbender pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode-native.git
The following commit(s) were added to refs/heads/develop by this push:
new 557e018 GEODE-8398: Add SNI support to .NET API (#634)
557e018 is described below
commit 557e018cdc8952c51ebeb7958fc5c9c076cb20f8
Author: Michael Martell <mm...@pivotal.io>
AuthorDate: Wed Aug 19 15:50:49 2020 -0700
GEODE-8398: Add SNI support to .NET API (#634)
- incorporate SNI proxy support into native client API
- Remove redundant initialization of string (PR feedback)
- TcpSslConn ctor was getting a string "host:port" instead of just "host", and needed to split off just the hostname
- Remove hard-coded path to Docker binaries
- Update "Skip" comment for SNI tests
Co-authored-by: Blake Bender <bb...@vmware.com>
---
clicache/integration-test2/CMakeLists.txt | 1 +
clicache/integration-test2/Config.cs.in | 5 +
clicache/integration-test2/SNITests.cs | 127 +++++++++++++++++++++
clicache/src/PoolFactory.cpp | 16 +++
clicache/src/PoolFactory.hpp | 14 +++
cppcache/include/geode/PoolFactory.hpp | 5 +
cppcache/integration/framework/TestConfig.cpp.in | 1 +
cppcache/integration/framework/TestConfig.h | 2 +-
cppcache/integration/test/SNITest.cpp | 82 +++++++------
.../sni-test-config/geode-config/truststore.p12 | Bin 0 -> 8983 bytes
.../geode-config/truststore_sni.pem | 68 +++++++++++
cppcache/src/PoolAttributes.cpp | 39 +------
cppcache/src/PoolAttributes.hpp | 14 ++-
cppcache/src/PoolFactory.cpp | 21 +++-
cppcache/src/TcpSslConn.cpp | 13 ++-
cppcache/src/TcpSslConn.hpp | 21 +++-
cppcache/src/TcrConnection.cpp | 20 +++-
cppcache/src/ThinClientLocatorHelper.cpp | 28 ++++-
cppcache/src/ThinClientLocatorHelper.hpp | 5 +
cppcache/src/ThinClientPoolDM.cpp | 4 +-
cppcache/src/ThinClientPoolDM.hpp | 2 +
sni-test-config/docker-compose.yml | 43 +++++++
sni-test-config/geode-config/gemfire.properties | 19 +++
sni-test-config/geode-config/gfsecurity.properties | 27 +++++
.../geode-config/locator-maeve-keystore.jks | Bin 0 -> 2048 bytes
.../geode-config/server-clementine-keystore.jks | Bin 0 -> 2059 bytes
.../geode-config/server-dolores-keystore.jks | Bin 0 -> 2050 bytes
sni-test-config/geode-config/truststore.jks | Bin 0 -> 8095 bytes
sni-test-config/geode-config/truststore.p12 | Bin 0 -> 8983 bytes
sni-test-config/geode-config/truststore_sni.pem | 68 +++++++++++
sni-test-config/haproxy.cfg | 44 +++++++
sni-test-config/scripts/forever | 20 ++++
sni-test-config/scripts/geode-starter-2.gfsh | 23 ++++
sni-test-config/scripts/geode-starter.gfsh | 22 ++++
ssl_keys/client_keys/truststore_sni.pem | 68 +++++++++++
35 files changed, 714 insertions(+), 108 deletions(-)
diff --git a/clicache/integration-test2/CMakeLists.txt b/clicache/integration-test2/CMakeLists.txt
index 3437621..c41a4d2 100644
--- a/clicache/integration-test2/CMakeLists.txt
+++ b/clicache/integration-test2/CMakeLists.txt
@@ -48,6 +48,7 @@ add_library( ${PROJECT_NAME} SHARED
packages.config
AutoSerializationTests.cs
SerializationTests.cs
+ SNITests.cs
)
set_source_files_properties(
diff --git a/clicache/integration-test2/Config.cs.in b/clicache/integration-test2/Config.cs.in
index 9ec3f65..4111ec2 100644
--- a/clicache/integration-test2/Config.cs.in
+++ b/clicache/integration-test2/Config.cs.in
@@ -38,4 +38,9 @@ public class Config
{
get { return @"@CMAKE_CURRENT_SOURCE_DIR@/../../ssl_keys/client_keys"; }
}
+
+ public static string SniConfigPath
+ {
+ get { return @"@CMAKE_CURRENT_SOURCE_DIR@/../../sni-test-config"; }
+ }
}
diff --git a/clicache/integration-test2/SNITests.cs b/clicache/integration-test2/SNITests.cs
new file mode 100644
index 0000000..f23818b
--- /dev/null
+++ b/clicache/integration-test2/SNITests.cs
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using Xunit;
+using PdxTests;
+using System.Collections;
+using System.Collections.Generic;
+using Xunit.Abstractions;
+
+namespace Apache.Geode.Client.IntegrationTests
+{
+ [Trait("Category", "Integration")]
+ public class SNITests : TestBase, IDisposable
+ {
+ string currentWorkingDirectory;
+ Process dockerProcess;
+ private readonly Cache cache_;
+
+ public SNITests(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
+ {
+ currentWorkingDirectory = Directory.GetCurrentDirectory();
+ var clientTruststore = Config.SslClientKeyPath + @"/truststore_sni.pem";
+
+
+
+ var cacheFactory = new CacheFactory();
+ cacheFactory.Set("log-level", "none");
+ cacheFactory.Set("log-file", "c:/temp/SNITest-csharp.log");
+ cacheFactory.Set("ssl-enabled", "true");
+ cacheFactory.Set("ssl-truststore", clientTruststore);
+
+ cache_ = cacheFactory.Create();
+
+ var dc = Process.Start(@"docker-compose.exe", "-f " + Config.SniConfigPath + "/docker-compose.yml" + " up -d");
+ dc.WaitForExit();
+
+ var d = Process.Start(@"docker.exe", "exec -t geode gfsh run --file=/geode/scripts/geode-starter.gfsh");
+ d.WaitForExit();
+ }
+
+ public void Dispose()
+ {
+
+ var dockerComposeProc = Process.Start(@"docker-compose.exe", "stop");
+ dockerComposeProc.WaitForExit();
+
+ var dockerProc = Process.Start(@"docker.exe", "container prune -f");
+ dockerProc.WaitForExit();
+
+ }
+
+ private string RunDockerCommand(string dockerCommand)
+ {
+ ProcessStartInfo startInfo = new ProcessStartInfo();
+ startInfo.RedirectStandardOutput = true;
+ startInfo.UseShellExecute = false;
+ startInfo.FileName = @"docker.exe";
+ startInfo.Arguments = dockerCommand;
+ dockerProcess = Process.Start(startInfo);
+ String rVal = dockerProcess.StandardOutput.ReadToEnd();
+ dockerProcess.WaitForExit();
+ return rVal;
+ }
+
+ private int ParseProxyPort(string proxyString)
+ {
+ int colonPosition = proxyString.IndexOf(":");
+ string portNumberString = proxyString.Substring(colonPosition + 1);
+ return Int32.Parse(portNumberString);
+ }
+
+ [Fact (Skip = "Disabled until we iron out our Docker issues on Windows")]
+ public void ConnectViaProxy()
+ {
+ var portString = RunDockerCommand("port haproxy");
+ var portNumber = ParseProxyPort(portString);
+
+ cache_.GetPoolManager()
+ .CreateFactory()
+ .SetSniProxy("localhost", portNumber)
+ .AddLocator("locator-maeve", 10334)
+ .Create("pool");
+
+ var region = cache_.CreateRegionFactory(RegionShortcut.PROXY)
+ .SetPoolName("pool")
+ .Create<string, string>("jellyfish");
+
+ region.Put("1", "one");
+ var value = region.Get("1");
+
+ Assert.Equal("one", value);
+ cache_.Close();
+ }
+
+ [Fact (Skip = "Disabled until we iron out our Docker issues on Windows")]
+ public void ConnectionWithoutProxyFails()
+ {
+ cache_.GetPoolManager()
+ .CreateFactory()
+ .AddLocator("localhost", 10334)
+ .Create("pool");
+
+ var region = cache_.CreateRegionFactory(RegionShortcut.PROXY)
+ .SetPoolName("pool")
+ .Create<string, string>("region");
+
+ Assert.Throws<NotConnectedException>(() => region.Put("1", "one"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/clicache/src/PoolFactory.cpp b/clicache/src/PoolFactory.cpp
index c72da35..9c62562 100644
--- a/clicache/src/PoolFactory.cpp
+++ b/clicache/src/PoolFactory.cpp
@@ -285,6 +285,22 @@ namespace Apache
return this;
}
+ PoolFactory^ PoolFactory::SetSniProxy(String^ hostname, Int32 port)
+ {
+ _GF_MG_EXCEPTION_TRY2/* due to auto replace */
+
+ try
+ {
+ m_nativeptr->get()->setSniProxy( marshal_as<std::string>(hostname), port );
+ }
+ finally
+ {
+ GC::KeepAlive(m_nativeptr);
+ }
+
+ _GF_MG_EXCEPTION_CATCH_ALL2/* due to auto replace */
+ return this;
+ }
PoolFactory^ PoolFactory::SetSubscriptionEnabled( Boolean enabled )
{
diff --git a/clicache/src/PoolFactory.hpp b/clicache/src/PoolFactory.hpp
index e6f703e..7d0e3c3 100644
--- a/clicache/src/PoolFactory.hpp
+++ b/clicache/src/PoolFactory.hpp
@@ -280,6 +280,20 @@ namespace Apache
PoolFactory^ AddServer(String^ host, Int32 port);
/// <summary>
+ /// Set proxy info for SNI connection.
+ /// </summary>
+ /// <remarks>
+ /// Used for connecting via SNI proxy.
+ /// </remarks>
+ /// <param>
+ /// host the host name or ip address that the server is listening on.
+ /// </param>
+ /// <param>
+ /// port the port that the server is listening on.
+ /// </param>
+ PoolFactory^ SetSniProxy(String^ hostname, Int32 port);
+
+ /// <summary>
/// Enable subscriptions.
/// </summary>
/// <remarks>
diff --git a/cppcache/include/geode/PoolFactory.hpp b/cppcache/include/geode/PoolFactory.hpp
index 195e28d..d554482 100644
--- a/cppcache/include/geode/PoolFactory.hpp
+++ b/cppcache/include/geode/PoolFactory.hpp
@@ -427,6 +427,11 @@ class APACHE_GEODE_EXPORT PoolFactory {
PoolFactory& addServer(const std::string& host, int port);
/**
+ * Set proxy info for SNI connection. Used for connecting via SNI proxy.
+ */
+ PoolFactory& setSniProxy(const std::string& hostname, const int port);
+
+ /**
* If set to <code>true</code> then the created pool will have
* server-to-client
* subscriptions enabled.
diff --git a/cppcache/integration/framework/TestConfig.cpp.in b/cppcache/integration/framework/TestConfig.cpp.in
index f937640..486cdd1 100644
--- a/cppcache/integration/framework/TestConfig.cpp.in
+++ b/cppcache/integration/framework/TestConfig.cpp.in
@@ -24,6 +24,7 @@ const char *getFrameworkString(FrameworkVariable name) {
case FrameworkVariable::TestCacheXmlDir: return "@CMAKE_CURRENT_SOURCE_DIR@/../../integration-test/resources";
case FrameworkVariable::TestClientSslKeysDir: return "@CMAKE_CURRENT_SOURCE_DIR@/../../../ssl_keys/client_keys";
case FrameworkVariable::TestServerSslKeysDir: return "@CMAKE_CURRENT_SOURCE_DIR@/../../../ssl_keys/server_keys";
+ case FrameworkVariable::TestSniConfigPath: return "@CMAKE_CURRENT_SOURCE_DIR@/../../../sni-test-config";
default: return "";
}
}
diff --git a/cppcache/integration/framework/TestConfig.h b/cppcache/integration/framework/TestConfig.h
index b7362d0..30203ae 100644
--- a/cppcache/integration/framework/TestConfig.h
+++ b/cppcache/integration/framework/TestConfig.h
@@ -20,7 +20,7 @@
#ifndef INTEGRATION_TEST_FRAMEWORK_CONFIG_H
#define INTEGRATION_TEST_FRAMEWORK_CONFIG_H
-enum class FrameworkVariable {JavaObjectJarPath, GfShExecutable, TestCacheXmlDir, TestClientSslKeysDir, TestServerSslKeysDir};
+enum class FrameworkVariable {JavaObjectJarPath, GfShExecutable, TestCacheXmlDir, TestClientSslKeysDir, TestServerSslKeysDir, TestSniConfigPath};
const char *getFrameworkString(FrameworkVariable name);
diff --git a/cppcache/integration/test/SNITest.cpp b/cppcache/integration/test/SNITest.cpp
index 7e70619..e5052b7 100644
--- a/cppcache/integration/test/SNITest.cpp
+++ b/cppcache/integration/test/SNITest.cpp
@@ -28,6 +28,7 @@
#include <geode/RegionShortcut.hpp>
#include "framework/Cluster.h"
+#include "framework/TestConfig.h"
namespace snitest {
@@ -41,28 +42,26 @@ class SNITest : public ::testing::Test {
protected:
SNITest() {
certificatePassword = std::string("apachegeode");
+ clientSslKeysDir = boost::filesystem::path(
+ getFrameworkString(FrameworkVariable::TestClientSslKeysDir));
currentWorkingDirectory = boost::filesystem::current_path();
+ sniConfigPath = boost::filesystem::path(
+ getFrameworkString(FrameworkVariable::TestSniConfigPath));
}
~SNITest() override = default;
void SetUp() override {
auto systemRVal = 0;
-#if defined(_WIN32)
- std::string sniDir(currentWorkingDirectory.string());
- sniDir += "/../sni-test-config";
- SetCurrentDirectory(sniDir.c_str());
-#else
- systemRVal = chdir("./sni-test-config");
- if (systemRVal == -1) {
- BOOST_LOG_TRIVIAL(error) << "chdir returned: " << systemRVal;
- }
-#endif
+ std::string dockerComposeCmd = "docker-compose -f " +
+ sniConfigPath.string() +
+ "/docker-compose.yml" + " up -d";
+ const char* dcc = dockerComposeCmd.c_str();
- systemRVal = std::system("docker-compose up -d");
+ systemRVal = std::system(dcc);
if (systemRVal == -1) {
BOOST_LOG_TRIVIAL(error)
- << "std::system(\"docker-compose up -d\") returned: " << systemRVal;
+ << "std::system(\"docker-compose\") returned: " << systemRVal;
}
systemRVal = std::system(
@@ -80,9 +79,14 @@ class SNITest : public ::testing::Test {
if (systemRVal == -1) {
BOOST_LOG_TRIVIAL(error) << "std::system returned: " << systemRVal;
}
+
+ systemRVal = std::system("docker container prune -f");
+ if (systemRVal == -1) {
+ BOOST_LOG_TRIVIAL(error) << "std::system returned: " << systemRVal;
+ }
}
- std::string makeItSo(const char* command) {
+ std::string runDockerCommand(const char* command) {
std::string commandOutput;
#if defined(_WIN32)
std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(command, "r"),
@@ -108,41 +112,51 @@ class SNITest : public ::testing::Test {
}
std::string certificatePassword;
+ boost::filesystem::path clientSslKeysDir;
boost::filesystem::path currentWorkingDirectory;
+ boost::filesystem::path sniConfigPath;
};
-TEST_F(SNITest, DISABLED_connectViaProxyTest) {
+#if defined(_WIN32)
+TEST_F(SNITest, DISABLE_connectViaProxyTest) {
+#else
+TEST_F(SNITest, connectViaProxyTest) {
+#endif
const auto clientTruststore =
- (currentWorkingDirectory /
- boost::filesystem::path("sni-test-config/geode-config/truststore.jks"));
+ (clientSslKeysDir / boost::filesystem::path("/truststore_sni.pem"));
auto cache = CacheFactory()
- .set("log-level", "DEBUG")
+ .set("log-level", "debug")
+ .set("log-file", "SNITest.log")
.set("ssl-enabled", "true")
.set("ssl-truststore", clientTruststore.string())
.create();
- auto portString = makeItSo("docker port haproxy");
+ auto portString = runDockerCommand("docker port haproxy");
auto portNumber = parseProxyPort(portString);
cache.getPoolManager()
.createFactory()
- .addLocator("localhost", portNumber)
+ .setSniProxy("localhost", portNumber)
+ .addLocator("locator-maeve", 10334)
.create("pool");
auto region = cache.createRegionFactory(RegionShortcut::PROXY)
.setPoolName("pool")
- .create("region");
+ .create("jellyfish");
region->put("1", "one");
cache.close();
}
-TEST_F(SNITest, connectionFailsTest) {
+#if defined(_WIN32)
+TEST_F(SNITest, DISABLE_connectWithoutProxyFails) {
+#else
+TEST_F(SNITest, connectWithoutProxyFails) {
+#endif
const auto clientTruststore =
- (currentWorkingDirectory /
- boost::filesystem::path("sni-test-config/geode-config/truststore.jks"));
+ (clientSslKeysDir / boost::filesystem::path("/truststore_sni.pem"));
auto cache = CacheFactory()
.set("log-level", "DEBUG")
@@ -152,7 +166,8 @@ TEST_F(SNITest, connectionFailsTest) {
cache.getPoolManager()
.createFactory()
- .addLocator("localhost", 10334)
+ .setSniProxy("badProxyName", 40000)
+ .addLocator("locator-maeve", 10334)
.create("pool");
auto region = cache.createRegionFactory(RegionShortcut::PROXY)
@@ -164,23 +179,4 @@ TEST_F(SNITest, connectionFailsTest) {
cache.close();
}
-TEST_F(SNITest, doNothingTest) {
- const auto clientTruststore =
- (currentWorkingDirectory /
- boost::filesystem::path("sni-test-config/geode-config/truststore.jks"));
-
- auto cache = CacheFactory()
- .set("log-level", "DEBUG")
- .set("ssl-enabled", "true")
- .set("ssl-truststore", clientTruststore.string())
- .create();
-
- cache.getPoolManager()
- .createFactory()
- .addLocator("localhost", 10334)
- .create("pool");
-
- cache.close();
-}
-
} // namespace snitest
diff --git a/cppcache/integration/test/sni-test-config/geode-config/truststore.p12 b/cppcache/integration/test/sni-test-config/geode-config/truststore.p12
new file mode 100644
index 0000000..339d775
Binary files /dev/null and b/cppcache/integration/test/sni-test-config/geode-config/truststore.p12 differ
diff --git a/cppcache/integration/test/sni-test-config/geode-config/truststore_sni.pem b/cppcache/integration/test/sni-test-config/geode-config/truststore_sni.pem
new file mode 100644
index 0000000..1857ce6
--- /dev/null
+++ b/cppcache/integration/test/sni-test-config/geode-config/truststore_sni.pem
@@ -0,0 +1,68 @@
+-----BEGIN CERTIFICATE-----
+MIICrDCCAZSgAwIBAgIEXozDxjANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1s
+b2NhdG9yLW1hZXZlMB4XDTIwMDQwNzE4MTc0MloXDTI1MDQwNzE4MTc0MlowGDEW
+MBQGA1UEAwwNbG9jYXRvci1tYWV2ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAOJ3jM2Rb50L+1fXyhZbaOHMuVUVGJ5jQV9wH3ijjeCEckaF29LbEtG8
+swMaxSoi4Sp/A4dp/7VI9CFZJKOX3zooZcuHyR7GSta4wH3oO55w0AfyTGeG6KF2
+Ekzj8pDPHyn/141rFAUPmMDnCfbF69Uixfi2XPxEJZw2GDN/YIHndY+X1pJ4ZuXS
+SmrORSEOSmrN9X7pqbL5D2cy15cmTK5449ZqLEfZS72Mv3gve1Ax2JMWCBEwLdob
+xW5utgmEe1/WhlhPzFr5C92znF/5Eucil/Rr+yynp31X+/QYBemYwOxbeZotHBZJ
+tMLMzaInydrZ04wgHRftNeN0TIZkPmcCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
+Jj1OSCWoILzWLBU1cAiQK8Gt0DVkqcpO4/vc3CoiU2T/em74cBzTwqmgrBvykWgq
+f05jWQcod2yNg8trHrgx8F9CfyyvTXRIxttyfmbD7DAQk+qn9QBSbRJFfzo8VfNp
+dGcT7KV9UDVyzltiTorqQJHUx3acUgtLYS2XUVlbGclhnNafRO44uobOsteAG01v
+YqFa8ZaZM7qcZ88mbbKLXn6lo203JguM+TM0P7wHnzcww9sLmsP8W2cvsvefwCl4
+O7OYcjhcbEph+mIC3/zN8vF6d8xtLiMSGk6BNCHd003MBEhZHizyquGtAFLaEafX
+V6sLm65i8uF2glnQfwS5JQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICrDCCAZSgAwIBAgIEXozDxjANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1s
+b2NhdG9yLW1hZXZlMB4XDTIwMDQwNzE4MTc0MloXDTI1MDQwNzE4MTc0MlowGDEW
+MBQGA1UEAwwNbG9jYXRvci1tYWV2ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAOJ3jM2Rb50L+1fXyhZbaOHMuVUVGJ5jQV9wH3ijjeCEckaF29LbEtG8
+swMaxSoi4Sp/A4dp/7VI9CFZJKOX3zooZcuHyR7GSta4wH3oO55w0AfyTGeG6KF2
+Ekzj8pDPHyn/141rFAUPmMDnCfbF69Uixfi2XPxEJZw2GDN/YIHndY+X1pJ4ZuXS
+SmrORSEOSmrN9X7pqbL5D2cy15cmTK5449ZqLEfZS72Mv3gve1Ax2JMWCBEwLdob
+xW5utgmEe1/WhlhPzFr5C92znF/5Eucil/Rr+yynp31X+/QYBemYwOxbeZotHBZJ
+tMLMzaInydrZ04wgHRftNeN0TIZkPmcCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
+Jj1OSCWoILzWLBU1cAiQK8Gt0DVkqcpO4/vc3CoiU2T/em74cBzTwqmgrBvykWgq
+f05jWQcod2yNg8trHrgx8F9CfyyvTXRIxttyfmbD7DAQk+qn9QBSbRJFfzo8VfNp
+dGcT7KV9UDVyzltiTorqQJHUx3acUgtLYS2XUVlbGclhnNafRO44uobOsteAG01v
+YqFa8ZaZM7qcZ88mbbKLXn6lo203JguM+TM0P7wHnzcww9sLmsP8W2cvsvefwCl4
+O7OYcjhcbEph+mIC3/zN8vF6d8xtLiMSGk6BNCHd003MBEhZHizyquGtAFLaEafX
+V6sLm65i8uF2glnQfwS5JQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICtDCCAZygAwIBAgIEXozGnzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFz
+ZXJ2ZXItY2xlbWVudGluZTAeFw0yMDA0MDcxODI5NTFaFw0yNTA0MDcxODI5NTFa
+MBwxGjAYBgNVBAMMEXNlcnZlci1jbGVtZW50aW5lMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEA6IzshjujS5c58AH8nJHBhlqjfNpacoNxhxykeCVsExa9
+vi0l8ezi35pte06j7gpMWhDYHokrHaw6ymp9iTi7D91yIPGeMMNUli8DnzgAzpeY
+V8SGgkrVBalkVe0GimAHXMrzeZF+8D2BEdvDAsIUbrZRACElPlLUoiO93xZZ8ad+
+fAfLVetH4lDJ54FT7ia+St6L0QxSrDLvrqmc/58ZunkQBnQcd4tMjCD1kX4l+5Q1
+eF+Rc/SbY+/8HfyCZcA98voC3dKF13U+0YAf/0ahin+8Ckm6BL/StUxFNftTtJ7l
+iKf56Y3FbSQ84Q9Te8feb05XidkF74Gifa4Q7gOzjwIDAQABMA0GCSqGSIb3DQEB
+CwUAA4IBAQDKvYcnVFryhupo156bB33BU14KN8b5joVyQLeGb2Tx+icZd/jFhqSQ
+c3f8VV+aG9+CtRi/6wesdzf9/CVF+J4ARJ7j3i60NlJi4vQJlZnou+JSBgbBiDkW
+p12ITsw7l1k2zxH8hoMPNbMK1EC/+uwVRJt92L52uShLw9zKtE4MLZxZVa7Amkf4
+zRc78fHwwPXoMjLcQxw+8JRjlciWr/hZccuppXI4qb17l6HAMvW4vCslao0c9pSp
+Opg5Q0PwVXFROIvCANdxNI9ptSrH78Thxh4rggnHs+OZF02D22oTkjquU4Xrar3u
+FXlIS8UmdkqAXGIJf0pqa48aXcqeipRe
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICrjCCAZagAwIBAgIEXozE5DANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDDA5z
+ZXJ2ZXItZG9sb3JlczAeFw0yMDA0MDcxODIyMjhaFw0yNTA0MDcxODIyMjhaMBkx
+FzAVBgNVBAMMDnNlcnZlci1kb2xvcmVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAyRTzsWsih9Boz2/aRFJsgJNDn8/C207kpvJ9lj0uBWNdZGJ86T4i
+CwvIyMFvxeYQB0qO0AHf6FvJfMgunRlCj3fD01s7AHj8kCFoM/akgo04M7iJfSkU
+dDCVuRbrFtz31akNckyxRw/oORiQ6NYGxnuAvtFdjE8jFc77WVXVU5QuqVEueJXs
+HM+t6VGEn+7GwPsSJMIuEERd+05ZlghB1HoQD4Wu4+b/CXU+8aFRad0HRXHInBl0
+0QABETcMtpe3xIotC7H1nsAMipb0jyl3p+1a49FbrAktsiko8Y2iRVv3kZ58xfx9
+2Unmw+ViEb5bVRFytqb5AIgARI/+XX1zBwIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
+AQB39QXR3HLEju8B1oNCH1UciZetMxvORC2fwgXhqjbJ2YkHlykaLAAKv6DOSyc2
+HE40F2Q/Y0p0NC41+4YIiujgzKWaDI1Gw22PlceE2B49dO8evmldN2NixkirJbtm
+bEtjINAxHXbhXn8GgUKJxSqtFPTX/fG7OCYvkvGItQAhSrGo9r5ACuDYkTZsBAZp
+9jHc50TZsQ7od4jsPXrtZ6S2doOA0TdQ/+XzNyoadbG0YZbRtUVmhJN7gQfkBcjH
+/AnYeYJL1kg39AuO3PsFhgWCsR2eNizGCh7CnHx7xpJnLYAw/01TGidsku/oYFiI
+5SthBjGC992gTekW54hYtMBU
+-----END CERTIFICATE-----
diff --git a/cppcache/src/PoolAttributes.cpp b/cppcache/src/PoolAttributes.cpp
index 2b4230d..206e1a4 100644
--- a/cppcache/src/PoolAttributes.cpp
+++ b/cppcache/src/PoolAttributes.cpp
@@ -44,46 +44,13 @@ PoolAttributes::PoolAttributes()
m_subsEnabled(PoolFactory::DEFAULT_SUBSCRIPTION_ENABLED),
m_multiuserSecurityMode(PoolFactory::DEFAULT_MULTIUSER_SECURE_MODE),
m_isPRSingleHopEnabled(PoolFactory::DEFAULT_PR_SINGLE_HOP_ENABLED),
- m_serverGrp(PoolFactory::DEFAULT_SERVER_GROUP) {}
+ m_serverGrp(PoolFactory::DEFAULT_SERVER_GROUP),
+ m_sniProxyPort(0) {}
+
std::shared_ptr<PoolAttributes> PoolAttributes::clone() {
return std::make_shared<PoolAttributes>(*this);
}
-/** Return true if all the attributes are equal to those of other. */
-bool PoolAttributes::operator==(const PoolAttributes& other) const {
- if (m_isThreadLocalConn != other.m_isThreadLocalConn) return false;
- if (m_freeConnTimeout != other.m_freeConnTimeout) return false;
- if (m_loadCondInterval != other.m_loadCondInterval) return false;
- if (m_sockBufferSize != other.m_sockBufferSize) return false;
- if (m_readTimeout != other.m_readTimeout) return false;
- if (m_minConns != other.m_minConns) return false;
- if (m_maxConns != other.m_maxConns) return false;
- if (m_retryAttempts != other.m_retryAttempts) return false;
- if (m_statsInterval != other.m_statsInterval) return false;
- if (m_redundancy != other.m_redundancy) return false;
- if (m_msgTrackTimeout != other.m_msgTrackTimeout) return false;
- if (m_subsAckInterval != other.m_subsAckInterval) return false;
- if (m_idleTimeout != other.m_idleTimeout) return false;
- if (m_pingInterval != other.m_pingInterval) return false;
- if (m_updateLocatorListInterval != other.m_updateLocatorListInterval) {
- return false;
- }
- if (m_subsEnabled != other.m_subsEnabled) return false;
- if (m_multiuserSecurityMode != other.m_multiuserSecurityMode) return false;
- if (m_isPRSingleHopEnabled != other.m_isPRSingleHopEnabled) return false;
- if (m_serverGrp != other.m_serverGrp) return false;
-
- if (m_initLocList.size() != other.m_initLocList.size()) return false;
- if (m_initServList.size() != other.m_initServList.size()) return false;
-
- if (!compareVectorOfStrings(m_initLocList, other.m_initLocList)) return false;
- if (!compareVectorOfStrings(m_initServList, other.m_initServList)) {
- return false;
- }
-
- return true;
-}
-
bool PoolAttributes::compareVectorOfStrings(
const std::vector<std::string>& thisVector,
const std::vector<std::string>& otherVector) {
diff --git a/cppcache/src/PoolAttributes.hpp b/cppcache/src/PoolAttributes.hpp
index 3fee900..44d9633 100644
--- a/cppcache/src/PoolAttributes.hpp
+++ b/cppcache/src/PoolAttributes.hpp
@@ -121,6 +121,14 @@ class PoolAttributes {
m_statsInterval = statisticInterval;
}
+ const std::string& getSniProxyHost() const { return m_sniProxyHost; }
+
+ void setSniProxyHost(const std::string& host) { m_sniProxyHost = host; }
+
+ int getSniProxyPort() const { return m_sniProxyPort; }
+
+ void setSniProxyPort(const int port) { m_sniProxyPort = port; }
+
const std::string& getServerGroup() const { return m_serverGrp; }
void setServerGroup(std::string group) { m_serverGrp = group; }
@@ -168,9 +176,6 @@ class PoolAttributes {
std::shared_ptr<PoolAttributes> clone();
- /** Return true if all the attributes are equal to those of other. */
- bool operator==(const PoolAttributes& other) const;
-
private:
bool m_isThreadLocalConn;
std::chrono::milliseconds m_freeConnTimeout;
@@ -197,6 +202,9 @@ class PoolAttributes {
std::vector<std::string> m_initLocList;
std::vector<std::string> m_initServList;
+ std::string m_sniProxyHost;
+ int m_sniProxyPort;
+
static bool compareVectorOfStrings(
const std::vector<std::string>& thisVector,
const std::vector<std::string>& otherVector);
diff --git a/cppcache/src/PoolFactory.cpp b/cppcache/src/PoolFactory.cpp
index 0ee2518..9a43f64 100644
--- a/cppcache/src/PoolFactory.cpp
+++ b/cppcache/src/PoolFactory.cpp
@@ -185,6 +185,13 @@ PoolFactory& PoolFactory::addServer(const std::string& host, int port) {
return *this;
}
+PoolFactory& PoolFactory::setSniProxy(const std::string& hostname,
+ const int port) {
+ m_attrs->setSniProxyHost(hostname);
+ m_attrs->setSniProxyPort(port);
+ return *this;
+}
+
PoolFactory& PoolFactory::setSubscriptionEnabled(bool enabled) {
m_attrs->setSubscriptionEnabled(enabled);
return *this;
@@ -304,15 +311,17 @@ PoolFactory& PoolFactory::addCheck(const std::string& host, int port) {
std::to_string(port));
}
- ACE_INET_Addr addr(port, host.c_str());
+ if (m_attrs->getSniProxyHost().empty()) {
+ ACE_INET_Addr addr(port, host.c_str());
#ifdef WITH_IPV6
- // check unknown host
- // ACE will not initialize port if hostname is not resolved.
- if (port != addr.get_port_number()) {
+ // check unknown host
+ // ACE will not initialize port if hostname is not resolved.
+ if (port != addr.get_port_number()) {
#else
- if (!(addr.get_ip_address())) {
+ if (!(addr.get_ip_address())) {
#endif
- throw IllegalArgumentException("Unknown host " + host);
+ throw IllegalArgumentException("Unknown host " + host);
+ }
}
return *this;
}
diff --git a/cppcache/src/TcpSslConn.cpp b/cppcache/src/TcpSslConn.cpp
index a72843e..8ede9a3 100644
--- a/cppcache/src/TcpSslConn.cpp
+++ b/cppcache/src/TcpSslConn.cpp
@@ -29,7 +29,6 @@
namespace apache {
namespace geode {
namespace client {
-
std::atomic_flag TcpSslConn::initialized_ = ATOMIC_FLAG_INIT;
void TcpSslConn::createSocket(ACE_HANDLE sock) {
@@ -48,12 +47,16 @@ void TcpSslConn::connect() {
std::to_string(inetAddress_.get_port_number()) + " waiting " +
to_string(timeout_));
+ if (!sniHostname_.empty()) {
+ SSL_set_tlsext_host_name(stream_->ssl(), sniHostname_.c_str());
+ }
+
ACE_SSL_SOCK_Connector conn;
ACE_Time_Value actTimeout(timeout_);
- if (ACE_SSL_SOCK_Connector{}.connect(
- *stream_, inetAddress_,
- timeout_ > std::chrono::microseconds::zero() ? &actTimeout
- : nullptr) == -1) {
+ if (conn.connect(*stream_, inetAddress_,
+ timeout_ > std::chrono::microseconds::zero()
+ ? &actTimeout
+ : nullptr) == -1) {
const auto lastError = ACE_OS::last_error();
if (lastError == ETIME || lastError == ETIMEDOUT) {
throw TimeoutException(
diff --git a/cppcache/src/TcpSslConn.hpp b/cppcache/src/TcpSslConn.hpp
index 6fca35f..6a543b1 100644
--- a/cppcache/src/TcpSslConn.hpp
+++ b/cppcache/src/TcpSslConn.hpp
@@ -50,6 +50,7 @@ class TcpSslConn : public TcpConn {
const std::string trustStoreFile_;
const std::string privateKeyFile_;
const std::string password_;
+ std::string sniHostname_;
std::unique_ptr<ACE_SSL_SOCK_Stream> stream_;
protected:
@@ -61,11 +62,23 @@ class TcpSslConn : public TcpConn {
void initSsl();
public:
+ TcpSslConn(const std::string& ipaddr, std::chrono::microseconds waitSeconds,
+ int32_t maxBuffSizePool, const std::string& sniProxyHostname,
+ uint16_t sniProxyPort, std::string publicKeyFile,
+ std::string privateKeyFile, std::string password)
+ : TcpConn(sniProxyHostname, sniProxyPort, waitSeconds, maxBuffSizePool),
+ trustStoreFile_(std::move(publicKeyFile)),
+ privateKeyFile_(std::move(privateKeyFile)),
+ password_(std::move(password)),
+ sniHostname_(ipaddr.substr(0, ipaddr.find(':'))) {
+ initSsl();
+ }
+
TcpSslConn(const std::string& hostname, uint16_t port,
- std::chrono::microseconds waitSeconds, int32_t maxBuffSizePool,
- std::string publicKeyFile, std::string privateKeyFile,
- std::string password)
- : TcpConn(hostname, port, waitSeconds, maxBuffSizePool),
+ std::chrono::microseconds connect_timeout, int32_t maxBuffSizePool,
+ const std::string& publicKeyFile,
+ const std::string& privateKeyFile, const std::string& password)
+ : TcpConn(hostname.c_str(), port, connect_timeout, maxBuffSizePool),
trustStoreFile_(std::move(publicKeyFile)),
privateKeyFile_(std::move(privateKeyFile)),
password_(std::move(password)) {
diff --git a/cppcache/src/TcrConnection.cpp b/cppcache/src/TcrConnection.cpp
index 8698fa5..bae9a4a 100644
--- a/cppcache/src/TcrConnection.cpp
+++ b/cppcache/src/TcrConnection.cpp
@@ -432,10 +432,19 @@ Connector* TcrConnection::createConnection(
->getDistributedSystem()
.getSystemProperties();
if (systemProperties.sslEnabled()) {
- socket = new TcpSslConn(address, connectTimeout, maxBuffSizePool,
- systemProperties.sslTrustStore(),
- systemProperties.sslKeyStore(),
- systemProperties.sslKeystorePassword());
+ auto sniHostname = m_poolDM->getSNIProxyHostname();
+ auto sniPort = m_poolDM->getSNIPort();
+ if (sniHostname.empty()) {
+ socket = new TcpSslConn(address, connectTimeout, maxBuffSizePool,
+ systemProperties.sslTrustStore(),
+ systemProperties.sslKeyStore(),
+ systemProperties.sslKeystorePassword());
+ } else {
+ socket = new TcpSslConn(
+ address, connectTimeout, maxBuffSizePool, sniHostname, sniPort,
+ systemProperties.sslTrustStore(), systemProperties.sslKeyStore(),
+ systemProperties.sslKeystorePassword());
+ }
} else {
socket = new TcpConn(address, connectTimeout, maxBuffSizePool);
}
@@ -514,7 +523,8 @@ inline ConnErrType TcrConnection::sendData(
std::chrono::microseconds defaultWaitSecs = std::chrono::seconds(2);
if (defaultWaitSecs > sendTimeout) defaultWaitSecs = sendTimeout;
LOGDEBUG(
- "before send len %zu sendTimeoutSec = %s checkConnected = %d m_connected "
+ "before send len %zu sendTimeoutSec = %s checkConnected = %d "
+ "m_connected "
"%d",
length, to_string(sendTimeout).c_str(), checkConnected, m_connected);
while (length > 0 && sendTimeout > std::chrono::microseconds::zero()) {
diff --git a/cppcache/src/ThinClientLocatorHelper.cpp b/cppcache/src/ThinClientLocatorHelper.cpp
index e7174ce..a8c58c5 100644
--- a/cppcache/src/ThinClientLocatorHelper.cpp
+++ b/cppcache/src/ThinClientLocatorHelper.cpp
@@ -66,6 +66,18 @@ ThinClientLocatorHelper::ThinClientLocatorHelper(
}
}
+ThinClientLocatorHelper::ThinClientLocatorHelper(
+ const std::vector<std::string>& locatorAddresses,
+ const std::string& sniProxyHost, int sniProxyPort,
+ const ThinClientPoolDM* poolDM)
+ : m_poolDM(poolDM),
+ m_sniProxyHost(sniProxyHost),
+ m_sniProxyPort(sniProxyPort) {
+ for (auto&& locatorAddress : locatorAddresses) {
+ m_locHostPort.emplace_back(locatorAddress);
+ }
+}
+
Connector* ThinClientLocatorHelper::createConnection(
Connector*& conn, const char* hostname, int32_t port,
std::chrono::microseconds waitSeconds, int32_t maxBuffSizePool) {
@@ -75,10 +87,18 @@ Connector* ThinClientLocatorHelper::createConnection(
->getDistributedSystem()
.getSystemProperties();
if (systemProperties.sslEnabled()) {
- socket = new TcpSslConn(hostname, port, waitSeconds, maxBuffSizePool,
- systemProperties.sslTrustStore().c_str(),
- systemProperties.sslKeyStore().c_str(),
- systemProperties.sslKeystorePassword().c_str());
+ if (m_sniProxyHost.empty()) {
+ socket = new TcpSslConn(
+ hostname, static_cast<uint16_t>(port), waitSeconds, maxBuffSizePool,
+ systemProperties.sslTrustStore(), systemProperties.sslKeyStore(),
+ systemProperties.sslKeystorePassword());
+ } else {
+ socket =
+ new TcpSslConn(hostname, waitSeconds, maxBuffSizePool, m_sniProxyHost,
+ m_sniProxyPort, systemProperties.sslTrustStore(),
+ systemProperties.sslKeyStore(),
+ systemProperties.sslKeystorePassword());
+ }
} else {
socket = new TcpConn(hostname, port, waitSeconds, maxBuffSizePool);
}
diff --git a/cppcache/src/ThinClientLocatorHelper.hpp b/cppcache/src/ThinClientLocatorHelper.hpp
index c72f950..df522a3 100644
--- a/cppcache/src/ThinClientLocatorHelper.hpp
+++ b/cppcache/src/ThinClientLocatorHelper.hpp
@@ -44,6 +44,9 @@ class ThinClientLocatorHelper {
public:
ThinClientLocatorHelper(const std::vector<std::string>& locatorAddresses,
const ThinClientPoolDM* poolDM);
+ ThinClientLocatorHelper(const std::vector<std::string>& locatorAddresses,
+ const std::string& sniProxyHost, int sniProxyPort,
+ const ThinClientPoolDM* poolDM);
GfErrType getEndpointForNewFwdConn(
ServerLocation& outEndpoint, std::string& additionalLoc,
const std::set<ServerLocation>& exclEndPts,
@@ -71,6 +74,8 @@ class ThinClientLocatorHelper {
const ThinClientPoolDM* m_poolDM;
ThinClientLocatorHelper(const ThinClientLocatorHelper&);
ThinClientLocatorHelper& operator=(const ThinClientLocatorHelper&);
+ std::string m_sniProxyHost;
+ int m_sniProxyPort;
};
} // namespace client
} // namespace geode
diff --git a/cppcache/src/ThinClientPoolDM.cpp b/cppcache/src/ThinClientPoolDM.cpp
index aab3200..8b7022d 100644
--- a/cppcache/src/ThinClientPoolDM.cpp
+++ b/cppcache/src/ThinClientPoolDM.cpp
@@ -186,7 +186,9 @@ ThinClientPoolDM::ThinClientPoolDM(const char* name,
throw IllegalStateException(msg);
}
reset();
- m_locHelper = new ThinClientLocatorHelper(m_attrs->m_initLocList, this);
+ m_locHelper = new ThinClientLocatorHelper(m_attrs->m_initLocList,
+ m_attrs->m_sniProxyHost,
+ m_attrs->m_sniProxyPort, this);
m_stats = new PoolStats(
cacheImpl->getStatisticsManager().getStatisticsFactory(), m_poolName);
diff --git a/cppcache/src/ThinClientPoolDM.hpp b/cppcache/src/ThinClientPoolDM.hpp
index 711e906..ffd3d90 100644
--- a/cppcache/src/ThinClientPoolDM.hpp
+++ b/cppcache/src/ThinClientPoolDM.hpp
@@ -168,6 +168,8 @@ class ThinClientPoolDM
GfErrType getConnectionToAnEndPoint(std::string epNameStr,
TcrConnection*& conn);
+ const std::string getSNIProxyHostname() { return m_attrs->getSniProxyHost(); }
+ uint16_t getSNIPort() { return m_attrs->getSniProxyPort(); }
virtual inline bool isSticky() { return m_sticky; }
virtual TcrEndpoint* getEndPoint(
const std::shared_ptr<BucketServerLocation>& serverLocation,
diff --git a/sni-test-config/docker-compose.yml b/sni-test-config/docker-compose.yml
new file mode 100644
index 0000000..b0a6100
--- /dev/null
+++ b/sni-test-config/docker-compose.yml
@@ -0,0 +1,43 @@
+#
+# 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.
+#
+version: '3'
+services:
+ geode:
+ container_name: 'geode'
+ image: 'apachegeode/geode'
+ expose:
+ - '10334'
+ - '40404'
+ entrypoint: 'sh'
+ command: ["-c", "while true; do sleep 600; done"]
+ networks:
+ geode-sni-test:
+ volumes:
+ - ./geode-config:/geode/config:ro
+ - ./scripts:/geode/scripts
+ haproxy:
+ container_name: 'haproxy'
+ image: 'haproxy:2.1'
+ ports:
+ - "15443"
+ networks:
+ geode-sni-test:
+ volumes:
+ - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
+networks:
+ geode-sni-test:
+
diff --git a/sni-test-config/geode-config/gemfire.properties b/sni-test-config/geode-config/gemfire.properties
new file mode 100644
index 0000000..1f13fb0
--- /dev/null
+++ b/sni-test-config/geode-config/gemfire.properties
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+
+statistic-sampling-enabled=true
+statistic-archive-file=statArchive.gfs
diff --git a/sni-test-config/geode-config/gfsecurity.properties b/sni-test-config/geode-config/gfsecurity.properties
new file mode 100644
index 0000000..813d260
--- /dev/null
+++ b/sni-test-config/geode-config/gfsecurity.properties
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+security-log-level=info
+security-peer-verifymember-timeout=1000
+ssl-keystore-password=geode
+ssl-truststore=/geode/config/truststore.jks
+ssl-truststore-password=geode
+ssl-require-authentication=false
+ssl-web-require-authentication=false
+ssl-enabled-components=all
+ssl-endpoint-identification-enabled=false
+
diff --git a/sni-test-config/geode-config/locator-maeve-keystore.jks b/sni-test-config/geode-config/locator-maeve-keystore.jks
new file mode 100644
index 0000000..a29cf0f
Binary files /dev/null and b/sni-test-config/geode-config/locator-maeve-keystore.jks differ
diff --git a/sni-test-config/geode-config/server-clementine-keystore.jks b/sni-test-config/geode-config/server-clementine-keystore.jks
new file mode 100644
index 0000000..380de6c
Binary files /dev/null and b/sni-test-config/geode-config/server-clementine-keystore.jks differ
diff --git a/sni-test-config/geode-config/server-dolores-keystore.jks b/sni-test-config/geode-config/server-dolores-keystore.jks
new file mode 100644
index 0000000..cb2c4c5
Binary files /dev/null and b/sni-test-config/geode-config/server-dolores-keystore.jks differ
diff --git a/sni-test-config/geode-config/truststore.jks b/sni-test-config/geode-config/truststore.jks
new file mode 100644
index 0000000..ffcdaf3
Binary files /dev/null and b/sni-test-config/geode-config/truststore.jks differ
diff --git a/sni-test-config/geode-config/truststore.p12 b/sni-test-config/geode-config/truststore.p12
new file mode 100644
index 0000000..339d775
Binary files /dev/null and b/sni-test-config/geode-config/truststore.p12 differ
diff --git a/sni-test-config/geode-config/truststore_sni.pem b/sni-test-config/geode-config/truststore_sni.pem
new file mode 100644
index 0000000..b4e8cda
--- /dev/null
+++ b/sni-test-config/geode-config/truststore_sni.pem
@@ -0,0 +1,68 @@
+-----BEGIN CERTIFICATE-----
+MIICrDCCAZSgAwIBAgIEXozDxjANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1s
+b2NhdG9yLW1hZXZlMB4XDTIwMDQwNzE4MTc0MloXDTI1MDQwNzE4MTc0MlowGDEW
+MBQGA1UEAwwNbG9jYXRvci1tYWV2ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAOJ3jM2Rb50L+1fXyhZbaOHMuVUVGJ5jQV9wH3ijjeCEckaF29LbEtG8
+swMaxSoi4Sp/A4dp/7VI9CFZJKOX3zooZcuHyR7GSta4wH3oO55w0AfyTGeG6KF2
+Ekzj8pDPHyn/141rFAUPmMDnCfbF69Uixfi2XPxEJZw2GDN/YIHndY+X1pJ4ZuXS
+SmrORSEOSmrN9X7pqbL5D2cy15cmTK5449ZqLEfZS72Mv3gve1Ax2JMWCBEwLdob
+xW5utgmEe1/WhlhPzFr5C92znF/5Eucil/Rr+yynp31X+/QYBemYwOxbeZotHBZJ
+tMLMzaInydrZ04wgHRftNeN0TIZkPmcCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
+Jj1OSCWoILzWLBU1cAiQK8Gt0DVkqcpO4/vc3CoiU2T/em74cBzTwqmgrBvykWgq
+f05jWQcod2yNg8trHrgx8F9CfyyvTXRIxttyfmbD7DAQk+qn9QBSbRJFfzo8VfNp
+dGcT7KV9UDVyzltiTorqQJHUx3acUgtLYS2XUVlbGclhnNafRO44uobOsteAG01v
+YqFa8ZaZM7qcZ88mbbKLXn6lo203JguM+TM0P7wHnzcww9sLmsP8W2cvsvefwCl4
+O7OYcjhcbEph+mIC3/zN8vF6d8xtLiMSGk6BNCHd003MBEhZHizyquGtAFLaEafX
+V6sLm65i8uF2glnQfwS5JQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICrDCCAZSgAwIBAgIEXozDxjANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1s
+b2NhdG9yLW1hZXZlMB4XDTIwMDQwNzE4MTc0MloXDTI1MDQwNzE4MTc0MlowGDEW
+MBQGA1UEAwwNbG9jYXRvci1tYWV2ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAOJ3jM2Rb50L+1fXyhZbaOHMuVUVGJ5jQV9wH3ijjeCEckaF29LbEtG8
+swMaxSoi4Sp/A4dp/7VI9CFZJKOX3zooZcuHyR7GSta4wH3oO55w0AfyTGeG6KF2
+Ekzj8pDPHyn/141rFAUPmMDnCfbF69Uixfi2XPxEJZw2GDN/YIHndY+X1pJ4ZuXS
+SmrORSEOSmrN9X7pqbL5D2cy15cmTK5449ZqLEfZS72Mv3gve1Ax2JMWCBEwLdob
+xW5utgmEe1/WhlhPzFr5C92znF/5Eucil/Rr+yynp31X+/QYBemYwOxbeZotHBZJ
+tMLMzaInydrZ04wgHRftNeN0TIZkPmcCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
+Jj1OSCWoILzWLBU1cAiQK8Gt0DVkqcpO4/vc3CoiU2T/em74cBzTwqmgrBvykWgq
+f05jWQcod2yNg8trHrgx8F9CfyyvTXRIxttyfmbD7DAQk+qn9QBSbRJFfzo8VfNp
+dGcT7KV9UDVyzltiTorqQJHUx3acUgtLYS2XUVlbGclhnNafRO44uobOsteAG01v
+YqFa8ZaZM7qcZ88mbbKLXn6lo203JguM+TM0P7wHnzcww9sLmsP8W2cvsvefwCl4
+O7OYcjhcbEph+mIC3/zN8vF6d8xtLiMSGk6BNCHd003MBEhZHizyquGtAFLaEafX
+V6sLm65i8uF2glnQfwS5JQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICtDCCAZygAwIBAgIEXozGnzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFz
+ZXJ2ZXItY2xlbWVudGluZTAeFw0yMDA0MDcxODI5NTFaFw0yNTA0MDcxODI5NTFa
+MBwxGjAYBgNVBAMMEXNlcnZlci1jbGVtZW50aW5lMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEA6IzshjujS5c58AH8nJHBhlqjfNpacoNxhxykeCVsExa9
+vi0l8ezi35pte06j7gpMWhDYHokrHaw6ymp9iTi7D91yIPGeMMNUli8DnzgAzpeY
+V8SGgkrVBalkVe0GimAHXMrzeZF+8D2BEdvDAsIUbrZRACElPlLUoiO93xZZ8ad+
+fAfLVetH4lDJ54FT7ia+St6L0QxSrDLvrqmc/58ZunkQBnQcd4tMjCD1kX4l+5Q1
+eF+Rc/SbY+/8HfyCZcA98voC3dKF13U+0YAf/0ahin+8Ckm6BL/StUxFNftTtJ7l
+iKf56Y3FbSQ84Q9Te8feb05XidkF74Gifa4Q7gOzjwIDAQABMA0GCSqGSIb3DQEB
+CwUAA4IBAQDKvYcnVFryhupo156bB33BU14KN8b5joVyQLeGb2Tx+icZd/jFhqSQ
+c3f8VV+aG9+CtRi/6wesdzf9/CVF+J4ARJ7j3i60NlJi4vQJlZnou+JSBgbBiDkW
+p12ITsw7l1k2zxH8hoMPNbMK1EC/+uwVRJt92L52uShLw9zKtE4MLZxZVa7Amkf4
+zRc78fHwwPXoMjLcQxw+8JRjlciWr/hZccuppXI4qb17l6HAMvW4vCslao0c9pSp
+Opg5Q0PwVXFROIvCANdxNI9ptSrH78Thxh4rggnHs+OZF02D22oTkjquU4Xrar3u
+FXlIS8UmdkqAXGIJf0pqa48aXcqeipRe
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICrjCCAZagAwIBAgIEXozE5DANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDDA5z
+ZXJ2ZXItZG9sb3JlczAeFw0yMDA0MDcxODIyMjhaFw0yNTA0MDcxODIyMjhaMBkx
+FzAVBgNVBAMMDnNlcnZlci1kb2xvcmVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAyRTzsWsih9Boz2/aRFJsgJNDn8/C207kpvJ9lj0uBWNdZGJ86T4i
+CwvIyMFvxeYQB0qO0AHf6FvJfMgunRlCj3fD01s7AHj8kCFoM/akgo04M7iJfSkU
+dDCVuRbrFtz31akNckyxRw/oORiQ6NYGxnuAvtFdjE8jFc77WVXVU5QuqVEueJXs
+HM+t6VGEn+7GwPsSJMIuEERd+05ZlghB1HoQD4Wu4+b/CXU+8aFRad0HRXHInBl0
+0QABETcMtpe3xIotC7H1nsAMipb0jyl3p+1a49FbrAktsiko8Y2iRVv3kZ58xfx9
+2Unmw+ViEb5bVRFytqb5AIgARI/+XX1zBwIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
+AQB39QXR3HLEju8B1oNCH1UciZetMxvORC2fwgXhqjbJ2YkHlykaLAAKv6DOSyc2
+HE40F2Q/Y0p0NC41+4YIiujgzKWaDI1Gw22PlceE2B49dO8evmldN2NixkirJbtm
+bEtjINAxHXbhXn8GgUKJxSqtFPTX/fG7OCYvkvGItQAhSrGo9r5ACuDYkTZsBAZp
+9jHc50TZsQ7od4jsPXrtZ6S2doOA0TdQ/+XzNyoadbG0YZbRtUVmhJN7gQfkBcjH
+/AnYeYJL1kg39AuO3PsFhgWCsR2eNizGCh7CnHx7xpJnLYAw/01TGidsku/oYFiI
+5SthBjGC992gTekW54hYtMBU
+-----END CERTIFICATE-----
diff --git a/sni-test-config/haproxy.cfg b/sni-test-config/haproxy.cfg
new file mode 100644
index 0000000..c451ef6
--- /dev/null
+++ b/sni-test-config/haproxy.cfg
@@ -0,0 +1,44 @@
+#
+# 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.
+#
+
+defaults
+ timeout client 1000
+ timeout connect 1000
+ timeout server 1000
+
+frontend sniproxy
+ bind *:15443
+ mode tcp
+ tcp-request inspect-delay 5s
+ tcp-request content accept if { req_ssl_hello_type 1 }
+ use_backend locators-maeve if { req.ssl_sni -i locator-maeve }
+ use_backend servers-dolores if { req.ssl_sni -i server-dolores }
+ use_backend servers-clementine if { req.ssl_sni -i server-clementine }
+ default_backend locators-maeve
+ log stdout format raw local0 debug
+
+backend locators-maeve
+ mode tcp
+ server locator1 geode:10334
+
+backend servers-dolores
+ mode tcp
+ server server1 geode:40404
+
+backend servers-clementine
+ mode tcp
+ server server1 geode:40405
diff --git a/sni-test-config/scripts/forever b/sni-test-config/scripts/forever
new file mode 100644
index 0000000..4fecfa8
--- /dev/null
+++ b/sni-test-config/scripts/forever
@@ -0,0 +1,20 @@
+#!/usr/bin/env sh
+
+#
+# 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.
+#
+
+while true; do sleep 600; done
diff --git a/sni-test-config/scripts/geode-starter-2.gfsh b/sni-test-config/scripts/geode-starter-2.gfsh
new file mode 100644
index 0000000..38600aa
--- /dev/null
+++ b/sni-test-config/scripts/geode-starter-2.gfsh
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+start locator --name=locator-maeve --hostname-for-clients=locator-maeve --properties-file=/geode/config/gemfire.properties --security-properties-file=/geode/config/gfsecurity.properties --J=-Dgemfire.ssl-keystore=/geode/config/locator-maeve-keystore.jks
+start server --name=server-dolores --group=group-dolores --hostname-for-clients=server-dolores --locators=localhost[10334] --properties-file=/geode/config/gemfire.properties --security-properties-file=/geode/config/gfsecurity.properties --J=-Dgemfire.ssl-keystore=/geode/config/server-dolores-keystore.jks
+start server --name=server-clementine --group=group-clementine --hostname-for-clients=server-clementine --server-port=40405 --locators=localhost[10334] --properties-file=/geode/config/gemfire.properties --security-properties-file=/geode/config/gfsecurity.properties --J=-Dgemfire.ssl-keystore=/geode/config/server-clementine-keystore.jks
+connect --locator=localhost[10334] --use-ssl=true --security-properties-file=/geode/config/gfsecurity.properties
+create region --name=region-dolores --group=group-dolores --type=REPLICATE
+create region --name=region-clementine --group=group-clementine --type=REPLICATE
diff --git a/sni-test-config/scripts/geode-starter.gfsh b/sni-test-config/scripts/geode-starter.gfsh
new file mode 100644
index 0000000..9ceecad
--- /dev/null
+++ b/sni-test-config/scripts/geode-starter.gfsh
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+start locator --name=locator-maeve --hostname-for-clients=locator-maeve --properties-file=/geode/config/gemfire.properties --security-properties-file=/geode/config/gfsecurity.properties --J=-Dgemfire.ssl-keystore=/geode/config/locator-maeve-keystore.jks
+start server --name=server-dolores --max-heap=256m --hostname-for-clients=server-dolores --locators=localhost[10334] --properties-file=/geode/config/gemfire.properties --security-properties-file=/geode/config/gfsecurity.properties --J=-Dgemfire.ssl-keystore=/geode/config/server-dolores-keystore.jks
+connect --locator=localhost[10334] --use-ssl=true --security-properties-file=/geode/config/gfsecurity.properties
+create region --name=jellyfish --type=REPLICATE
+
diff --git a/ssl_keys/client_keys/truststore_sni.pem b/ssl_keys/client_keys/truststore_sni.pem
new file mode 100644
index 0000000..1857ce6
--- /dev/null
+++ b/ssl_keys/client_keys/truststore_sni.pem
@@ -0,0 +1,68 @@
+-----BEGIN CERTIFICATE-----
+MIICrDCCAZSgAwIBAgIEXozDxjANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1s
+b2NhdG9yLW1hZXZlMB4XDTIwMDQwNzE4MTc0MloXDTI1MDQwNzE4MTc0MlowGDEW
+MBQGA1UEAwwNbG9jYXRvci1tYWV2ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAOJ3jM2Rb50L+1fXyhZbaOHMuVUVGJ5jQV9wH3ijjeCEckaF29LbEtG8
+swMaxSoi4Sp/A4dp/7VI9CFZJKOX3zooZcuHyR7GSta4wH3oO55w0AfyTGeG6KF2
+Ekzj8pDPHyn/141rFAUPmMDnCfbF69Uixfi2XPxEJZw2GDN/YIHndY+X1pJ4ZuXS
+SmrORSEOSmrN9X7pqbL5D2cy15cmTK5449ZqLEfZS72Mv3gve1Ax2JMWCBEwLdob
+xW5utgmEe1/WhlhPzFr5C92znF/5Eucil/Rr+yynp31X+/QYBemYwOxbeZotHBZJ
+tMLMzaInydrZ04wgHRftNeN0TIZkPmcCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
+Jj1OSCWoILzWLBU1cAiQK8Gt0DVkqcpO4/vc3CoiU2T/em74cBzTwqmgrBvykWgq
+f05jWQcod2yNg8trHrgx8F9CfyyvTXRIxttyfmbD7DAQk+qn9QBSbRJFfzo8VfNp
+dGcT7KV9UDVyzltiTorqQJHUx3acUgtLYS2XUVlbGclhnNafRO44uobOsteAG01v
+YqFa8ZaZM7qcZ88mbbKLXn6lo203JguM+TM0P7wHnzcww9sLmsP8W2cvsvefwCl4
+O7OYcjhcbEph+mIC3/zN8vF6d8xtLiMSGk6BNCHd003MBEhZHizyquGtAFLaEafX
+V6sLm65i8uF2glnQfwS5JQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICrDCCAZSgAwIBAgIEXozDxjANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1s
+b2NhdG9yLW1hZXZlMB4XDTIwMDQwNzE4MTc0MloXDTI1MDQwNzE4MTc0MlowGDEW
+MBQGA1UEAwwNbG9jYXRvci1tYWV2ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAOJ3jM2Rb50L+1fXyhZbaOHMuVUVGJ5jQV9wH3ijjeCEckaF29LbEtG8
+swMaxSoi4Sp/A4dp/7VI9CFZJKOX3zooZcuHyR7GSta4wH3oO55w0AfyTGeG6KF2
+Ekzj8pDPHyn/141rFAUPmMDnCfbF69Uixfi2XPxEJZw2GDN/YIHndY+X1pJ4ZuXS
+SmrORSEOSmrN9X7pqbL5D2cy15cmTK5449ZqLEfZS72Mv3gve1Ax2JMWCBEwLdob
+xW5utgmEe1/WhlhPzFr5C92znF/5Eucil/Rr+yynp31X+/QYBemYwOxbeZotHBZJ
+tMLMzaInydrZ04wgHRftNeN0TIZkPmcCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
+Jj1OSCWoILzWLBU1cAiQK8Gt0DVkqcpO4/vc3CoiU2T/em74cBzTwqmgrBvykWgq
+f05jWQcod2yNg8trHrgx8F9CfyyvTXRIxttyfmbD7DAQk+qn9QBSbRJFfzo8VfNp
+dGcT7KV9UDVyzltiTorqQJHUx3acUgtLYS2XUVlbGclhnNafRO44uobOsteAG01v
+YqFa8ZaZM7qcZ88mbbKLXn6lo203JguM+TM0P7wHnzcww9sLmsP8W2cvsvefwCl4
+O7OYcjhcbEph+mIC3/zN8vF6d8xtLiMSGk6BNCHd003MBEhZHizyquGtAFLaEafX
+V6sLm65i8uF2glnQfwS5JQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICtDCCAZygAwIBAgIEXozGnzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFz
+ZXJ2ZXItY2xlbWVudGluZTAeFw0yMDA0MDcxODI5NTFaFw0yNTA0MDcxODI5NTFa
+MBwxGjAYBgNVBAMMEXNlcnZlci1jbGVtZW50aW5lMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEA6IzshjujS5c58AH8nJHBhlqjfNpacoNxhxykeCVsExa9
+vi0l8ezi35pte06j7gpMWhDYHokrHaw6ymp9iTi7D91yIPGeMMNUli8DnzgAzpeY
+V8SGgkrVBalkVe0GimAHXMrzeZF+8D2BEdvDAsIUbrZRACElPlLUoiO93xZZ8ad+
+fAfLVetH4lDJ54FT7ia+St6L0QxSrDLvrqmc/58ZunkQBnQcd4tMjCD1kX4l+5Q1
+eF+Rc/SbY+/8HfyCZcA98voC3dKF13U+0YAf/0ahin+8Ckm6BL/StUxFNftTtJ7l
+iKf56Y3FbSQ84Q9Te8feb05XidkF74Gifa4Q7gOzjwIDAQABMA0GCSqGSIb3DQEB
+CwUAA4IBAQDKvYcnVFryhupo156bB33BU14KN8b5joVyQLeGb2Tx+icZd/jFhqSQ
+c3f8VV+aG9+CtRi/6wesdzf9/CVF+J4ARJ7j3i60NlJi4vQJlZnou+JSBgbBiDkW
+p12ITsw7l1k2zxH8hoMPNbMK1EC/+uwVRJt92L52uShLw9zKtE4MLZxZVa7Amkf4
+zRc78fHwwPXoMjLcQxw+8JRjlciWr/hZccuppXI4qb17l6HAMvW4vCslao0c9pSp
+Opg5Q0PwVXFROIvCANdxNI9ptSrH78Thxh4rggnHs+OZF02D22oTkjquU4Xrar3u
+FXlIS8UmdkqAXGIJf0pqa48aXcqeipRe
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICrjCCAZagAwIBAgIEXozE5DANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDDA5z
+ZXJ2ZXItZG9sb3JlczAeFw0yMDA0MDcxODIyMjhaFw0yNTA0MDcxODIyMjhaMBkx
+FzAVBgNVBAMMDnNlcnZlci1kb2xvcmVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAyRTzsWsih9Boz2/aRFJsgJNDn8/C207kpvJ9lj0uBWNdZGJ86T4i
+CwvIyMFvxeYQB0qO0AHf6FvJfMgunRlCj3fD01s7AHj8kCFoM/akgo04M7iJfSkU
+dDCVuRbrFtz31akNckyxRw/oORiQ6NYGxnuAvtFdjE8jFc77WVXVU5QuqVEueJXs
+HM+t6VGEn+7GwPsSJMIuEERd+05ZlghB1HoQD4Wu4+b/CXU+8aFRad0HRXHInBl0
+0QABETcMtpe3xIotC7H1nsAMipb0jyl3p+1a49FbrAktsiko8Y2iRVv3kZ58xfx9
+2Unmw+ViEb5bVRFytqb5AIgARI/+XX1zBwIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
+AQB39QXR3HLEju8B1oNCH1UciZetMxvORC2fwgXhqjbJ2YkHlykaLAAKv6DOSyc2
+HE40F2Q/Y0p0NC41+4YIiujgzKWaDI1Gw22PlceE2B49dO8evmldN2NixkirJbtm
+bEtjINAxHXbhXn8GgUKJxSqtFPTX/fG7OCYvkvGItQAhSrGo9r5ACuDYkTZsBAZp
+9jHc50TZsQ7od4jsPXrtZ6S2doOA0TdQ/+XzNyoadbG0YZbRtUVmhJN7gQfkBcjH
+/AnYeYJL1kg39AuO3PsFhgWCsR2eNizGCh7CnHx7xpJnLYAw/01TGidsku/oYFiI
+5SthBjGC992gTekW54hYtMBU
+-----END CERTIFICATE-----