brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [1/3] incubator-brooklyn git commit: Use white listed protocols and ciphers for console https
Date Mon, 18 May 2015 15:12:08 GMT
Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master e4f44c88a -> de67eb0cd


Use white listed protocols and ciphers for console https

Jetty upgrade needed because of support for white-listing.


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/1de9eea5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/1de9eea5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/1de9eea5

Branch: refs/heads/master
Commit: 1de9eea5f29f055bd06b10e4760e00af31e17005
Parents: d0cbcf3
Author: Svetoslav Neykov <svetoslav.neykov@cloudsoftcorp.com>
Authored: Tue May 5 15:27:33 2015 +0300
Committer: Svetoslav Neykov <svetoslav.neykov@cloudsoftcorp.com>
Committed: Thu May 7 20:34:00 2015 +0300

----------------------------------------------------------------------
 pom.xml                                         |  2 +-
 .../brooklyn/launcher/BrooklynWebServer.java    | 97 +++++++++++++-------
 .../launcher/BrooklynWebServerTest.java         | 22 +++++
 .../java/brooklyn/rest/BrooklynWebConfig.java   | 58 ++++++++++++
 4 files changed, 144 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1de9eea5/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 962bc30..c063d79 100644
--- a/pom.xml
+++ b/pom.xml
@@ -144,7 +144,7 @@
         <sshj.version>0.8.1</sshj.version>
         <felix.framework.version>4.4.0</felix.framework.version>
         <reflections.version>0.9.9-RC1</reflections.version>
-        <jetty.version>8.1.4.v20120524</jetty.version>
+        <jetty.version>8.1.17.v20150415</jetty.version>
         <airline.version>0.6</airline.version>
         <mockwebserver.version>20121111</mockwebserver.version>
         <httpclient.version>4.2.5</httpclient.version>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1de9eea5/usage/launcher/src/main/java/brooklyn/launcher/BrooklynWebServer.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynWebServer.java b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynWebServer.java
index 8112b13..ad8bd7a 100644
--- a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynWebServer.java
+++ b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynWebServer.java
@@ -31,6 +31,7 @@ import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.EnumSet;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 
 import javax.servlet.DispatcherType;
@@ -86,6 +87,7 @@ import brooklyn.util.text.Strings;
 import brooklyn.util.web.ContextHandlerCollectionHotSwappable;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Splitter;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Maps;
