blob: 177570a510ca1f8b1d67c8eada77fb381f9be3d8 [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.internal.cache.wan.serial;
import static org.apache.geode.cache.ssl.TestSSLUtils.CertificateBuilder;
import static org.apache.geode.distributed.ConfigurationProperties.DISTRIBUTED_SYSTEM_ID;
import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
import static org.apache.geode.distributed.ConfigurationProperties.REMOTE_LOCATORS;
import static org.apache.geode.security.SecurableCommunicationChannels.ALL;
import static org.apache.geode.security.SecurableCommunicationChannels.GATEWAY;
import static org.apache.geode.test.awaitility.GeodeAwaitility.await;
import static org.apache.geode.test.dunit.rules.ClusterStartupRule.getCache;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.net.InetAddress;
import java.security.GeneralSecurityException;
import java.util.Properties;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionFactory;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.ssl.CertStores;
import org.apache.geode.cache.wan.GatewayReceiverFactory;
import org.apache.geode.cache.wan.GatewaySenderFactory;
import org.apache.geode.internal.AvailablePortHelper;
import org.apache.geode.internal.cache.wan.AbstractGatewaySender;
import org.apache.geode.internal.cache.wan.GatewaySenderEventRemoteDispatcher;
import org.apache.geode.test.dunit.IgnoredException;
import org.apache.geode.test.dunit.rules.ClusterStartupRule;
import org.apache.geode.test.dunit.rules.MemberVM;
public class WANHostNameVerificationDistributedTest {
private static MemberVM locator_ln;
private static MemberVM server_ln;
private static MemberVM locator_ny;
private static MemberVM server_ny;
@Rule
public ClusterStartupRule cluster = new ClusterStartupRule();
@Before
public void setupCluster() throws Exception {
IgnoredException.addIgnoredException("Connection reset");
IgnoredException.addIgnoredException("Broken pipe");
IgnoredException.addIgnoredException("Connection refused");
IgnoredException.addIgnoredException("could not get remote locator information");
IgnoredException.addIgnoredException("Unexpected IOException");
}
private void setupWanSites(CertificateBuilder locator_ln_cert, CertificateBuilder server_ln_cert,
CertificateBuilder locator_ny_cert, CertificateBuilder server_ny_cert)
throws GeneralSecurityException, IOException {
CertStores locator_ln_store = new CertStores("ln_locator", "ln_locator");
locator_ln_store.withCertificate(locator_ln_cert);
CertStores server_ln_store = new CertStores("ln_server", "ln_server");
server_ln_store.withCertificate(server_ln_cert);
CertStores locator_ny_store = new CertStores("ny_locator", "ny_locator");
locator_ny_store.withCertificate(locator_ny_cert);
CertStores server_ny_store = new CertStores("ny_server", "ny_server");
server_ny_store.withCertificate(server_ny_cert);
int site1Port =
setupWanSite1(locator_ln_store, server_ln_store, locator_ny_store, server_ny_store);
setupWanSite2(site1Port, locator_ny_store, server_ny_store, locator_ln_store, server_ln_store);
}
private int setupWanSite1(CertStores locator_ln_store, CertStores server_ln_store,
CertStores locator_ny_store, CertStores server_ny_store)
throws GeneralSecurityException, IOException {
Properties locatorSSLProps = locator_ln_store
.trustSelf()
.trust(server_ln_store.alias(), server_ln_store.certificate())
.trust(locator_ny_store.alias(), locator_ny_store.certificate())
.trust(server_ny_store.alias(), server_ny_store.certificate())
.propertiesWith(ALL, true, true);
Properties serverSSLProps = server_ln_store
.trustSelf()
.trust(locator_ln_store.alias(), locator_ln_store.certificate())
.trust(locator_ny_store.alias(), locator_ny_store.certificate())
.trust(server_ny_store.alias(), server_ny_store.certificate())
.propertiesWith(ALL, true, true);
// create a cluster
locatorSSLProps.setProperty(DISTRIBUTED_SYSTEM_ID, "1");
locator_ln = cluster.startLocatorVM(0, locatorSSLProps);
server_ln = cluster.startServerVM(1, serverSSLProps, locator_ln.getPort());
// create a region
server_ln.invoke(WANHostNameVerificationDistributedTest::createServerRegion);
locator_ln.waitUntilRegionIsReadyOnExactlyThisManyServers("/region", 1);
// create gateway sender
server_ln.invoke(WANHostNameVerificationDistributedTest::createGatewaySender);
return locator_ln.getPort();
}
private void setupWanSite2(int site1Port, CertStores locator_ny_store,
CertStores server_ny_store,
CertStores locator_ln_store, CertStores server_ln_store)
throws GeneralSecurityException, IOException {
Properties locator_ny_props = locator_ny_store
.trustSelf()
.trust(server_ln_store.alias(), server_ln_store.certificate())
.trust(server_ny_store.alias(), server_ny_store.certificate())
.trust(locator_ln_store.alias(), locator_ln_store.certificate())
.propertiesWith(ALL, true, true);
locator_ny_props.setProperty(MCAST_PORT, "0");
locator_ny_props.setProperty(DISTRIBUTED_SYSTEM_ID, "2");
locator_ny_props.setProperty(REMOTE_LOCATORS, "localhost[" + site1Port + "]");
Properties server_ny_props = server_ny_store
.trustSelf()
.trust(locator_ln_store.alias(), locator_ln_store.certificate())
.trust(locator_ny_store.alias(), locator_ny_store.certificate())
.trust(server_ln_store.alias(), server_ln_store.certificate())
.propertiesWith(ALL, true, true);
// create a cluster
locator_ny_props.setProperty(DISTRIBUTED_SYSTEM_ID, "2");
locator_ny = cluster.startLocatorVM(2, locator_ny_props);
server_ny = cluster.startServerVM(3, server_ny_props, locator_ny.getPort());
// create a region
server_ny.invoke(WANHostNameVerificationDistributedTest::createServerRegion);
locator_ny.waitUntilRegionIsReadyOnExactlyThisManyServers("/region", 1);
// create gateway sender
server_ny.invoke(WANHostNameVerificationDistributedTest::createGatewayReceiver);
}
private static void createGatewayReceiver() {
int port = AvailablePortHelper.getRandomAvailablePortForDUnitSite();
GatewayReceiverFactory gwReceiver = getCache().createGatewayReceiverFactory();
gwReceiver.setStartPort(port);
gwReceiver.setEndPort(port);
gwReceiver.create();
}
private static void createGatewaySender() {
GatewaySenderFactory gwSender = getCache().createGatewaySenderFactory();
gwSender.setBatchSize(1);
gwSender.create("ln", 2);
}
private static void createServerRegion() {
RegionFactory factory =
ClusterStartupRule.getCache().createRegionFactory(RegionShortcut.REPLICATE);
factory.addGatewaySenderId("ln");
factory.create("region");
}
private static void doPutOnSite1() {
Region<String, String> region = ClusterStartupRule.getCache().getRegion("region");
region.put("serverkey", "servervalue");
assertThat("servervalue").isEqualTo(region.get("serverkey"));
}
private static void verifySite2Received() {
Region<String, String> region = ClusterStartupRule.getCache().getRegion("region");
await()
.untilAsserted(() -> assertThat("servervalue").isEqualTo(region.get("serverkey")));
}
private static void verifySite2DidNotReceived() {
Region<String, String> region = ClusterStartupRule.getCache().getRegion("region");
await()
.untilAsserted(() -> assertThat(region.keySet().size()).isEqualTo(0));
}
@Test
public void enableHostNameValidationAcrossAllComponents() throws Exception {
// this test enables hostname validation across all components in both sites
// to test tcp clients validating hostname in servers identity across
// locator-ln -> locator-ny
// server-ln -> locator-ln
// server-ln -> locator-ny
// server-ln -> server-ny
CertificateBuilder locator_ln_cert = new CertificateBuilder()
.commonName("locator_ln")
// ClusterStartupRule uses 'localhost' as locator host
.sanDnsName(InetAddress.getLoopbackAddress().getHostName())
.sanDnsName(InetAddress.getLocalHost().getHostName())
.sanIpAddress(InetAddress.getLocalHost())
.sanIpAddress(InetAddress.getByName("0.0.0.0")); // to pass on windows
CertificateBuilder server_ln_cert = new CertificateBuilder()
.commonName("server_ln")
.sanDnsName(InetAddress.getLocalHost().getHostName())
.sanIpAddress(InetAddress.getLocalHost());
CertificateBuilder locator_ny_cert = new CertificateBuilder()
.commonName("locator_ny")
// ClusterStartupRule uses 'localhost' as locator host
.sanDnsName(InetAddress.getLoopbackAddress().getHostName())
.sanDnsName(InetAddress.getLocalHost().getHostName())
.sanDnsName(InetAddress.getLocalHost().getCanonicalHostName())
.sanIpAddress(InetAddress.getLocalHost())
.sanIpAddress(InetAddress.getByName("0.0.0.0")); // to pass on windows
CertificateBuilder server_ny_cert = new CertificateBuilder()
.commonName("server_ny")
.sanDnsName(InetAddress.getLocalHost().getHostName())
.sanDnsName(InetAddress.getLocalHost().getCanonicalHostName())
.sanIpAddress(InetAddress.getLocalHost());
setupWanSites(locator_ln_cert, server_ln_cert, locator_ny_cert, server_ny_cert);
server_ln.invoke(WANHostNameVerificationDistributedTest::doPutOnSite1);
server_ny.invoke(WANHostNameVerificationDistributedTest::verifySite2Received);
}
@Test
public void gwsenderFailsToConnectIfGWReceiverHasNoHostName() throws Exception {
CertificateBuilder gwSenderCertificate = new CertificateBuilder()
.commonName("gwsender-ln");
CertificateBuilder gwReceiverCertificate = new CertificateBuilder()
.commonName("gwreceiver-ny");
CertStores gwSender = new CertStores("gwsender", "gwsender");
gwSender.withCertificate(gwSenderCertificate);
CertStores gwReceiver = new CertStores("gwreceiver", "gwreceiver");
gwReceiver.withCertificate(gwReceiverCertificate);
Properties ln_SSLProps = gwSender
.trustSelf()
.trust(gwReceiver.alias(), gwReceiver.certificate())
.propertiesWith(GATEWAY, true, true);
Properties ny_SSLProps = gwReceiver
.trustSelf()
.trust(gwSender.alias(), gwSender.certificate())
.propertiesWith(GATEWAY, true, true);
// create a ln cluster
ln_SSLProps.setProperty(DISTRIBUTED_SYSTEM_ID, "1");
locator_ln = cluster.startLocatorVM(0, ln_SSLProps);
server_ln = cluster.startServerVM(1, ln_SSLProps, locator_ln.getPort());
// create a region in ln
server_ln.invoke(WANHostNameVerificationDistributedTest::createServerRegion);
locator_ln.waitUntilRegionIsReadyOnExactlyThisManyServers("/region", 1);
// create a gateway sender ln
server_ln.invoke(WANHostNameVerificationDistributedTest::createGatewaySender);
// create a ny cluster
ny_SSLProps.setProperty(MCAST_PORT, "0");
ny_SSLProps.setProperty(DISTRIBUTED_SYSTEM_ID, "2");
ny_SSLProps.setProperty(REMOTE_LOCATORS, "localhost[" + locator_ln.getPort() + "]");
locator_ny = cluster.startLocatorVM(2, ny_SSLProps);
server_ny = cluster.startServerVM(3, ny_SSLProps, locator_ny.getPort());
// create a region in ny
server_ny.invoke(WANHostNameVerificationDistributedTest::createServerRegion);
locator_ny.waitUntilRegionIsReadyOnExactlyThisManyServers("/region", 1);
// create a gateway sender in ny
server_ny.invoke(WANHostNameVerificationDistributedTest::createGatewayReceiver);
IgnoredException.addIgnoredException("javax.net.ssl.SSLHandshakeException");
server_ln.invoke(WANHostNameVerificationDistributedTest::doPutOnSite1);
server_ny.invoke(WANHostNameVerificationDistributedTest::verifySite2DidNotReceived);
server_ln.invoke(() -> {
AbstractGatewaySender gatewaySender =
(AbstractGatewaySender) getCache().getGatewaySender("ln");
GatewaySenderEventRemoteDispatcher dispatcher =
(GatewaySenderEventRemoteDispatcher) gatewaySender.getEventProcessor().getDispatcher();
assertThat(dispatcher.isConnectedToRemote()).isFalse();
// Close the processor so its does not retry, CSRule tearDown
// closes cache which eventually close processor. But processor
// can keep retrying between closing suspect log buffer and cache
// can continue to log connection exceptions which will pollute
// next test
gatewaySender.getEventProcessor().closeProcessor();
});
IgnoredException.removeAllExpectedExceptions();
}
}