diff --git a/vcloud-director-nat-microservice/src/test/java/brooklyn/networking/vclouddirector/natmicroservice/NatServiceMicroserviceLiveTest.java b/vcloud-director-nat-microservice/src/test/java/brooklyn/networking/vclouddirector/natmicroservice/NatServiceMicroserviceLiveTest.java index f26500ab..22cd55d6 100644 --- a/vcloud-director-nat-microservice/src/test/java/brooklyn/networking/vclouddirector/natmicroservice/NatServiceMicroserviceLiveTest.java +++ b/vcloud-director-nat-microservice/src/test/java/brooklyn/networking/vclouddirector/natmicroservice/NatServiceMicroserviceLiveTest.java @@ -15,6 +15,11 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import com.google.common.escape.Escaper; +import com.google.common.net.UrlEscapers; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.GenericType; + import brooklyn.config.BrooklynProperties; import brooklyn.entity.basic.Entities; import brooklyn.location.jclouds.JcloudsLocation; @@ -25,23 +30,24 @@ import brooklyn.test.entity.LocalManagementContextForTests; import brooklyn.util.exceptions.Exceptions; -import com.google.common.escape.Escaper; -import com.google.common.net.UrlEscapers; -import com.sun.jersey.api.client.ClientResponse; -import com.sun.jersey.api.client.GenericType; - public class NatServiceMicroserviceLiveTest extends AbstractRestApiTest { private static final Logger LOG = LoggerFactory.getLogger(NatServiceMicroserviceLiveTest.class); private ManagementContext mgmt; private JcloudsLocation loc; + + private String trustStore; + private String trustStorePassword; @BeforeClass(alwaysRun=true) @Override public void setUp() throws Exception { mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newDefault()); loc = (JcloudsLocation) mgmt.getLocationRegistry().resolve("canopy-vCHS"); + trustStore = (String) loc.getAllConfigBag().getStringKey("trustStore"); + trustStorePassword = (String) loc.getAllConfigBag().getStringKey("trustStorePassword"); + super.setUp(); } @@ -54,7 +60,7 @@ public void tearDown() throws Exception { protected NatServiceDispatcher newNatServiceDispatcher() { return NatServiceDispatcher.builder() - .endpoint(endpoint(loc), new TrustConfig(null, null)) + .endpoint(endpoint(loc), new TrustConfig(trustStore, trustStorePassword)) .build(); } diff --git a/vcloud-director-portforwarding/src/main/java/brooklyn/networking/vclouddirector/NatDirectClient.java b/vcloud-director-portforwarding/src/main/java/brooklyn/networking/vclouddirector/NatDirectClient.java index ccb1fbb7..f14a7e2b 100644 --- a/vcloud-director-portforwarding/src/main/java/brooklyn/networking/vclouddirector/NatDirectClient.java +++ b/vcloud-director-portforwarding/src/main/java/brooklyn/networking/vclouddirector/NatDirectClient.java @@ -47,6 +47,8 @@ public NatDirectClient(JcloudsLocation loc) { .endpoint(endpoint) .identity(loc.getIdentity()) .credential(loc.getCredential()) + .trustStore((String) loc.getAllConfigBag().getStringKey("trustStore")) + .trustStorePassword((String) loc.getAllConfigBag().getStringKey("trustStorePassword")) .mutex(MutexRegistry.INSTANCE.getMutexFor(endpoint)) .build(); } diff --git a/vcloud-director/src/main/java/brooklyn/networking/vclouddirector/CustomSSLSocketFactory.java b/vcloud-director/src/main/java/brooklyn/networking/vclouddirector/CustomSSLSocketFactory.java index 346a9377..3bd94c2f 100644 --- a/vcloud-director/src/main/java/brooklyn/networking/vclouddirector/CustomSSLSocketFactory.java +++ b/vcloud-director/src/main/java/brooklyn/networking/vclouddirector/CustomSSLSocketFactory.java @@ -1,14 +1,3 @@ -/* - * ******************************************************* - * Copyright VMware, Inc. 2010-2013. All Rights Reserved. - * ******************************************************* - * - * DISCLAIMER. THIS PROGRAM IS PROVIDED TO YOU "AS IS" WITHOUT - * WARRANTIES OR CONDITIONS # OF ANY KIND, WHETHER ORAL OR WRITTEN, - * EXPRESS OR IMPLIED. THE AUTHOR SPECIFICALLY # DISCLAIMS ANY IMPLIED - * WARRANTIES OR CONDITIONS OF MERCHANTABILITY, SATISFACTORY # QUALITY, - * NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. - */ package brooklyn.networking.vclouddirector; import java.io.FileInputStream; @@ -26,43 +15,42 @@ import org.apache.http.conn.ssl.SSLSocketFactory; +import com.google.common.base.Throwables; + public class CustomSSLSocketFactory { private CustomSSLSocketFactory() { } - public static SSLSocketFactory getInstance() - throws NoSuchAlgorithmException, KeyStoreException, - CertificateException, KeyManagementException, IOException { - - TrustManagerFactory trustManagerFactory = TrustManagerFactory - .getInstance(TrustManagerFactory.getDefaultAlgorithm()); - KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); - try{ - String trustStore = System.getProperty("javax.net.ssl.trustStore"); - String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword"); - if(trustStore == null || trustStorePassword == null){ - throw new IOException("javax.net.ssl.trustStore/javax.net.ssl.trustStorePassword property - not set"); - } + public static SSLSocketFactory getInstance(String trustStore, String trustStorePassword) { + try { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); FileInputStream keystoreStream = new FileInputStream(trustStore); - try{ - keystore = KeyStore.getInstance(KeyStore.getDefaultType()); + KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); + try { keystore.load(keystoreStream, trustStorePassword.toCharArray()); - } finally{ + } finally { keystoreStream.close(); } - } catch(FileNotFoundException e){ - e.printStackTrace(); + trustManagerFactory.init(keystore); + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustManagers, null); + SSLContext.setDefault(sslContext); + return new SSLSocketFactory(sslContext, SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); + } catch (CertificateException e) { + throw Throwables.propagate(e); + } catch (NoSuchAlgorithmException e) { + throw Throwables.propagate(e); + } catch (KeyStoreException e) { + throw Throwables.propagate(e); + } catch (KeyManagementException e) { + throw Throwables.propagate(e); + } catch (FileNotFoundException e) { + throw Throwables.propagate(e); } catch (IOException e) { - e.printStackTrace(); + throw Throwables.propagate(e); } - trustManagerFactory.init(keystore); - TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, trustManagers, null); - SSLContext.setDefault(sslContext); - - return new SSLSocketFactory(sslContext, - SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); } + } diff --git a/vcloud-director/src/main/java/brooklyn/networking/vclouddirector/FakeSSLSocketFactory.java b/vcloud-director/src/main/java/brooklyn/networking/vclouddirector/FakeSSLSocketFactory.java index 3a0e6368..33a91526 100644 --- a/vcloud-director/src/main/java/brooklyn/networking/vclouddirector/FakeSSLSocketFactory.java +++ b/vcloud-director/src/main/java/brooklyn/networking/vclouddirector/FakeSSLSocketFactory.java @@ -1,14 +1,3 @@ -/* - * ******************************************************* - * Copyright VMware, Inc. 2010-2013. All Rights Reserved. - * ******************************************************* - * - * DISCLAIMER. THIS PROGRAM IS PROVIDED TO YOU "AS IS" WITHOUT - * WARRANTIES OR CONDITIONS # OF ANY KIND, WHETHER ORAL OR WRITTEN, - * EXPRESS OR IMPLIED. THE AUTHOR SPECIFICALLY # DISCLAIMS ANY IMPLIED - * WARRANTIES OR CONDITIONS OF MERCHANTABILITY, SATISFACTORY # QUALITY, - * NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. - */ package brooklyn.networking.vclouddirector; import java.security.KeyManagementException; diff --git a/vcloud-director/src/main/java/brooklyn/networking/vclouddirector/NatService.java b/vcloud-director/src/main/java/brooklyn/networking/vclouddirector/NatService.java index f692b8be..83387492 100644 --- a/vcloud-director/src/main/java/brooklyn/networking/vclouddirector/NatService.java +++ b/vcloud-director/src/main/java/brooklyn/networking/vclouddirector/NatService.java @@ -2,6 +2,7 @@ import static com.google.common.base.Preconditions.checkNotNull; +import java.io.File; import java.net.InetAddress; import java.util.ArrayList; import java.util.Iterator; @@ -15,13 +16,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import brooklyn.util.exceptions.Exceptions; -import brooklyn.util.guava.Maybe; -import brooklyn.util.net.Protocol; -import brooklyn.util.text.Strings; -import brooklyn.util.time.Duration; -import brooklyn.util.time.Time; - import com.google.common.annotations.Beta; import com.google.common.base.Objects; import com.google.common.base.Predicates; @@ -51,6 +45,14 @@ import com.vmware.vcloud.sdk.constants.Version; import com.vmware.vcloud.sdk.constants.query.QueryReferenceType; +import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.guava.Maybe; +import brooklyn.util.net.Protocol; +import brooklyn.util.os.Os; +import brooklyn.util.text.Strings; +import brooklyn.util.time.Duration; +import brooklyn.util.time.Time; + /** * For adding/removing NAT rules to vcloud-director. * @@ -477,8 +479,12 @@ protected VcloudClient newVcloudClient() { return newVcloudClient(baseUrl, identity, credential, trustStore, trustStorePassword, logLevel); } - // FIXME Don't set sysprop as could affect all other activities of the JVM! protected VcloudClient newVcloudClient(String endpoint, String identity, String credential, String trustStore, String trustStorePassword, Level logLevel) { + + if (trustStore == null) { + trustStore = getDefaultTrustStore(); + } + try { if (logLevel != null) { // Logging is extremely verbose at INFO - it logs in full every http request/response (including payload). @@ -493,6 +499,16 @@ protected VcloudClient newVcloudClient(String endpoint, String identity, String try { vcloudClient = new VcloudClient(endpoint, version); LOG.debug("VCloudClient - trying login to {} using {}", endpoint, version); + + // Performing Certificate Validation + if (Strings.isNonBlank(trustStorePassword)) { + LOG.debug("Registering HTTPS scheme using trustStore ='{}'", trustStore); + vcloudClient.registerScheme("https", 443, CustomSSLSocketFactory.getInstance(trustStore, trustStorePassword)); + } else { + LOG.warn("Registering HTTPS scheme using FakeSSLSocketFactory, as trustStore ='{}' and/or trustStorePassword are not valid.", trustStore); + vcloudClient.registerScheme("https", 443, FakeSSLSocketFactory.getInstance()); + } + vcloudClient.login(identity, credential); versionFound = true; LOG.info("VCloudClient - Logged into {} using version {}", endpoint, version); @@ -504,23 +520,29 @@ protected VcloudClient newVcloudClient(String endpoint, String identity, String if (!versionFound) { throw new IllegalStateException("Cannot login to " + endpoint + " using any of " + VCLOUD_VERSIONS); } - - // Performing Certificate Validation - if (Strings.isNonBlank(trustStore)) { - System.setProperty("javax.net.ssl.trustStore", trustStore); - System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword); - vcloudClient.registerScheme("https", 443, CustomSSLSocketFactory.getInstance()); - - } else { - LOG.warn("Ignoring the Certificate Validation using FakeSSLSocketFactory"); - vcloudClient.registerScheme("https", 443, FakeSSLSocketFactory.getInstance()); - } return vcloudClient; } catch (Exception e) { throw Exceptions.propagate(e); } } + /** + * http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#InstallationAndCustomization + * + * @return the default truststore, jssecacerts, if it exists. Otherwise, cacerts + */ + private String getDefaultTrustStore() { + String trustStore; + String trustStoreFolder = Os.mergePaths(System.getProperty("java.home"), "lib", "security"); + trustStore = Os.mergePaths(trustStoreFolder, "jssecacerts"); + if (!new File(trustStore).exists()) { + trustStore = Os.mergePaths(trustStoreFolder, "cacerts"); + } else { + throw new IllegalStateException("Cannot find a valid default truststore (jssecacerts or cacerts) in " + trustStoreFolder); + } + return trustStore; + } + private GatewayNatRuleType generateGatewayNatRule(Protocol protocol, HostAndPort original, HostAndPort translated, ReferenceType interfaceRef) { GatewayNatRuleType gatewayNatRule = new GatewayNatRuleType(); diff --git a/vcloud-director/src/test/java/brooklyn/networking/vclouddirector/SecureNatServiceLiveTest.java b/vcloud-director/src/test/java/brooklyn/networking/vclouddirector/SecureNatServiceLiveTest.java new file mode 100644 index 00000000..4d6a876d --- /dev/null +++ b/vcloud-director/src/test/java/brooklyn/networking/vclouddirector/SecureNatServiceLiveTest.java @@ -0,0 +1,96 @@ +package brooklyn.networking.vclouddirector; + +import static org.testng.Assert.assertNotNull; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.vmware.vcloud.api.rest.schema.NatRuleType; + +import brooklyn.entity.BrooklynAppLiveTestSupport; +import brooklyn.location.jclouds.JcloudsLocation; +import brooklyn.util.exceptions.Exceptions; + +/** + * Tests assume that brooklyn.properties have been configured with location specs for vCHS and TAI. + * For example: + * + *
+ * brooklyn.location.named.canopy-vCHS=jclouds:vcloud-director:https://p5v1-vcd.vchs.vmware.com/api
+ * brooklyn.location.named.canopy-vCHS.identity=jo.blogs@cloudsoftcorp.com@M123456789-1234
+ * brooklyn.location.named.canopy-vCHS.credential=pa55w0rd
+ * brooklyn.location.named.canopy-vCHS.advancednetworking.vcloud.network.id=041e176a-befc-4b28-89e2-3c5343ff4d12
+ * brooklyn.location.named.canopy-vCHS.advancednetworking.vcloud.network.publicip=23.92.230.21
+ * brooklyn.location.named.canopy-vCHS.trustStorePassword=changeit
+ *
+ * brooklyn.location.named.canopy-TAI=jclouds:vcloud-director:https://svdc.it-solutions.atos.net/api
+ * brooklyn.location.named.canopy-TAI.identity=jo.blogs@myvorg_01
+ * brooklyn.location.named.canopy-TAI.credential=pa55w0rd
+ * brooklyn.location.named.canopy-TAI.trustStore=/Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home/jre/lib/security/cacerts
+ * brooklyn.location.named.canopy-TAI.trustStorePassword=changeit
+ * 
+ * + * Notice `trustStore` will be automatically inferred as in canopy-vCHS location, but it can be overridden by using + * `trustStore` as in canopy-TAI location + */ +public class SecureNatServiceLiveTest extends BrooklynAppLiveTestSupport { + + // + private static final String LOCATION_SPEC = "canopy-vCHS"; + + private static final String LOCATION_TAI_SPEC = "canopy-TAI"; + + protected JcloudsLocation loc; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + loc = (JcloudsLocation) mgmt.getLocationRegistry().resolve(LOCATION_SPEC); + } + + // TAI (as at 2014-12-16) is running vcloud-director version 5.1 + @Test(groups="Live") + public void testGetNatRulesAtTai() throws Exception { + loc = (JcloudsLocation) mgmt.getLocationRegistry().resolve(LOCATION_TAI_SPEC); + NatService service = newServiceBuilder(loc).build(); + List rules = service.getNatRules(service.getEdgeGateway()); + assertNotNull(rules); + } + + // Simple test that just checks no errors (e.g. can authenticate etc) + @Test(groups="Live") + public void testGetNatRules() throws Exception { + NatService service = newServiceBuilder(loc).build(); + List rules = service.getNatRules(service.getEdgeGateway()); + assertNotNull(rules); + } + + private NatService.Builder newServiceBuilder(JcloudsLocation loc) { + String endpoint = loc.getEndpoint(); + + // jclouds endpoint has suffix "/api"; but VMware SDK wants it without "api" + String convertedUri; + try { + URI uri = URI.create(endpoint); + convertedUri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null).toString(); + } catch (URISyntaxException e) { + throw Exceptions.propagate(e); + } + + String trustStore = (String) loc.getAllConfigBag().getStringKey("trustStore"); // if null, it will use default trustore + String trustStorePassword = (String) loc.getAllConfigBag().getStringKey("trustStorePassword"); + assertNotNull(trustStorePassword, "trustStorePassword not set on location " + loc); + + return NatService.builder() + .identity(loc.getIdentity()) + .credential(loc.getCredential()) + .endpoint(convertedUri) + .trustStore(trustStore) + .trustStorePassword(trustStorePassword); + } +}