Enabled HTTPS for sdc-workflow-designer 42/96842/5
authorr.bogacki <r.bogacki@samsung.com>
Thu, 10 Oct 2019 06:48:03 +0000 (08:48 +0200)
committerOfir Sonsino <ofir.sonsino@intl.att.com>
Tue, 15 Oct 2019 08:10:32 +0000 (08:10 +0000)
-Fixes for frontend and backend communication

Change-Id: Ic8e27e1e8f116ccef23e032fbb02a99af56fa516
Issue-ID: SDC-2479
Signed-off-by: Robert Bogacki <r.bogacki@samsung.com>
Signed-off-by: Krystian Kedron <k.kedron@partner.samsung.com>
README.md
workflow-designer-ui/docker/Dockerfile
workflow-designer-ui/docker/startup.sh
workflow-designer-ui/src/main/java/org/onap/workflow/web/SSLProxyServlet.java [new file with mode: 0644]
workflow-designer-ui/src/main/java/org/onap/workflow/web/TransparentProxy.java [deleted file]
workflow-designer-ui/src/main/webapp/WEB-INF/web.xml

index 0c09031..c37b281 100644 (file)
--- a/README.md
+++ b/README.md
@@ -152,7 +152,7 @@ protected with a password. The following command will start a backend container
 `docker run -d --name workflow-backend -e SDC_PROTOCOL=http
 -e SDC_ENDPOINT=$(docker inspect sdc-BE --format={{.NetworkSettings.IPAddress}}):8080
 -e CS_HOSTS=$(docker inspect workflow-cassandra --format={{.NetworkSettings.IPAddress}})
--e SDC_USER=workflow -e SDC_PASSWORD=<secret> -e JAVA_OPTIONS="-Xmx128m -Xms128m -Xss1m"
+-e SDC_USER=workflow -e SDC_PASSWORD=<secret> -e JAVA_OPTIONS="-Xmx128m -Xms128m -Xss1m" -p 8184:8443
 nexus3.onap.org:10001/onap/workflow-backend:latest`
 
 ### Troubleshooting
@@ -179,19 +179,20 @@ For SSL connectivity:
 - IS_HTTPS &mdash; flag to set if frontend accepts https connection from client. Default is false.
 
 - KEYSTORE_PATH 
-- KEYSTORE_PASSWORD 
-- KEYSTORE_TYPE
+- KEYSTORE_PASS
 - TRUSTSTORE_PATH 
-- TRUSTSTORE_PASSWORD
-- TRUSTSTORE_TYPE
+- TRUSTSTORE_PASS
 
 If not set then Using jetty default SSL keys.
 
 ### Example
 
 `docker run -d --name workflow-frontend
--e BACKEND=http://$(docker inspect workflow-backend --format={{.NetworkSettings.IPAddress}}):8080
--e JAVA_OPTIONS="-Xmx64m -Xms64m -Xss1m" -p 9088:8080 -p 8186:8443 -e IS_HTTPS=true nexus3.onap.org:10001/onap/workflow-frontend:latest`
+-e BACKEND=http://$(docker inspect workflow-backend --format={{.NetworkSettings.IPAddress}}):8443 -e IS_HTTPS=true 
+-e KEYSTORE_PATH="etc/org.onap.sdc.p12" -e KEYSTORE_PASS='!ppJ.JvWn0hGh)oVF]([Kv)^' 
+-e TRUSTSTORE_PATH="etc/org.onap.sdc.trust.jks" -e TRUSTSTORE_PASS="].][xgtze]hBhz*wy]}m#lf*" 
+-e JAVA_OPTIONS="-Xmx64m -Xms64m -Xss1m" -p 9088:8080 -p 8186:8443 
+-e IS_HTTPS=true nexus3.onap.org:10001/onap/workflow-frontend:latest`
 
 Notice that port 8080 of the frontend container has been
 [mapped]( https://docs.docker.com/config/containers/container-networking/#published-ports) to port 9088 of the host
@@ -323,13 +324,13 @@ corresponding section of *plugins-configuration.yaml* will look like below:
 ```
 
 - pluginId: WORKFLOW
