From: MichaelMorris Date: Tue, 3 Oct 2023 08:58:40 +0000 (+0100) Subject: TLS support in sdc-fe X-Git-Tag: 1.13.6~8 X-Git-Url: https://gerrit.onap.org/r/gitweb?p=sdc.git;a=commitdiff_plain;h=95c95b08ae8fa2592852168ec11b9aff3a6a31d5 TLS support in sdc-fe Signed-off-by: MichaelMorris Issue-ID: SDC-4642 Change-Id: I960c0a114889c7b5c1c7924cefff93168132e2b6 --- diff --git a/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/org.onap.sdc.p12 b/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/org.onap.sdc.p12 deleted file mode 100644 index 446856071b..0000000000 Binary files a/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/org.onap.sdc.p12 and /dev/null differ diff --git a/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/org.onap.sdc.trust.jks b/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/org.onap.sdc.trust.jks deleted file mode 100644 index e6686cc08c..0000000000 Binary files a/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/org.onap.sdc.trust.jks and /dev/null differ diff --git a/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_6_locate_keystore.rb b/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_6_locate_keystore.rb deleted file mode 100644 index 50cb2639e8..0000000000 --- a/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_6_locate_keystore.rb +++ /dev/null @@ -1,21 +0,0 @@ -directory "Jetty_etcdir_creation" do - path "#{ENV['JETTY_BASE']}/etc" - owner "#{ENV['JETTY_USER']}" - group "#{ENV['JETTY_GROUP']}" - mode '0755' - action :create -end - -cookbook_file "#{ENV['JETTY_BASE']}/etc/org.onap.sdc.p12" do - source "org.onap.sdc.p12" - owner "#{ENV['JETTY_USER']}" - group "#{ENV['JETTY_GROUP']}" - mode 0755 -end - -cookbook_file "#{ENV['JETTY_BASE']}/etc/org.onap.sdc.trust.jks" do - source "org.onap.sdc.trust.jks" - owner "#{ENV['JETTY_USER']}" - group "#{ENV['JETTY_GROUP']}" - mode 0755 -end diff --git a/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_6_setup_key_and_trust_store.rb b/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_6_setup_key_and_trust_store.rb new file mode 100644 index 0000000000..2585d1b449 --- /dev/null +++ b/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_6_setup_key_and_trust_store.rb @@ -0,0 +1,21 @@ +#Set the http module option +if node['FE'][:tls_cert] + execute "generate-keystore" do + command "openssl pkcs12 -inkey #{node['FE'][:tls_key]} -in #{node['FE'][:tls_cert]} -export -out /tmp/keystore.pkcs12 -passin pass:#{node['FE'][:tls_password]} -passout pass:#{node['FE'][:tls_password]}" + end + + execute "import-keystore" do + command "keytool -importkeystore -srcstoretype PKCS12 -srckeystore /tmp/keystore.pkcs12 -srcstorepass #{node['FE'][:tls_password]} -destkeystore #{ENV['JETTY_BASE']}/#{node['FE'][:keystore_path]} -deststorepass #{node['FE'][:keystore_password]} -noprompt" + end +end + +if node['FE'][:ca_cert] + execute "delete-existing-ca-alias" do + command "keytool -delete -alias sdc-be -storepass #{node['FE'][:truststore_password]} -keystore #{ENV['JETTY_BASE']}/#{node['FE'][:truststore_path]}" + returns [0, 1] + end + + execute "generate-truststore" do + command "keytool -import -alias sdc-be -file #{node['FE'][:ca_cert]} -storetype JKS -keystore #{ENV['JETTY_BASE']}/#{node['FE'][:truststore_path]} -storepass #{node['FE'][:truststore_password]} -noprompt" + end +end diff --git a/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_7_create_jetty_modules.rb b/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_7_create_jetty_modules.rb index 734c05ae02..3a7433e9df 100644 --- a/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_7_create_jetty_modules.rb +++ b/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_7_create_jetty_modules.rb @@ -49,8 +49,9 @@ template "ssl-ini" do mode "0755" variables({ :https_port => "#{node['FE'][:https_port]}" , - :jetty_keystore_pwd => "#{node['jetty'][:keystore_pwd]}" , - :jetty_keymanager_pwd => "#{node['jetty'][:keymanager_pwd]}" , - :jetty_truststore_pwd => "#{node['jetty'][:truststore_pwd]}" + :keystore_path => "#{node['FE'][:keystore_path]}" , + :keystore_password => "#{node['FE'][:keystore_password]}" , + :truststore_path => "#{node['FE'][:truststore_path]}" , + :truststore_password => "#{node['FE'][:truststore_password]}" }) end diff --git a/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/ssl-ini.erb b/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/ssl-ini.erb index 278fdea2ae..d3c8bc187c 100644 --- a/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/ssl-ini.erb +++ b/catalog-fe/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/ssl-ini.erb @@ -42,33 +42,42 @@ jetty.ssl.port=<%= @https_port %> ## See http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html ## Keystore file path (relative to $jetty.base) -jetty.sslContext.keyStorePath=etc/org.onap.sdc.p12 +<% unless @keystore_path.nil? || @keystore_path.strip.empty? -%> +jetty.sslContext.keyStorePath=<%= @keystore_path %> +<% end -%> ## Truststore file path (relative to $jetty.base) -jetty.sslContext.trustStorePath=etc/org.onap.sdc.trust.jks +<% unless @truststore_path.nil? || @truststore_path.strip.empty? -%> +jetty.sslContext.trustStorePath=<%= @truststore_path %> +<% end -%> ## Keystore password -# jetty.sslContext.keyStorePassword=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 -jetty.sslContext.keyStorePassword=<%= @jetty_keystore_pwd %> +<% unless @keystore_password.nil? || @keystore_password.strip.empty? -%> +jetty.sslContext.keyStorePassword=<%= @keystore_password %> +<% end -%> ## Keystore type and provider # jetty.sslContext.keyStoreType=JKS # jetty.sslContext.keyStoreProvider= ## KeyManager password -# jetty.sslContext.keyManagerPassword=OBF:1u2u1wml1z7s1z7a1wnl1u2g -jetty.sslContext.keyManagerPassword=<%= @jetty_keymanager_pwd %> +<% unless @keystore_password.nil? || @keystore_password.strip.empty? -%> +jetty.sslContext.keyManagerPassword=<%= @keystore_password %> +<% end -%> ## Truststore password -# jetty.sslContext.trustStorePassword=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 -jetty.sslContext.trustStorePassword=<%= @jetty_truststore_pwd %> +# tp<%= @truststore_password %>end +# kp<%= @keystore_password %>end +<% unless @truststore_password.nil? || @truststore_password.strip.empty? -%> +jetty.sslContext.trustStorePassword=<%= @truststore_password %> +<% end -%> ## Truststore type and provider # jetty.sslContext.trustStoreType=JKS # jetty.sslContext.trustStoreProvider= ## whether client certificate authentication is required -# jetty.sslContext.needClientAuth=false +jetty.sslContext.needClientAuth=<%= !@truststore_password.nil? && !@truststore_password.strip.empty? %> ## Whether client certificate authentication is desired # jetty.sslContext.wantClientAuth=false diff --git a/catalog-fe/sdc-frontend/chef-solo/roles/catalog-fe.json b/catalog-fe/sdc-frontend/chef-solo/roles/catalog-fe.json index 44bb56b5be..815a38c39a 100644 --- a/catalog-fe/sdc-frontend/chef-solo/roles/catalog-fe.json +++ b/catalog-fe/sdc-frontend/chef-solo/roles/catalog-fe.json @@ -15,7 +15,7 @@ "recipe[sdc-catalog-fe::FE_3_errors_config]", "recipe[sdc-catalog-fe::FE_4_logback]", "recipe[sdc-catalog-fe::FE_5_rest_configuration]", - "recipe[sdc-catalog-fe::FE_6_locate_keystore]", + "recipe[sdc-catalog-fe::FE_6_setup_key_and_trust_store]", "recipe[sdc-catalog-fe::FE_7_create_jetty_modules]", "recipe[sdc-catalog-fe::FE_8_prepareProbeFile]" ], diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/HealthCheckScheduledTask.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/HealthCheckScheduledTask.java index 2d99f84b9a..0db9fe9a44 100644 --- a/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/HealthCheckScheduledTask.java +++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/HealthCheckScheduledTask.java @@ -37,15 +37,22 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; + +import javax.servlet.ServletException; + import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpStatus; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.onap.config.api.JettySSLUtils; import org.openecomp.sdc.common.api.Constants; import org.openecomp.sdc.common.api.HealthCheckInfo; import org.openecomp.sdc.common.api.HealthCheckWrapper; import org.openecomp.sdc.common.config.EcompErrorEnum; import org.openecomp.sdc.common.http.client.api.HttpRequest; import org.openecomp.sdc.common.http.client.api.HttpResponse; +import org.openecomp.sdc.common.http.config.ClientCertificate; import org.openecomp.sdc.common.http.config.HttpClientConfig; import org.openecomp.sdc.common.http.config.Timeouts; import org.openecomp.sdc.common.impl.ExternalConfiguration; @@ -116,7 +123,9 @@ public class HealthCheckScheduledTask implements Runnable { if (healthCheckUrl != null) { ObjectMapper mapper = new ObjectMapper(); try { - HttpResponse response = HttpRequest.get(healthCheckUrl, new HttpClientConfig(new Timeouts(connectTimeoutMs, readTimeoutMs))); + HttpClientConfig clientConfig = new HttpClientConfig(new Timeouts(connectTimeoutMs, readTimeoutMs), getHttpClientCertificate()); + + HttpResponse response = HttpRequest.get(healthCheckUrl, clientConfig); int beStatus = response.getStatusCode(); if (beStatus == HttpStatus.SC_OK || beStatus == HttpStatus.SC_INTERNAL_SERVER_ERROR) { String beJsonResponse = response.getResponse(); @@ -135,6 +144,15 @@ public class HealthCheckScheduledTask implements Runnable { String compName = requestedByBE ? Constants.HC_COMPONENT_FE : baseComponent; return Collections.singletonList(new HealthCheckInfo(compName, HealthCheckInfo.HealthCheckStatus.DOWN, null, description.toString())); } + + private ClientCertificate getHttpClientCertificate() { + ClientCertificate clientCertificate = new ClientCertificate(); + clientCertificate.setKeyStore(JettySSLUtils.getSSLConfig().getKeystorePath()); + clientCertificate.setKeyStorePassword(JettySSLUtils.getSSLConfig().getKeystorePass(), false); + clientCertificate.setTrustStore(JettySSLUtils.getSSLConfig().getTruststorePath()); + clientCertificate.setTrustStorePassword(JettySSLUtils.getSSLConfig().getTruststorePass()); + return clientCertificate; + } private String getExternalComponentHcUri(String baseComponent) { String healthCheckUri = null; @@ -197,7 +215,8 @@ public class HealthCheckScheduledTask implements Runnable { ErrorLogOptionalData errorLogOptionalData = ErrorLogOptionalData.newBuilder().targetEntity(LOG_TARGET_ENTITY_BE) .targetServiceName(LOG_SERVICE_NAME).build(); try { - HttpResponse response = HttpRequest.get(redirectedUrl, new HttpClientConfig(new Timeouts(connectTimeoutMs, readTimeoutMs))); + HttpClientConfig clientConfig = new HttpClientConfig(new Timeouts(connectTimeoutMs, readTimeoutMs), getHttpClientCertificate()); + HttpResponse response = HttpRequest.get(redirectedUrl, clientConfig); log.debug("HC call to BE - status code is {}", response.getStatusCode()); String beJsonResponse = response.getResponse(); feAggHealthCheck = getFeHealthCheckInfos(gson, beJsonResponse); diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/SSLProxyServlet.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/SSLProxyServlet.java index 891bc4ae34..0923716bd0 100644 --- a/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/SSLProxyServlet.java +++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/SSLProxyServlet.java @@ -20,9 +20,11 @@ package org.openecomp.sdc.fe.servlets; import javax.servlet.ServletException; + import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.proxy.ProxyServlet; import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.onap.config.api.JettySSLUtils; import org.openecomp.sdc.common.api.Constants; import org.openecomp.sdc.fe.config.Configuration; import org.openecomp.sdc.fe.config.ConfigurationManager; @@ -52,8 +54,17 @@ public abstract class SSLProxyServlet extends ProxyServlet { } private HttpClient getSecureHttpClient() throws ServletException { + final JettySSLUtils.JettySslConfig sslConfig = JettySSLUtils.getSSLConfig(); + SslContextFactory sslContextFactory = new SslContextFactory.Client(); + sslContextFactory.setKeyStorePath(sslConfig.getKeystorePath()); + sslContextFactory.setKeyStorePassword(sslConfig.getKeystorePass()); + sslContextFactory.setKeyManagerPassword(sslConfig.getKeystorePass()); + sslContextFactory.setTrustStorePath(sslConfig.getTruststorePath()); + sslContextFactory.setTrustStorePassword(sslConfig.getTruststorePass()); + sslContextFactory.setKeyStorePath(sslConfig.getKeystorePath()); + // Instantiate HttpClient with the SslContextFactory - final var httpClient = new HttpClient(new SslContextFactory.Client(true)); + final var httpClient = new HttpClient(sslContextFactory); // Configure HttpClient, for example: httpClient.setFollowRedirects(false); // Start HttpClient diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/http/client/api/HttpConnectionMngFactory.java b/common-app-api/src/main/java/org/openecomp/sdc/common/http/client/api/HttpConnectionMngFactory.java index 966bf857c8..8f3e460ca3 100644 --- a/common-app-api/src/main/java/org/openecomp/sdc/common/http/client/api/HttpConnectionMngFactory.java +++ b/common-app-api/src/main/java/org/openecomp/sdc/common/http/client/api/HttpConnectionMngFactory.java @@ -19,6 +19,7 @@ */ package org.openecomp.sdc.common.http.client.api; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -70,9 +71,10 @@ public class HttpConnectionMngFactory { SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); SSLConnectionSocketFactory sslsf = null; try { - sslContextBuilder.loadTrustMaterial(new TrustSelfSignedStrategy()); if (clientCertificate != null) { setClientSsl(clientCertificate, sslContextBuilder); + } else { + sslContextBuilder.loadTrustMaterial(new TrustSelfSignedStrategy()); } sslsf = new SSLConnectionSocketFactory(sslContextBuilder.build(), NoopHostnameVerifier.INSTANCE); } catch (GeneralSecurityException e) { @@ -93,6 +95,11 @@ public class HttpConnectionMngFactory { char[] keyStorePassword = clientCertificate.getKeyStorePassword().toCharArray(); KeyStore clientKeyStore = createClientKeyStore(clientCertificate.getKeyStore(), keyStorePassword); sslContextBuilder.loadKeyMaterial(clientKeyStore, keyStorePassword); + if (StringUtils.isEmpty(clientCertificate.getTrustStore())) { + sslContextBuilder.loadTrustMaterial(new TrustSelfSignedStrategy()); + } else { + sslContextBuilder.loadTrustMaterial(new File(clientCertificate.getTrustStore()), clientCertificate.getTrustStorePassword().toCharArray()); + } logger.debug("#setClientSsl - Set Client Certificate authentication"); } catch (IOException | GeneralSecurityException e) { logger.debug("#setClientSsl - Set Client Certificate authentication failed with exception, diasable client SSL authentication ", e); @@ -107,6 +114,7 @@ public class HttpConnectionMngFactory { } return keyStore; } + private String getKeyStoreType(String keyStore) { if (!StringUtils.isEmpty(keyStore)) { diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/http/config/ClientCertificate.java b/common-app-api/src/main/java/org/openecomp/sdc/common/http/config/ClientCertificate.java index 93fc3b9f0f..2946217ebd 100644 --- a/common-app-api/src/main/java/org/openecomp/sdc/common/http/config/ClientCertificate.java +++ b/common-app-api/src/main/java/org/openecomp/sdc/common/http/config/ClientCertificate.java @@ -21,24 +21,33 @@ package org.openecomp.sdc.common.http.config; import fj.data.Either; import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + import org.apache.commons.lang3.StringUtils; import org.onap.sdc.security.SecurityUtil; @EqualsAndHashCode +@Getter +@Setter public class ClientCertificate { private String keyStore; private String keyStorePassword; - + private String trustStore; + private String trustStorePassword; + public ClientCertificate() { } public ClientCertificate(ClientCertificate clientCertificate) { setKeyStore(clientCertificate.getKeyStore()); setKeyStorePassword(clientCertificate.getKeyStorePassword(), false); + setTrustStore(clientCertificate.getTrustStore()); + setTrustStorePassword(clientCertificate.getTrustStorePassword()); } - private void setKeyStorePassword(String keyStorePassword, boolean isEncoded) { + public void setKeyStorePassword(String keyStorePassword, boolean isEncoded) { validate(keyStorePassword); if (isEncoded) { Either passkey = SecurityUtil.decrypt(keyStorePassword); @@ -52,32 +61,15 @@ public class ClientCertificate { } } - public String getKeyStore() { - return keyStore; - } - public void setKeyStore(String keyStore) { validate(keyStore); this.keyStore = keyStore; } - public String getKeyStorePassword() { - return keyStorePassword; - } - public void setKeyStorePassword(String keyStorePassword) { setKeyStorePassword(keyStorePassword, true); } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("ClientCertificate [keyStore="); - builder.append(keyStore); - builder.append("]"); - return builder.toString(); - } - private void validate(String str) { if (StringUtils.isEmpty(str)) { throw new IllegalArgumentException("ClientCertificate keystore and/or kestorePassword cannot be empty");