From 4ebf74691af13ecb46bb13b7ccd7241d637de676 Mon Sep 17 00:00:00 2001 From: PatrikBuhr Date: Fri, 17 Apr 2020 08:58:02 +0200 Subject: [PATCH] Added support for https The agent listens to two ports: - 8081 for http - 8433 for https The agent can communicate using http or https. A self signed certificate is used, which built into the docker container. Change-Id: Ia54d00ea849bd656678a76dfeab5d10c6e4dc1a2 Issue-ID: NONRTRIC-195 Signed-off-by: PatrikBuhr --- policy-agent/Dockerfile | 2 +- policy-agent/config/application.yaml | 13 ++- .../java/org/oransc/policyagent/BeanFactory.java | 18 +++ .../policyagent/clients/AsyncRestClient.java | 124 ++++++++++++++------- policy-agent/src/main/resources/keystore.jks | Bin 2627 -> 2611 bytes .../org/oransc/policyagent/ApplicationTest.java | 2 +- .../org/oransc/policyagent/MockPolicyAgent.java | 1 + 7 files changed, 115 insertions(+), 45 deletions(-) diff --git a/policy-agent/Dockerfile b/policy-agent/Dockerfile index 26ce5317..da9fd448 100644 --- a/policy-agent/Dockerfile +++ b/policy-agent/Dockerfile @@ -25,7 +25,7 @@ WORKDIR /opt/app/policy-agent RUN mkdir -p /var/log/policy-agent RUN mkdir -p /opt/app/policy-agent/etc/cert/ -EXPOSE 8081 +EXPOSE 8081 8433 ADD /config/* /opt/app/policy-agent/config/ ADD target/${JAR} /opt/app/policy-agent/policy-agent.jar diff --git a/policy-agent/config/application.yaml b/policy-agent/config/application.yaml index 5416a4d2..b1284942 100644 --- a/policy-agent/config/application.yaml +++ b/policy-agent/config/application.yaml @@ -19,7 +19,14 @@ logging: org.springframework.web.reactive.function.client.ExchangeFunctions: ERROR org.oransc.policyagent: INFO file: /var/log/policy-agent/application.log -app: - filepath: /opt/app/policy-agent/config/application_configuration.json server: - port : 8081 + port : 8433 + ssl: + key-store-type: PKCS12 + key-store-password: policy_agent + key-store: classpath:keystore.jks + key-password: policy_agent +app: + filepath: /opt/app/policy-agent/config/application_configuration.json + + diff --git a/policy-agent/src/main/java/org/oransc/policyagent/BeanFactory.java b/policy-agent/src/main/java/org/oransc/policyagent/BeanFactory.java index 93e1739f..e2874cb7 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/BeanFactory.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/BeanFactory.java @@ -22,12 +22,15 @@ package org.oransc.policyagent; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.catalina.connector.Connector; import org.oransc.policyagent.clients.A1ClientFactory; import org.oransc.policyagent.configuration.ApplicationConfig; import org.oransc.policyagent.repository.Policies; import org.oransc.policyagent.repository.PolicyTypes; import org.oransc.policyagent.repository.Rics; import org.oransc.policyagent.repository.Services; +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; @@ -70,4 +73,19 @@ class BeanFactory { return new ObjectMapper(); } + @Bean + public ServletWebServerFactory servletContainer() { + TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); + tomcat.addAdditionalTomcatConnectors(getHttpConnector()); + return tomcat; + } + + private static Connector getHttpConnector() { + Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL); + connector.setScheme("http"); + connector.setPort(8081); + connector.setSecure(false); + return connector; + } + } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java index f0b2ce33..ef1acfc1 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java @@ -21,11 +21,16 @@ package org.oransc.policyagent.clients; import io.netty.channel.ChannelOption; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.timeout.ReadTimeoutHandler; import io.netty.handler.timeout.WriteTimeoutHandler; import java.lang.invoke.MethodHandles; +import javax.net.ssl.SSLException; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; @@ -45,36 +50,24 @@ import reactor.netty.tcp.TcpClient; */ public class AsyncRestClient { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private final WebClient client; + private WebClient webClient = null; private final String baseUrl; public AsyncRestClient(String baseUrl) { - - TcpClient tcpClient = TcpClient.create() // - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000) // - .doOnConnected(connection -> { - connection.addHandler(new ReadTimeoutHandler(10)); - connection.addHandler(new WriteTimeoutHandler(30)); - }); - HttpClient httpClient = HttpClient.from(tcpClient); - ReactorClientHttpConnector connector = new ReactorClientHttpConnector(httpClient); - - this.client = WebClient.builder() // - .clientConnector(connector) // - .baseUrl(baseUrl) // - .build(); - this.baseUrl = baseUrl; } public Mono> postForEntity(String uri, @Nullable String body) { logger.debug("POST uri = '{}{}''", baseUrl, uri); Mono bodyProducer = body != null ? Mono.just(body) : Mono.empty(); - RequestHeadersSpec request = client.post() // - .uri(uri) // - .contentType(MediaType.APPLICATION_JSON) // - .body(bodyProducer, String.class); - return retrieve(request); + return getWebClient() // + .flatMap(client -> { + RequestHeadersSpec request = client.post() // + .uri(uri) // + .contentType(MediaType.APPLICATION_JSON) // + .body(bodyProducer, String.class); + return retrieve(request); + }); } public Mono post(String uri, @Nullable String body) { @@ -84,29 +77,38 @@ public class AsyncRestClient { public Mono postWithAuthHeader(String uri, String body, String username, String password) { logger.debug("POST (auth) uri = '{}{}''", baseUrl, uri); - RequestHeadersSpec request = client.post() // - .uri(uri) // - .headers(headers -> headers.setBasicAuth(username, password)) // - .contentType(MediaType.APPLICATION_JSON) // - .bodyValue(body); - return retrieve(request) // - .flatMap(this::toBody); + return getWebClient() // + .flatMap(client -> { + RequestHeadersSpec request = client.post() // + .uri(uri) // + .headers(headers -> headers.setBasicAuth(username, password)) // + .contentType(MediaType.APPLICATION_JSON) // + .bodyValue(body); + return retrieve(request) // + .flatMap(this::toBody); + }); } public Mono> putForEntity(String uri, String body) { logger.debug("PUT uri = '{}{}''", baseUrl, uri); - RequestHeadersSpec request = client.put() // - .uri(uri) // - .contentType(MediaType.APPLICATION_JSON) // - .bodyValue(body); - return retrieve(request); + return getWebClient() // + .flatMap(client -> { + RequestHeadersSpec request = client.put() // + .uri(uri) // + .contentType(MediaType.APPLICATION_JSON) // + .bodyValue(body); + return retrieve(request); + }); } public Mono> putForEntity(String uri) { logger.debug("PUT uri = '{}{}''", baseUrl, uri); - RequestHeadersSpec request = client.put() // - .uri(uri); - return retrieve(request); + return getWebClient() // + .flatMap(client -> { + RequestHeadersSpec request = client.put() // + .uri(uri); + return retrieve(request); + }); } public Mono put(String uri, String body) { @@ -116,8 +118,11 @@ public class AsyncRestClient { public Mono> getForEntity(String uri) { logger.debug("GET uri = '{}{}''", baseUrl, uri); - RequestHeadersSpec request = client.get().uri(uri); - return retrieve(request); + return getWebClient() // + .flatMap(client -> { + RequestHeadersSpec request = client.get().uri(uri); + return retrieve(request); + }); } public Mono get(String uri) { @@ -127,8 +132,11 @@ public class AsyncRestClient { public Mono> deleteForEntity(String uri) { logger.debug("DELETE uri = '{}{}''", baseUrl, uri); - RequestHeadersSpec request = client.delete().uri(uri); - return retrieve(request); + return getWebClient() // + .flatMap(client -> { + RequestHeadersSpec request = client.delete().uri(uri); + return retrieve(request); + }); } public Mono delete(String uri) { @@ -160,4 +168,40 @@ public class AsyncRestClient { } } + private static SslContext createSslContext() throws SSLException { + return SslContextBuilder.forClient() // + .trustManager(InsecureTrustManagerFactory.INSTANCE) // + .build(); + } + + private static WebClient createWebClient(String baseUrl, SslContext sslContext) { + TcpClient tcpClient = TcpClient.create() // + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000) // + .secure(c -> c.sslContext(sslContext)) // + .doOnConnected(connection -> { + connection.addHandler(new ReadTimeoutHandler(10)); + connection.addHandler(new WriteTimeoutHandler(30)); + }); + HttpClient httpClient = HttpClient.from(tcpClient); + ReactorClientHttpConnector connector = new ReactorClientHttpConnector(httpClient); + + return WebClient.builder() // + .clientConnector(connector) // + .baseUrl(baseUrl) // + .build(); + } + + private Mono getWebClient() { + if (this.webClient == null) { + try { + SslContext sslContext = createSslContext(); + this.webClient = createWebClient(this.baseUrl, sslContext); + } catch (SSLException e) { + logger.error("Could not create WebClient {}", e.getMessage()); + return Mono.error(e); + } + } + return Mono.just(this.webClient); + } + } diff --git a/policy-agent/src/main/resources/keystore.jks b/policy-agent/src/main/resources/keystore.jks index 574a585b9b53e52cd97821d3e2f02e184939767f..3cd6bb7977749b1d80c1c65bd5fc3958e0abc911 100644 GIT binary patch delta 2554 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 Bqmuvt diff --git a/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java b/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java index 6905c70c..0027cca1 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java @@ -659,7 +659,7 @@ public class ApplicationTest { } private String baseUrl() { - return "http://localhost:" + port; + return "https://localhost:" + port; } private String jsonString() { diff --git a/policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java b/policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java index 2b66a353..cdf614c2 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java @@ -123,6 +123,7 @@ public class MockPolicyAgent { logger.error("Could not load json schema ", e); } } + policyTypes.put(ImmutablePolicyType.builder().name("").schema("{}").build()); } } -- 2.16.6