From: ychacon Date: Wed, 13 Jan 2021 08:20:54 +0000 (+0100) Subject: HTTPS support for rApp Catalogue X-Git-Tag: 2.2.0~87^2 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=132dcaf425bc2ce6f13b0725f11a9dd99a08eccb;p=nonrtric.git HTTPS support for rApp Catalogue Issue-ID: NONRTRIC-375 Change-Id: Iaef7c5a1b55767fe96e884f2bd1c245b93e7d85d Signed-off-by: ychacon --- diff --git a/docker-compose/rapp/docker-compose.yaml b/docker-compose/rapp/docker-compose.yaml index a7f6f431..ade37f71 100644 --- a/docker-compose/rapp/docker-compose.yaml +++ b/docker-compose/rapp/docker-compose.yaml @@ -29,6 +29,6 @@ services: aliases: - r-app-catalogue ports: - - 8080:8080 - - 8433:8433 + - 8680:8680 + - 8633:8633 diff --git a/r-app-catalogue/Dockerfile b/r-app-catalogue/Dockerfile index a85f57d6..cd2efc9b 100644 --- a/r-app-catalogue/Dockerfile +++ b/r-app-catalogue/Dockerfile @@ -23,10 +23,12 @@ ARG JAR WORKDIR /opt/app/r-app-catalogue RUN mkdir -p /var/log/r-app-catalogue +RUN mkdir -p /opt/app/r-app-catalogue/etc/cert/ -EXPOSE 8081 8433 +EXPOSE 8680 8633 ADD /config/application.yaml /opt/app/r-app-catalogue/config/application.yaml +ADD /config/r-app-catalogue-keystore.jks /opt/app/r-app-catalogue/etc/cert/keystore.jks ADD target/${JAR} /opt/app/r-app-catalogue/r-app-catalogue.jar diff --git a/r-app-catalogue/config/application.yaml b/r-app-catalogue/config/application.yaml index fadf7d24..1ef0bdcc 100644 --- a/r-app-catalogue/config/application.yaml +++ b/r-app-catalogue/config/application.yaml @@ -1,4 +1,30 @@ + # ========================LICENSE_START================================= + # Copyright (C) 2021 Nordix Foundation. 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=================================== + spring: profiles: active: prod - +server: + # Configuration of the HTTP/REST server. The parameters are defined and handled by the springboot framework. + # See springboot documentation. + port : 8633 + http-port: 8680 + ssl: + key-store-type: JKS + key-store: /opt/app/r-app-catalogue/etc/cert/keystore.jks + key-store-password: r-app-catalogue + key-password: r-app-catalogue + key-alias: server-cert diff --git a/r-app-catalogue/config/r-app-catalogue-keystore.jks b/r-app-catalogue/config/r-app-catalogue-keystore.jks new file mode 100644 index 00000000..192fe173 Binary files /dev/null and b/r-app-catalogue/config/r-app-catalogue-keystore.jks differ diff --git a/r-app-catalogue/pom.xml b/r-app-catalogue/pom.xml index 07e4c39d..5da52d34 100644 --- a/r-app-catalogue/pom.xml +++ b/r-app-catalogue/pom.xml @@ -157,6 +157,17 @@ junit-jupiter-engine test + + org.springframework.boot + spring-boot-starter-test + test + + + org.apache.httpcomponents + httpclient + test + + diff --git a/r-app-catalogue/src/main/java/org/oransc/rappcatalogue/configuration/TomcatConfig.java b/r-app-catalogue/src/main/java/org/oransc/rappcatalogue/configuration/TomcatConfig.java new file mode 100644 index 00000000..a04a3323 --- /dev/null +++ b/r-app-catalogue/src/main/java/org/oransc/rappcatalogue/configuration/TomcatConfig.java @@ -0,0 +1,56 @@ +/*- + * ========================LICENSE_START================================= + * Copyright (C) 2021 Nordix Foundation. 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.oransc.rappcatalogue.configuration; + +import org.apache.catalina.connector.Connector; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.servlet.server.ServletWebServerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Configure embedded Tomcat + */ + +@Configuration +public class TomcatConfig { + + @Value("${server.http-port}") + private int httpPort = 0; + + // Embedded Tomcat with HTTP and HTTPS support + @Bean + public ServletWebServerFactory servletContainer() { + TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); + + if (httpPort > 0) { + tomcat.addAdditionalTomcatConnectors(getHttpConnector(httpPort)); + } + return tomcat; + } + + private static Connector getHttpConnector(int httpPort) { + Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL); + connector.setScheme("http"); + connector.setPort(httpPort); + connector.setSecure(false); + return connector; + } +} diff --git a/r-app-catalogue/src/test/java/org/oransc/rappcatalogue/HttpsRequestTest.java b/r-app-catalogue/src/test/java/org/oransc/rappcatalogue/HttpsRequestTest.java new file mode 100644 index 00000000..3cf2c2e7 --- /dev/null +++ b/r-app-catalogue/src/test/java/org/oransc/rappcatalogue/HttpsRequestTest.java @@ -0,0 +1,113 @@ +/*- + * ========================LICENSE_START================================= + * Copyright (C) 2021 Nordix Foundation. 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.oransc.rappcatalogue; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +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.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.client.TestRestTemplate.HttpClientOption; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.boot.web.server.AbstractConfigurableWebServerFactory; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.util.ResourceUtils; +import org.springframework.web.client.ResourceAccessException; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@TestPropertySource( + properties = { // + "server.ssl.key-store=./config/r-app-catalogue-keystore.jks", // + "server.http-port=0"}) +public class HttpsRequestTest { + + @Value("${server.ssl.key-store-password}") + private String keyStorePassword; // inject password from config + + @Value("${server.ssl.key-store}") + private String keyStore; // inject keyStore from config + + @LocalServerPort + private int port; + + @Autowired + private AbstractConfigurableWebServerFactory webServerFactory; + + @Test + public void testSsl() { + assertEquals(this.webServerFactory.getSsl().isEnabled(), true); + } + + @Test + public void rest_OverPlainHttp_GetsBadRequestRequiresTLS() throws Exception { + TestRestTemplate template = new TestRestTemplate(); + ResponseEntity responseEntity = + template.getForEntity("http://localhost:" + port + "/services", String.class); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + assertTrue(responseEntity.getBody().contains("This combination of host and port requires TLS")); + } + + @Test + public void rest_WithoutSSLConfiguration_ThrowsSSLExceptionUnableFindValidCertPath() throws Exception { + TestRestTemplate template = new TestRestTemplate(); + + ResourceAccessException thrown = assertThrows(ResourceAccessException.class, () -> { + template.getForEntity("https://localhost:" + port + "/services", String.class); + }); + assertTrue(thrown.getMessage().contains("unable to find valid certification path to requested target")); + } + + @Test + public void rest_WithTwoWaySSL_AuthenticatesAndGetsExpectedResponse() throws Exception { + + SSLContext sslContext = new SSLContextBuilder().loadKeyMaterial(ResourceUtils.getFile(keyStore), + keyStorePassword.toCharArray(), keyStorePassword.toCharArray()).build(); + + SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext); + HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build(); + HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); + RestTemplateBuilder rtb = + new RestTemplateBuilder().requestFactory(() -> factory).rootUri("https://localhost:" + port); + + TestRestTemplate template = new TestRestTemplate(rtb, null, null, HttpClientOption.SSL); + + ResponseEntity responseEntity = template.getForEntity("/services", String.class); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + assertEquals("[]", responseEntity.getBody()); + } + +}