From d419c8d0fbfb235234c5a3f01711b1c76a9748bb Mon Sep 17 00:00:00 2001 From: MichaelMorris Date: Thu, 18 Jul 2019 11:42:07 +0000 Subject: [PATCH] Implement TLS for calls from VNFM adapter to VNFM Issue-ID: SO-2116 Change-Id: I1e5bdfcf3164545c89fb370014d49ef3ae6a9cf1 Signed-off-by: MichaelMorris --- .../mso-vnfm-adapter/mso-vnfm-etsi-adapter/pom.xml | 18 +++++++ .../vnfmadapter/extclients/SdcPackageProvider.java | 3 +- .../extclients/aai/AaiServiceProviderImpl.java | 8 ++-- .../vnfm/VnfmServiceProviderConfiguration.java | 53 +++++++++++++++++++++ .../extclients/vnfm/VnfmServiceProviderImpl.java | 2 +- .../vnfmadapter/lifecycle/LifecycleManager.java | 2 +- .../src/main/resources/application.yaml | 6 +++ .../src/main/resources/org.onap.so.trust.jks | Bin 0 -> 1413 bytes vnfm-simulator/vnfm-service/pom.xml | 18 +++++++ .../svnfm/simulator/config/ApplicationConfig.java | 10 ++-- .../simulator/controller/SvnfmController.java | 2 +- .../src/main/resources/application.yaml | 7 ++- .../src/main/resources/so-vnfm-simulator.p12 | Bin 0 -> 4079 bytes 13 files changed, 118 insertions(+), 11 deletions(-) create mode 100644 adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/resources/org.onap.so.trust.jks create mode 100644 vnfm-simulator/vnfm-service/src/main/resources/so-vnfm-simulator.p12 diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/pom.xml b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/pom.xml index 09c28f93f1..e2dd64d0f4 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/pom.xml +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/pom.xml @@ -58,6 +58,24 @@ + + + src/main/resources + true + + **/*.p12 + **/*.jks + + + + src/main/resources + false + + **/*.p12 + **/*.jks + + + diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/SdcPackageProvider.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/SdcPackageProvider.java index 57d6615d66..735e1f9d42 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/SdcPackageProvider.java +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/SdcPackageProvider.java @@ -49,6 +49,7 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import javax.net.ssl.SSLContext; import org.apache.commons.codec.binary.Base64; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; @@ -133,7 +134,7 @@ public class SdcPackageProvider { private byte[] getPackage(final String csarId) { final String SERVICE_NAME = "vnfm-adapter"; - try (CloseableHttpClient client = HttpClients.createDefault()) { + try (CloseableHttpClient client = HttpClients.custom().setSSLContext(SSLContext.getDefault()).build()) { final HttpGet httpget = new HttpGet(format(GET_PACKAGE_URL, baseUrl, csarId)); httpget.setHeader(ACCEPT, APPLICATION_OCTET_STREAM_VALUE); httpget.setHeader("X-ECOMP-InstanceID", SERVICE_NAME); diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiServiceProviderImpl.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiServiceProviderImpl.java index 1fa62efa25..019a08af78 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiServiceProviderImpl.java +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/aai/AaiServiceProviderImpl.java @@ -28,6 +28,7 @@ import org.onap.aai.domain.yang.GenericVnfs; import org.onap.aai.domain.yang.Vserver; import org.onap.so.client.aai.AAIObjectType; import org.onap.so.client.aai.entities.uri.AAIUriFactory; +import org.onap.so.client.graphinventory.entities.uri.Depth; import org.onap.vnfmadapter.v1.model.Tenant; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,8 +59,8 @@ public class AaiServiceProviderImpl implements AaiServiceProvider { @Override public GenericVnfs invokeQueryGenericVnf(final String selfLink) { return aaiClientProvider.getAaiClient() - .get(GenericVnfs.class, AAIUriFactory.createResourceUri(AAIObjectType.GENERIC_VNFS) - .queryParam("selflink", selfLink.replaceAll("https", "http"))) + .get(GenericVnfs.class, + AAIUriFactory.createResourceUri(AAIObjectType.GENERIC_VNFS).queryParam("selflink", selfLink)) .orElseGet(() -> { logger.debug("No vnf found in AAI with selflink: {}", selfLink); return null; @@ -78,7 +79,8 @@ public class AaiServiceProviderImpl implements AaiServiceProvider { @Override public EsrVnfm invokeGetVnfm(final String vnfmId) { return aaiClientProvider.getAaiClient() - .get(EsrVnfm.class, AAIUriFactory.createResourceUri(AAIObjectType.VNFM, vnfmId)).orElseGet(() -> { + .get(EsrVnfm.class, AAIUriFactory.createResourceUri(AAIObjectType.VNFM, vnfmId).depth(Depth.ONE)) + .orElseGet(() -> { logger.debug("VNFM not found in AAI"); return null; }); diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java index 2aee1c06e3..3342e0d054 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java @@ -22,16 +22,34 @@ package org.onap.so.adapters.vnfmadapter.extclients.vnfm; import static org.onap.so.client.RestTemplateConfig.CONFIGURABLE_REST_TEMPLATE; import com.google.gson.Gson; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.util.Iterator; +import java.util.ListIterator; +import javax.net.ssl.SSLContext; +import org.apache.http.client.HttpClient; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; import org.onap.so.adapters.vnfmadapter.extclients.vnfm.lcn.JSON; import org.onap.so.configuration.rest.BasicHttpHeadersProvider; import org.onap.so.configuration.rest.HttpHeadersProvider; +import org.onap.so.logging.jaxrs.filter.SpringClientFilter; import org.onap.so.rest.service.HttpRestServiceProvider; import org.onap.so.rest.service.HttpRestServiceProviderImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.GsonHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; @@ -43,6 +61,13 @@ import org.springframework.web.client.RestTemplate; @Configuration public class VnfmServiceProviderConfiguration { + private static final Logger logger = LoggerFactory.getLogger(VnfmServiceProviderConfiguration.class); + + @Value("${http.client.ssl.trust-store}") + private Resource keyStore; + @Value("${http.client.ssl.trust-store-password}") + private String keyStorePassword; + @Bean(name = "vnfmServiceProvider") public HttpRestServiceProvider httpRestServiceProvider( @Qualifier(CONFIGURABLE_REST_TEMPLATE) @Autowired final RestTemplate restTemplate) { @@ -52,6 +77,8 @@ public class VnfmServiceProviderConfiguration { private HttpRestServiceProvider getHttpRestServiceProvider(final RestTemplate restTemplate, final HttpHeadersProvider httpHeadersProvider) { setGsonMessageConverter(restTemplate); + setTrustStore(restTemplate); + removeSpringClientFilter(restTemplate); return new HttpRestServiceProviderImpl(restTemplate, httpHeadersProvider); } @@ -66,4 +93,30 @@ public class VnfmServiceProviderConfiguration { restTemplate.getMessageConverters().add(new GsonHttpMessageConverter(gson)); } + private void setTrustStore(final RestTemplate restTemplate) { + SSLContext sslContext; + try { + sslContext = new SSLContextBuilder().loadTrustMaterial(keyStore.getURL(), keyStorePassword.toCharArray()) + .build(); + logger.info("Setting truststore: {}", keyStore.getURL()); + final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext); + final HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build(); + final HttpComponentsClientHttpRequestFactory factory = + new HttpComponentsClientHttpRequestFactory(httpClient); + restTemplate.setRequestFactory(factory); + } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException + | IOException exception) { + logger.error("Error reading truststore, TLS connection to VNFM will fail.", exception); + } + } + + private void removeSpringClientFilter(final RestTemplate restTemplate) { + ListIterator interceptorIterator = restTemplate.getInterceptors().listIterator(); + while (interceptorIterator.hasNext()) { + if (interceptorIterator.next() instanceof SpringClientFilter) { + interceptorIterator.remove(); + } + } + } + } diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java index 0b5b09ae39..c470008d08 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java @@ -54,7 +54,7 @@ public class VnfmServiceProviderImpl implements VnfmServiceProvider { @Override public Optional getVnf(final String vnfSelfLink) { - return httpServiceProvider.get(vnfSelfLink.replaceAll("https", "http"), InlineResponse201.class); + return httpServiceProvider.get(vnfSelfLink, InlineResponse201.class); } @Override diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java index a4f7d3206d..fa2fa30b4a 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java @@ -114,7 +114,7 @@ public class LifecycleManager { private String getSelfLink(final InlineResponse201 vnfmResponse, final EsrVnfm vnfm) { if (vnfmResponse.getLinks() != null && vnfmResponse.getLinks().getSelf() != null && vnfmResponse.getLinks().getSelf().getHref() != null) { - return vnfmResponse.getLinks().getSelf().getHref().replaceAll("https", "http"); + return vnfmResponse.getLinks().getSelf().getHref(); } return vnfm.getEsrSystemInfoList().getEsrSystemInfo().iterator().next().getServiceUrl() + "/vnf_instances/" + vnfmResponse.getId(); diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/resources/application.yaml b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/resources/application.yaml index 951d4a3bb9..0bd63dffa9 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/resources/application.yaml +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/resources/application.yaml @@ -23,6 +23,12 @@ spring: http: converters: preferred-json-mapper: gson + +http: + client: + ssl: + trust-store: classpath:org.onap.so.trust.jks + trust-store-password: ',sx#.C*W)]wVgJC6ccFHI#:H' server: port: 9092 diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/resources/org.onap.so.trust.jks b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/resources/org.onap.so.trust.jks new file mode 100644 index 0000000000000000000000000000000000000000..1f0d8a550a92bc44003e36b849884d5d8e013dc9 GIT binary patch literal 1413 zcmb7DYdF&j9RBa7<#DuzKJP^cnMcyF$&LM-5dstSt6 z9DB|uRR}~G0e~hrB&ebQqr#O{5YBEkHg-5wPX2| zKEwX!$fvP`O*!q?AI)?zl zq7E_=!b*KtGvB7d+XD2G$I7w_q6vq67DWRp+SS<89o=YAr)$#9*fn>obUHm`srkO` zwPOkH743qkP!(XS83Z3F_y*rFFy3q#8(*CrPY$h6O zro693AK+!7#;q`&xXt%QI_Z(_pJNqY4|xULIiUuHK>$eES&$CutVd7>2}i@Xx>4~< zj%yHZ3>jz)e$BVf(`I{#ml)Pu-tHki>qo%BxHtG zaXiyeQBW6pN-`Dn(%nfiIh5k7o z?ty7}k%QZa2fcK@GB2L&HtOPj%YIr3FAlvjvHeO!^6i4Vw)Ck}thU}LvDQcvM;-c~;@w15NnG3zCn40IjByPCi z;f|<8BdSAfOS|LI!4Q4OyWU2-ksPEfJnG%|_{_ti?tlr;CNaYL8$#;Jo+l?4SIXbYwp6+=jXLG!Z^grD2l(i SNV>spring-boot-maven-plugin + + + src/main/resources + true + + **/*.p12 + **/*.jks + + + + src/main/resources + false + + **/*.p12 + **/*.jks + + + diff --git a/vnfm-simulator/vnfm-service/src/main/java/org/onap/svnfm/simulator/config/ApplicationConfig.java b/vnfm-simulator/vnfm-service/src/main/java/org/onap/svnfm/simulator/config/ApplicationConfig.java index 91b79754a5..32c05ebca8 100644 --- a/vnfm-simulator/vnfm-service/src/main/java/org/onap/svnfm/simulator/config/ApplicationConfig.java +++ b/vnfm-simulator/vnfm-service/src/main/java/org/onap/svnfm/simulator/config/ApplicationConfig.java @@ -4,6 +4,7 @@ import java.net.InetAddress; import java.util.Arrays; import org.onap.svnfm.simulator.constants.Constant; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.cache.Cache; @@ -19,6 +20,9 @@ public class ApplicationConfig implements ApplicationRunner { private static final String PORT = "local.server.port"; + @Value("${server.dns.name:so-vnfm-simulator.onap}") + private String serverDnsName; + @Autowired private Environment environment; @@ -26,7 +30,7 @@ public class ApplicationConfig implements ApplicationRunner { @Override public void run(final ApplicationArguments args) throws Exception { - baseUrl = "http://" + InetAddress.getLocalHost().getHostAddress() + ":" + environment.getProperty(PORT); + baseUrl = "https://" + serverDnsName + ":" + environment.getProperty(PORT); } public String getBaseUrl() { @@ -35,8 +39,8 @@ public class ApplicationConfig implements ApplicationRunner { @Bean public CacheManager cacheManager() { - Cache inlineResponse201 = new ConcurrentMapCache(Constant.IN_LINE_RESPONSE_201_CACHE); - SimpleCacheManager manager = new SimpleCacheManager(); + final Cache inlineResponse201 = new ConcurrentMapCache(Constant.IN_LINE_RESPONSE_201_CACHE); + final SimpleCacheManager manager = new SimpleCacheManager(); manager.setCaches(Arrays.asList(inlineResponse201)); return manager; } diff --git a/vnfm-simulator/vnfm-service/src/main/java/org/onap/svnfm/simulator/controller/SvnfmController.java b/vnfm-simulator/vnfm-service/src/main/java/org/onap/svnfm/simulator/controller/SvnfmController.java index 9c3a02d4e6..d3ff66aed0 100644 --- a/vnfm-simulator/vnfm-service/src/main/java/org/onap/svnfm/simulator/controller/SvnfmController.java +++ b/vnfm-simulator/vnfm-service/src/main/java/org/onap/svnfm/simulator/controller/SvnfmController.java @@ -168,6 +168,6 @@ public class SvnfmController { final HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - return new ResponseEntity<>(response, headers, HttpStatus.OK); + return new ResponseEntity<>(response, headers, HttpStatus.CREATED); } } diff --git a/vnfm-simulator/vnfm-service/src/main/resources/application.yaml b/vnfm-simulator/vnfm-service/src/main/resources/application.yaml index 2ef302ce25..ea8105d891 100644 --- a/vnfm-simulator/vnfm-service/src/main/resources/application.yaml +++ b/vnfm-simulator/vnfm-service/src/main/resources/application.yaml @@ -34,6 +34,11 @@ server: port: 9093 tomcat: max-threads: 50 + ssl: + key-alias: so@so.onap.org + key--store-password: '7Em3&j4.19xYiMelhD5?xbQ.' + key-store: classpath:so-vnfm-simulator.p12 + key-store-type: PKCS12 vnfds: vnfdlist: @@ -56,4 +61,4 @@ vnfds: - vnfcid: VNFC4 resourceTemplateId: vnfd2_vnfc4 vduId: vnfd2_vduForVnfc4 - type: COMPUTE \ No newline at end of file + type: COMPUTE diff --git a/vnfm-simulator/vnfm-service/src/main/resources/so-vnfm-simulator.p12 b/vnfm-simulator/vnfm-service/src/main/resources/so-vnfm-simulator.p12 new file mode 100644 index 0000000000000000000000000000000000000000..7ac02855bc121dcd77a9793c2772265b9103616e GIT binary patch literal 4079 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 K`Tc;A`Tqj$w7I$f literal 0 HcmV?d00001 -- 2.16.6