-     pluginDiscoveryUrl: "http://workflow.example.com:9088/ping"
-     pluginSourceUrl: "http://workflow.example.com:9088"
-     pluginStateUrl: "workflowDesigner"
-     pluginDisplayOptions:
-        tab:
-            displayName: "WORKFLOW"
-            displayRoles: ["DESIGNER", "TESTER"]
+  pluginDiscoveryUrl: "http://workflow.example.com:9088/ping"
+  pluginSourceUrl: "http://workflow.example.com:9088"
+  pluginStateUrl: "workflowDesigner"
+  pluginDisplayOptions:
+    tab:
+      displayName: "WORKFLOW"
+      displayRoles: ["DESIGNER", "TESTER"]
 
 ```
 
index 2a0ef24..5813088 100644 (file)
@@ -1,4 +1,4 @@
-FROM jetty:9.4.9-alpine
+FROM jetty:9.4-jre8-alpine
 
 EXPOSE 8080
 EXPOSE 8443
@@ -7,10 +7,10 @@ USER root
 
 ARG ARTIFACT
 
-COPY org.onap.sdc.p12 org.onap.sdc.trust.jks /etc/sdc-cert/
+COPY org.onap.sdc.p12 org.onap.sdc.trust.jks ${JETTY_BASE}/etc/
 
 ADD ${ARTIFACT} ${JETTY_BASE}/webapps/
-RUN chown -R jetty:jetty ${JETTY_BASE}/webapps /etc/sdc-cert
+RUN chown -R jetty:jetty ${JETTY_BASE}/webapps ${JETTY_BASE}/etc/
 
 COPY startup.sh .
 RUN chmod 744 startup.sh
index 297be0d..b2f2d51 100644 (file)
@@ -1,26 +1,27 @@
 #!/bin/sh
+
 # adding support for https
 HTTPS_ENABLED=${IS_HTTPS:-"false"}
-
-if [ "$HTTPS_ENABLED" = "true" ]; then
+CLIENT_AUTH=${IS_CLIENT_AUTH:-"false"}
+if [ "$HTTPS_ENABLED" = "true" ]
+then
     echo "enable ssl"
-    if [ -n "$KEYSTORE_PATH" ]; then
 
-        keystore_pass="!ppJ.JvWn0hGh)oVF]([Kv)^"
-        truststore_pass="].][xgtze]hBhz*wy]}m#lf*"
+    java -jar "${JETTY_HOME}/start.jar" --add-to-start=https,ssl \
+        jetty.sslContext.keyStorePath=$KEYSTORE_PATH \
+        jetty.sslContext.keyStorePassword=$KEYSTORE_PASS \
+           jetty.sslContext.keyManagerPassword=$KEYSTORE_PASS \
+        jetty.sslContext.trustStorePath=$TRUSTSTORE_PATH \
+        jetty.sslContext.trustStorePassword=$TRUSTSTORE_PASS
+
+    echo "setting SSL environment variable"
+
+    SSL_JAVA_OPTS=" -DkeystorePath=$JETTY_BASE/$KEYSTORE_PATH -DkeystorePassword=$KEYSTORE_PASS -DkeyManagerPassword=$KEYSTORE_PASS -DtruststorePath=$JETTY_BASE/$KEYSTORE_PATH -DtruststorePassword=$TRUSTSTORE_PASS -DsslTrustAll=$TRUST_ALL"
+
+    echo $SSL_JAVA_OPTS
 
-        java -jar "${JETTY_HOME}/start.jar" --add-to-start=https,ssl \
-            jetty.sslContext.keyStorePath=$KEYSTORE_PATH \
-            jetty.sslContext.keyStorePassword=${KEYSTORE_PASS:-$keystore_pass} \
-            jetty.sslContext.trustStorePath=$TRUSTSTORE_PATH \
-            jetty.sslContext.trustStorePassword=${TRUSTSTORE_PASS:-$truststore_pass} 
-     else
-         echo "Using jetty default SSL"
-         java -jar "${JETTY_HOME}/start.jar" --add-to-start=https,ssl
-     fi
 else
     echo "no ssl required"
 fi
-
-java -DproxyTo=$BACKEND $JAVA_OPTIONS -jar $JETTY_HOME/start.jar
+java $JAVA_OPTIONS -DproxyTo=$BACKEND $SSL_JAVA_OPTS -jar $JETTY_HOME/start.jar
 
diff --git a/workflow-designer-ui/src/main/java/org/onap/workflow/web/SSLProxyServlet.java b/workflow-designer-ui/src/main/java/org/onap/workflow/web/SSLProxyServlet.java
new file mode 100644 (file)
index 0000000..775706d
--- /dev/null
@@ -0,0 +1,211 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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.
+ * ============LICENSE_END=========================================================
+ */
+
+
+package org.onap.workflow.web;
+
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.proxy.ProxyServlet;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+
+
+/***
+ * Class that provides the proxy implementation for both secured and unsecured backend connections.
+ *
+ * The following nevironment value is mandatory:
+ * proxyTo - the full URL to the backend server (including protocol and context path if relevant)
+ *
+ * In case of a secured connection (proxyTo starting with https) the following may be set:
+ * sslTrustAll - set to true if all secure connection are accepted
+ * maxPoolConnections - number of connection in the pool, only when overriding the jetty default
+ *
+ * In case of SSL and nto trusting all certificates:
+ * keystorePath - path to the keystore
+ * keystoreType - type of the keystore
+ * keystorePassword - keystore password
+ *
+ * truststorePath - path to the truststore
+ * truststoreType - type of the truststore
+ * truststorePassword - truststore password
+
+ */
+
+public class SSLProxyServlet extends ProxyServlet {
+
+
+    public static final int TIMEOUT = 600000;
+    protected static final String PROXY_TO = "proxyTo";
+    protected static final String TRUST_ALL = "sslTrustAll";
+    protected static final String MAX_POOL_CONNECTIONS = "maxPoolConnections";
+    protected static final String KEYSTORE_PATH = "keystorePath";
+    protected static final String KEYSTORE_TYPE = "keystoreType";
+    protected static final String KEYSTORE_P = "keystorePassword";
+    protected static final String KEYMANAGER_P = "keyManagerPassword";
+    protected static final String KEYSTORE_CYPHER = "keystoreCypher";
+    protected static final String TRUSTSTORE_PATH = "truststorePath";
+    protected static final String TRUSTSTORE_TYPE = "truststoreType";
+    protected static final String TRUSTSTORE_P = "truststorePassword";
+    protected static final String ENDPOINT_IDENTIFICATION_ALGORITHM = "endpointIdentificationAlgorithm";
+    private static final long serialVersionUID = 1L;
+    private static URL proxyUrl = null;
+
+
+    private static void setProxyUrl(URL proxy) {
+        SSLProxyServlet.proxyUrl = proxy;
+    }
+
+    private void initProxyUrl() throws ServletException, MalformedURLException {
+
+        if (SSLProxyServlet.proxyUrl != null)
+            return;
+        String proxyUrlStr = System.getProperty(PROXY_TO);
+        if (proxyUrlStr == null) {
+            throw new ServletException("-D" + PROXY_TO + " must be specified");
+        }
+        setProxyUrl(new URL(proxyUrlStr));
+    }
+
+
+    @Override
+    public void init() throws ServletException {
+        super.init();
+        try {
+            initProxyUrl();
+        } catch (MalformedURLException e) {
+            throw new ServletException(e);
+        }
+    }
+
+
+    @Override
+    public void sendProxyRequest(HttpServletRequest request, HttpServletResponse response, Request proxyRequest) {
+
+        @SuppressWarnings("unchecked")
+        Enumeration<String> headerNames = request.getHeaderNames();
+        while (headerNames.hasMoreElements()) {
+            String headerName = headerNames.nextElement();
+            if (!proxyRequest.getHeaders().containsKey(headerName)) {
+                String headerVal = request.getHeader(headerName);
+                proxyRequest.header(headerName, headerVal);
+            }
+        }
+        proxyRequest.getHeaders().remove(HttpHeader.HOST);
+        super.sendProxyRequest(request, response, proxyRequest);
+
+    }
+
+    @Override
+    protected HttpClient newHttpClient() {
+        // ioverride parent method to be able to create a secured client as well.
+        boolean isSecureClient = (
+            proxyUrl.getProtocol() != null &&
+                proxyUrl.getProtocol().equalsIgnoreCase(HttpScheme.HTTPS.toString()));
+        if ((isSecureClient)) {
+            String trustAll = System.getProperty(TRUST_ALL);
+            SslContextFactory sslContextFactory = null;
+            if (trustAll != null && Boolean.parseBoolean(trustAll) == Boolean.TRUE) {
+                sslContextFactory = new SslContextFactory.Client(true);
+            } else {
+                sslContextFactory = new SslContextFactory.Client(false);
+                // setting up truststore
+                sslContextFactory.setTrustStorePath(System.getProperty(TRUSTSTORE_PATH));
+                sslContextFactory.setTrustStorePassword(System.getProperty(TRUSTSTORE_P));
+                sslContextFactory.setTrustStoreType(System.getProperty(TRUSTSTORE_TYPE));
+                // setting up keystore
+                sslContextFactory.setKeyStorePath(System.getProperty(KEYSTORE_PATH));
+                sslContextFactory.setKeyStorePassword(System.getProperty(KEYSTORE_P));
+                sslContextFactory.setKeyStoreType(System.getProperty(KEYSTORE_TYPE));
+                sslContextFactory.setKeyManagerPassword(System.getProperty(KEYMANAGER_P));
+
+                if (System.getProperty(ENDPOINT_IDENTIFICATION_ALGORITHM) != null &&
+                    !System.getProperty(ENDPOINT_IDENTIFICATION_ALGORITHM).equals("")) {
+                    sslContextFactory
+                        .setEndpointIdentificationAlgorithm(System.getProperty(ENDPOINT_IDENTIFICATION_ALGORITHM));
+                }
+
+                if (System.getProperty(KEYSTORE_CYPHER) != null &&
+                    !System.getProperty(KEYSTORE_CYPHER).equals("")) {
+                    sslContextFactory.setIncludeCipherSuites(System.getProperty(KEYSTORE_CYPHER));
+                }
+            }
+
+            return new HttpClient(sslContextFactory);
+
+        } else {
+            return super.newHttpClient();
+        }
+
+    }
+
+    @Override
+    protected HttpClient createHttpClient() throws ServletException {
+
+        try {
+            initProxyUrl();
+        } catch (MalformedURLException e) {
+            throw new ServletException(e);
+        }
+        // calling the parent and setting the configuration for our implementation
+        HttpClient client = super.createHttpClient();
+        setTimeout(TIMEOUT);
+        client.setIdleTimeout(TIMEOUT);
+        client.setStopTimeout(TIMEOUT);
+        if (System.getProperty(MAX_POOL_CONNECTIONS) != null) {
+            client.setMaxConnectionsPerDestination(
+                Integer.valueOf(System.getProperty(MAX_POOL_CONNECTIONS)));
+        }
+        return client;
+
+    }
+
+
+
+    @Override
+    protected String rewriteTarget(HttpServletRequest request) {
+
+        String path = proxyUrl.getPath();
+        if (request.getServletPath() != null) {
+            path += request.getServletPath();
+        }
+        if (request.getPathInfo() != null) {
+            path += request.getPathInfo();
+        }
+
+        return URIUtil.newURI(
+            proxyUrl.getProtocol(),
+            proxyUrl.getHost(),
+            proxyUrl.getPort(),
+            path,
+            request.getQueryString());
+    }
+
+}
diff --git a/workflow-designer-ui/src/main/java/org/onap/workflow/web/TransparentProxy.java b/workflow-designer-ui/src/main/java/org/onap/workflow/web/TransparentProxy.java
deleted file mode 100644 (file)
index 3dc6523..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright © 2016-2018 European Support Limited
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      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.onap.workflow.web;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import org.eclipse.jetty.proxy.ProxyServlet;
-
-/**
- * <p>A naive implementation of transparent proxy based on
- * <a href="https://www.eclipse.org/jetty/documentation/9.4.x/proxy-servlet.html">Jetty proxy servlet</a>.
- * The only difference is that the <code>proxyTo</code> configuration parameter is taken from a JVM argument (and can
- * be therefore injected via an environment variable), instead of an <code>init-param</code> in <i>web.xml</i>.</p>
- * <p>Example: <code>java -DproxyTo=http://172.17.0.9:8080 -jar $JETTY_HOME/start.jar</code></p>
- * <p>If you get a <i>502 Bad Gateway</i> error:</p>
- * <ul>
- *     <ol>
- *         Make sure that Jetty 'proxy' module
- *         <a href="https://www.eclipse.org/jetty/documentation/9.4.x/startup-modules.html">is not enabled</a>.
- *     </ol>
- *     <ol>
- *         Check the value of <code>proxyTo</code>. Make sure it does not redirect to the proxy server itself.
- *     </ol>
- *     <ol>
- *         Make sure there is no proxy (e.g. in a corporate environment) between the servlet and <code>proxyTo</code>.
- *     </ol>
- * </ul>
- *
- * @author evitaliy
- * @since 16 Jul 2018
- */
-public class TransparentProxy extends ProxyServlet.Transparent {
-
-    @Override
-    public void init(ServletConfig config) throws ServletException {
-        super.init(new ServletConfigWrapper(config));
-    }
-
-    private class ServletConfigWrapper implements ServletConfig {
-
-        private static final String PROXY_TO = "proxyTo";
-
-        private final String proxyTo;
-        private final ServletConfig config;
-
-        ServletConfigWrapper(ServletConfig config) throws ServletException {
-
-            this.proxyTo = System.getProperty(PROXY_TO);
-            if (this.proxyTo == null) {
-                throw new ServletException("-D" + PROXY_TO + " must be specified");
-            }
-
-            this.config = config;
-        }
-
-        @Override
-        public String getServletName() {
-            return config.getServletName();
-        }
-
-        @Override
-        public ServletContext getServletContext() {
-            return config.getServletContext();
-        }
-
-        @Override
-        public String getInitParameter(String s) {
-            return PROXY_TO.equals(s) ? this.proxyTo : config.getInitParameter(s);
-        }
-
-        @Override
-        public Enumeration<String> getInitParameterNames() {
-            ArrayList<String> params = Collections.list(config.getInitParameterNames());
-            params.add(PROXY_TO);
-            return Collections.enumeration(params);
-        }
-    }
-}
index a58e127..279b405 100644 (file)
@@ -5,14 +5,13 @@
          version="4.0">
 
     <servlet>
-        <servlet-name>Transparent Proxy</servlet-name>
-        <servlet-class>org.onap.workflow.web.TransparentProxy</servlet-class>
+        <servlet-name>Backend Proxy</servlet-name>
+        <servlet-class>org.onap.workflow.web.SSLProxyServlet</servlet-class>
         <load-on-startup>1</load-on-startup>
         <async-supported>true</async-supported>
     </servlet>
-
     <servlet-mapping>
-        <servlet-name>Transparent Proxy</servlet-name>
+        <servlet-name>Backend Proxy</servlet-name>
         <url-pattern>/wf/*</url-pattern>
         <url-pattern>/v1.0/activity-spec/*</url-pattern>
     </servlet-mapping>