@@ -181,6 +183,12 @@ public class BrooklynWebServer {
 
     @SetFromFlag
     private String trustStorePassword;
+    
+    @SetFromFlag
+    private String transportProtocols;
+    
+    @SetFromFlag
+    private String transportCiphers;
 
     private File webappTempDir;
     
@@ -235,9 +243,7 @@ public class BrooklynWebServer {
     }
     
     public boolean getHttpsEnabled() {
-        if (httpsEnabled!=null) return httpsEnabled;
-        httpsEnabled = managementContext.getConfig().getConfig(BrooklynWebConfig.HTTPS_REQUIRED);
-        return httpsEnabled;
+        return getConfig(httpsEnabled, BrooklynWebConfig.HTTPS_REQUIRED);
     }
     
     public PortRange getRequestedPort() {
@@ -352,10 +358,7 @@ public class BrooklynWebServer {
         if (server != null) throw new IllegalStateException(""+this+" already running");
 
         if (actualPort == -1){
-            PortRange portRange = requestedPort;
-            if (portRange==null) {
-                portRange = managementContext.getConfig().getConfig(BrooklynWebConfig.WEB_CONSOLE_PORT);
-            }
+            PortRange portRange = getConfig(requestedPort, BrooklynWebConfig.WEB_CONSOLE_PORT);
             if (portRange==null) {
                 portRange = getHttpsEnabled() ? httpsPort : httpPort;
             }
@@ -430,57 +433,83 @@ public class BrooklynWebServer {
         SslContextFactory sslContextFactory = new SslContextFactory();
 
         // allow webconsole keystore & related properties to be set in brooklyn.properties
-        if (Strings.isNonBlank(keystorePath)) {
-            if (keystoreUrl==null) {
-                log.warn("Deprecated 'keystorePath' used; callers should use 'keystoreUrl'");
-                keystoreUrl = keystorePath;
-            } else if (!keystoreUrl.equals(keystorePath)) {
-                log.warn("Deprecated 'keystorePath' supplied with different value than 'keystoreUrl',
preferring the latter: "+
-                    keystorePath+" / "+keystoreUrl);
-            }
-        }
-        if (keystoreUrl==null) keystoreUrl = managementContext.getConfig().getConfig(BrooklynWebConfig.KEYSTORE_URL);
-        if (keystorePassword==null) keystorePassword = managementContext.getConfig().getConfig(BrooklynWebConfig.KEYSTORE_PASSWORD);
-        if (keystoreCertAlias==null) keystoreCertAlias = managementContext.getConfig().getConfig(BrooklynWebConfig.KEYSTORE_CERTIFICATE_ALIAS);
+        String ksUrl = getKeystoreUrl();
+        String ksPassword = getConfig(keystorePassword, BrooklynWebConfig.KEYSTORE_PASSWORD);
+        String ksCertAlias = getConfig(keystoreCertAlias, BrooklynWebConfig.KEYSTORE_CERTIFICATE_ALIAS);
+        String trProtos = getConfig(transportProtocols, BrooklynWebConfig.TRANSPORT_PROTOCOLS);
+        String trCiphers = getConfig(transportCiphers, BrooklynWebConfig.TRANSPORT_CIPHERS);
         
-        if (keystoreUrl!=null) {
-            sslContextFactory.setKeyStorePath(getLocalKeyStorePath(keystoreUrl));
-            if (Strings.isEmpty(keystorePassword))
+        if (ksUrl!=null) {
+            sslContextFactory.setKeyStorePath(getLocalKeyStorePath(ksUrl));
+            if (Strings.isEmpty(ksPassword))
                 throw new IllegalArgumentException("Keystore password is required and non-empty
if keystore is specified.");
-            sslContextFactory.setKeyStorePassword(keystorePassword);
-            if (Strings.isNonEmpty(keystoreCertAlias))
-                sslContextFactory.setCertAlias(keystoreCertAlias);
+            sslContextFactory.setKeyStorePassword(ksPassword);
+            if (Strings.isNonEmpty(ksCertAlias))
+                sslContextFactory.setCertAlias(ksCertAlias);
         } else {
             log.info("No keystore specified but https enabled; creating a default keystore");
             
-            if (Strings.isEmpty(keystoreCertAlias))
-                keystoreCertAlias = "web-console";
+            if (Strings.isEmpty(ksCertAlias))
+                ksCertAlias = "web-console";
             
             // if password is blank the process will block and read from stdin !
-            if (Strings.isEmpty(keystorePassword)) {
-                keystorePassword = Identifiers.makeRandomId(8);
-                log.debug("created random password "+keystorePassword+" for ad hoc internal
keystore");
+            if (Strings.isEmpty(ksPassword)) {
+                ksPassword = Identifiers.makeRandomId(8);
+                log.debug("created random password "+ksPassword+" for ad hoc internal keystore");
             }
             
             KeyStore ks = SecureKeys.newKeyStore();
             KeyPair key = SecureKeys.newKeyPair();
             X509Certificate cert = new FluentKeySigner("brooklyn").newCertificateFor("web-console",
key);
-            ks.setKeyEntry(keystoreCertAlias, key.getPrivate(), keystorePassword.toCharArray(),
+            ks.setKeyEntry(ksCertAlias, key.getPrivate(), ksPassword.toCharArray(),
                 new Certificate[] { cert });
             
             sslContextFactory.setKeyStore(ks);
-            sslContextFactory.setKeyStorePassword(keystorePassword);
-            sslContextFactory.setCertAlias(keystoreCertAlias);
+            sslContextFactory.setKeyStorePassword(ksPassword);
+            sslContextFactory.setCertAlias(ksCertAlias);
         }
         if (!Strings.isEmpty(truststorePath)) {
             sslContextFactory.setTrustStore(checkFileExists(truststorePath, "truststore"));
             sslContextFactory.setTrustStorePassword(trustStorePassword);
         }
 
-        sslContextFactory.addExcludeProtocols("SSLv3");
+        if (Strings.isNonBlank(trProtos)) {
+            sslContextFactory.setIncludeProtocols(parseArray(trProtos));
+        }
+        if (Strings.isNonBlank(trCiphers)) {
+            sslContextFactory.setIncludeCipherSuites(parseArray(trCiphers));
+        }
         return sslContextFactory;
     }
 
+    private String[] parseArray(String list) {
+        List<String> arr = Splitter.on(",").omitEmptyStrings().trimResults().splitToList(list);
+        return arr.toArray(new String[arr.size()]);
+    }
+
+    private String getKeystoreUrl() {
+        if (keystoreUrl != null) {
+            if (Strings.isNonBlank(keystorePath) && !keystoreUrl.equals(keystorePath))
{
+                log.warn("Deprecated 'keystorePath' supplied with different value than 'keystoreUrl',
preferring the latter: "+
+                        keystorePath+" / "+keystoreUrl);
+            }
+            return keystoreUrl;
+        } else if (Strings.isNonBlank(keystorePath)) {
+            log.warn("Deprecated 'keystorePath' used; callers should use 'keystoreUrl'");
+            return keystorePath;
+        } else {
+            return managementContext.getConfig().getConfig(BrooklynWebConfig.KEYSTORE_URL);
+        }
+    }
+
+    private <T> T getConfig(T override, ConfigKey<T> key) {
+        if (override!=null) {
+            return override;
+        } else {
+            return managementContext.getConfig().getConfig(key);
+        }
+    }
+
     private String getLocalKeyStorePath(String keystoreUrl) {
         ResourceUtils res = ResourceUtils.create(this);
         res.checkUrlExists(keystoreUrl, BrooklynWebConfig.KEYSTORE_URL.getName());

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1de9eea5/usage/launcher/src/test/java/brooklyn/launcher/BrooklynWebServerTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynWebServerTest.java b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynWebServerTest.java
index 3b7e8f6..d20ee1a 100644
--- a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynWebServerTest.java
+++ b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynWebServerTest.java
@@ -19,6 +19,8 @@
 package brooklyn.launcher;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -27,6 +29,8 @@ import java.security.SecureRandom;
 import java.util.List;
 import java.util.Map;
 
+import javax.net.ssl.SSLPeerUnverifiedException;
+
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.conn.ssl.SSLSocketFactory;
 import org.apache.http.impl.client.DefaultHttpClient;
@@ -43,6 +47,7 @@ import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.rest.BrooklynWebConfig;
 import brooklyn.test.entity.LocalManagementContextForTests;
 import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.http.HttpTool;
 import brooklyn.util.http.HttpToolResponse;
 
@@ -131,6 +136,23 @@ public class BrooklynWebServerTest {
         brooklynProperties.put(BrooklynWebConfig.HTTPS_REQUIRED, true);
         brooklynProperties.put(BrooklynWebConfig.KEYSTORE_URL, getFile("server.ks"));
         brooklynProperties.put(BrooklynWebConfig.KEYSTORE_PASSWORD, "password");
+        verifyHttpsFromConfig(brooklynProperties);
+    }
+
+    @Test
+    public void verifyHttpsCiphers() throws Exception {
+        brooklynProperties.put(BrooklynWebConfig.HTTPS_REQUIRED, true);
+        brooklynProperties.put(BrooklynWebConfig.TRANSPORT_PROTOCOLS, "XXX");
+        brooklynProperties.put(BrooklynWebConfig.TRANSPORT_CIPHERS, "XXX");
+        try {
+            verifyHttpsFromConfig(brooklynProperties);
+            fail("Expected to fail due to unsupported ciphers during connection negotiation");
+        } catch (Exception e) {
+            assertTrue(Exceptions.getFirstThrowableOfType(e, SSLPeerUnverifiedException.class)
!= null, "Expected to fail due to inability to negotiate");
+        }
+    }
+
+    private void verifyHttpsFromConfig(BrooklynProperties brooklynProperties) throws Exception
{
         webServer = new BrooklynWebServer(MutableMap.of(), newManagementContext(brooklynProperties));
         webServer.start();
         

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1de9eea5/usage/rest-server/src/main/java/brooklyn/rest/BrooklynWebConfig.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/BrooklynWebConfig.java b/usage/rest-server/src/main/java/brooklyn/rest/BrooklynWebConfig.java
index 289837a..4443b00 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/BrooklynWebConfig.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/BrooklynWebConfig.java
@@ -87,6 +87,64 @@ public class BrooklynWebConfig {
             BASE_NAME+".security.keystore.certificate.alias",
             "Alias in "+KEYSTORE_URL+" for the certificate to use; defaults to the first
if not supplied");
 
+    public final static ConfigKey<String> TRANSPORT_PROTOCOLS = ConfigKeys.newStringConfigKey(
+            BASE_NAME+".security.transport.protocols",
+            "SSL/TLS protocol versions to use for web console connections",
+            "TLSv1, TLSv1.1, TLSv1.2");
+
+    // https://wiki.mozilla.org/Security/Server_Side_TLS (v3.4)
+    // http://stackoverflow.com/questions/19846020/how-to-map-a-openssls-cipher-list-to-java-jsse
+    // list created on 05.05.2015, Intermediate config from first link
+    public final static ConfigKey<String> TRANSPORT_CIPHERS = ConfigKeys.newStringConfigKey(
+            BASE_NAME+".security.transport.ciphers",
+            "SSL/TLS cipher suites to use for web console connections",
+            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,"
+
+            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,"
+
+            "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_DSS_WITH_AES_128_GCM_SHA256," +
+            "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384," +
+            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,"
+
+            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA," +
+            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,"
+
+            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA," +
+            "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA," +
+            "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256," +
+            "TLS_DHE_DSS_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA," +
+            "TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384," +
+            "TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA256," +
+            "TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA," +
+            "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA,TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,"
+
+            "TLS_SRP_SHA_WITH_AES_256_CBC_SHA,TLS_DHE_DSS_WITH_AES_256_CBC_SHA256," +
+            "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,"
+
+            "TLS_SRP_SHA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA," +
+            "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA,"
+
+            "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA," +
+            "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA,TLS_RSA_WITH_CAMELLIA_128_CBC_SHA," +
+            "TLS_RSA_WITH_3DES_EDE_CBC_SHA," +
+            // Same as above but with SSL_ prefix, IBM Java compatibility (cipher is independent
of protocol)
+            // https://www-01.ibm.com/support/knowledgecenter/SSYKE2_7.0.0/com.ibm.java.security.component.70.doc/security-component/jsse2Docs/ciphersuites.html
+            "SSL_ECDHE_RSA_WITH_AES_128_GCM_SHA256,SSL_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,"
+
+            "SSL_ECDHE_RSA_WITH_AES_256_GCM_SHA384,SSL_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,"
+
+            "SSL_DHE_RSA_WITH_AES_128_GCM_SHA256,SSL_DHE_DSS_WITH_AES_128_GCM_SHA256," +
+            "SSL_DHE_DSS_WITH_AES_256_GCM_SHA384,SSL_DHE_RSA_WITH_AES_256_GCM_SHA384," +
+            "SSL_ECDHE_RSA_WITH_AES_128_CBC_SHA256,SSL_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,"
+
+            "SSL_ECDHE_RSA_WITH_AES_128_CBC_SHA,SSL_ECDHE_ECDSA_WITH_AES_128_CBC_SHA," +
+            "SSL_ECDHE_RSA_WITH_AES_256_CBC_SHA384,SSL_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,"
+
+            "SSL_ECDHE_RSA_WITH_AES_256_CBC_SHA,SSL_ECDHE_ECDSA_WITH_AES_256_CBC_SHA," +
+            "SSL_DHE_RSA_WITH_AES_128_CBC_SHA256,SSL_DHE_RSA_WITH_AES_128_CBC_SHA," +
+            "SSL_DHE_DSS_WITH_AES_128_CBC_SHA256,SSL_DHE_RSA_WITH_AES_256_CBC_SHA256," +
+            "SSL_DHE_DSS_WITH_AES_256_CBC_SHA,SSL_DHE_RSA_WITH_AES_256_CBC_SHA," +
+            "SSL_RSA_WITH_AES_128_GCM_SHA256,SSL_RSA_WITH_AES_256_GCM_SHA384," +
+            "SSL_RSA_WITH_AES_128_CBC_SHA256,SSL_RSA_WITH_AES_256_CBC_SHA256," +
+            "SSL_RSA_WITH_AES_128_CBC_SHA,SSL_RSA_WITH_AES_256_CBC_SHA," +
+            "SSL_SRP_SHA_DSS_WITH_AES_256_CBC_SHA,SSL_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,"
+
+            "SSL_SRP_SHA_WITH_AES_256_CBC_SHA,SSL_DHE_DSS_WITH_AES_256_CBC_SHA256," +
+            "SSL_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,SSL_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,"
+
+            "SSL_SRP_SHA_WITH_AES_128_CBC_SHA,SSL_DHE_DSS_WITH_AES_128_CBC_SHA," +
+            "SSL_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,SSL_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA,"
+
+            "SSL_RSA_WITH_CAMELLIA_256_CBC_SHA,SSL_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA," +
+            "SSL_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA,SSL_RSA_WITH_CAMELLIA_128_CBC_SHA," +
+            "SSL_RSA_WITH_3DES_EDE_CBC_SHA");
+
     public final static boolean hasNoSecurityOptions(ConfigMap config) {
         return config.submap(ConfigPredicates.startingWith(BASE_NAME_SECURITY)).isEmpty();
     }


Mime
View raw message