From 162506c71c90bb0b24005f81ac0673820cf57421 Mon Sep 17 00:00:00 2001 From: Piotr Jaszczyk Date: Thu, 7 Jun 2018 08:46:51 +0200 Subject: Add SSL/TLS to client simulator Change-Id: Iedebd222be08931b95e52a84c8c4d9c0df9e1da1 Signed-off-by: Piotr Jaszczyk Issue-ID: DCAEGEN2-601 --- .../config/ArgBasedClientConfiguration.kt | 62 ++++++++++++++++++---- .../config/ClientConfiguration.kt | 6 ++- .../config/ClientSecurityConfiguration.kt | 31 +++++++++++ .../impl/VesHvClient.kt | 35 ++++++++++-- .../org.onap.dcae.collectors.veshv.main/main.kt | 21 ++++---- 5 files changed, 130 insertions(+), 25 deletions(-) create mode 100644 hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/config/ClientSecurityConfiguration.kt (limited to 'hv-collector-client-simulator/src') diff --git a/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/config/ArgBasedClientConfiguration.kt b/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/config/ArgBasedClientConfiguration.kt index 1bf9046f..628afdad 100644 --- a/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/config/ArgBasedClientConfiguration.kt +++ b/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/config/ArgBasedClientConfiguration.kt @@ -24,10 +24,15 @@ import org.apache.commons.cli.Options import org.apache.commons.cli.DefaultParser import org.apache.commons.cli.CommandLine import org.apache.commons.cli.HelpFormatter +import java.io.File +import java.nio.file.Paths internal object DefaultValues { const val MESSAGES_AMOUNT = 1 + const val PRIVATE_KEY_FILE = "/etc/ves-hv/client.key" + const val CERT_FILE = "/etc/ves-hv/client.crt" + const val TRUST_CERT_FILE = "/etc/ves-hv/trust.crt" } /** @@ -56,29 +61,67 @@ internal object ArgBasedClientConfiguration { .desc("Amount of messages to send") .build() + private val OPT_PK_FILE = Option.builder("k") + .longOpt("private-key-file") + .hasArg() + .desc("File with client private key in PEM format") + .build() + + private val OPT_CERT_FILE = Option.builder("e") + .longOpt("cert-file") + .hasArg() + .desc("File with client certificate bundle") + .build() + + private val OPT_TRUST_CERT_FILE = Option.builder("t") + .longOpt("trust-cert-file") + .hasArg() + .desc("File with trusted certificate bundle for trusting servers") + .build() + private val options by lazy { val options = Options() options.addOption(OPT_VES_PORT) options.addOption(OPT_VES_HOST) options.addOption(OPT_MESSAGES_AMOUNT) + options.addOption(OPT_PK_FILE) + options.addOption(OPT_CERT_FILE) + options.addOption(OPT_TRUST_CERT_FILE) options } fun parse(args: Array): ClientConfiguration { + + val parser = DefaultParser() try { - parser.parse(options, args).run { - return ClientConfiguration( - stringValue(OPT_VES_HOST), - intValue(OPT_VES_PORT), - intValueOrDefault(OPT_MESSAGES_AMOUNT, DefaultValues.MESSAGES_AMOUNT)) - } + val cmdLine = parser.parse(options, args) + val host = cmdLine.stringValue(OPT_VES_HOST) + val port = cmdLine.intValue(OPT_VES_PORT) + val msgsAmount = cmdLine.intValueOrDefault(OPT_MESSAGES_AMOUNT, DefaultValues.MESSAGES_AMOUNT) + return ClientConfiguration( + host, + port, + parseSecurityConfig(cmdLine), + msgsAmount) } catch (ex: Exception) { throw WrongArgumentException(ex) } } + private fun parseSecurityConfig(cmdLine: CommandLine): ClientSecurityConfiguration { + val pkFile = cmdLine.stringValue(OPT_PK_FILE, DefaultValues.PRIVATE_KEY_FILE) + val certFile = cmdLine.stringValue(OPT_CERT_FILE, DefaultValues.CERT_FILE) + val trustCertFile = cmdLine.stringValue(OPT_TRUST_CERT_FILE, DefaultValues.TRUST_CERT_FILE) + return ClientSecurityConfiguration( + privateKey = stringPathToPath(pkFile), + cert = stringPathToPath(certFile), + trustedCert = stringPathToPath(trustCertFile)) + } + + private fun stringPathToPath(path: String) = Paths.get(File(path).toURI()) + private fun CommandLine.intValueOrDefault(option: Option, default: Int) = getOptionValue(option.opt)?.toInt() ?: default @@ -88,12 +131,11 @@ internal object ArgBasedClientConfiguration { private fun CommandLine.stringValue(option: Option) = getOptionValue(option.opt) + private fun CommandLine.stringValue(option: Option, default: String) = + getOptionValue(option.opt) ?: default - class WrongArgumentException(parent: Exception) : Exception(parent.message, parent) { - fun printMessage() { - println(message) - } + class WrongArgumentException(parent: Exception) : Exception(parent.message, parent) { fun printHelp(programName: String) { val formatter = HelpFormatter() formatter.printHelp(programName, options) diff --git a/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/config/ClientConfiguration.kt b/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/config/ClientConfiguration.kt index 189dff63..e3cba57b 100644 --- a/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/config/ClientConfiguration.kt +++ b/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/config/ClientConfiguration.kt @@ -23,4 +23,8 @@ package org.onap.dcae.collectors.veshv.main.config * @author Jakub Dudycz * @since June 2018 */ -data class ClientConfiguration( val vesHost: String, val vesPort: Int ,val messagesAmount: Int) +data class ClientConfiguration( + val vesHost: String, + val vesPort: Int, + val security: ClientSecurityConfiguration, + val messagesAmount: Int) diff --git a/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/config/ClientSecurityConfiguration.kt b/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/config/ClientSecurityConfiguration.kt new file mode 100644 index 00000000..fc7cf665 --- /dev/null +++ b/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/config/ClientSecurityConfiguration.kt @@ -0,0 +1,31 @@ +/* + * ============LICENSE_START======================================================= + * dcaegen2-collectors-veshv + * ================================================================================ + * Copyright (C) 2018 NOKIA + * ================================================================================ + * 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.dcae.collectors.veshv.main.config + +import java.nio.file.Path + +/** + * @author Piotr Jaszczyk + * @since June 2018 + */ +data class ClientSecurityConfiguration( + val privateKey: Path, + val cert: Path, + val trustedCert: Path) diff --git a/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/impl/VesHvClient.kt b/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/impl/VesHvClient.kt index 108b664f..4553ab20 100644 --- a/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/impl/VesHvClient.kt +++ b/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/impl/VesHvClient.kt @@ -19,9 +19,13 @@ */ package org.onap.dcae.collectors.veshv.main.impl -import io.netty.buffer.ByteBufAllocator +import io.netty.handler.ssl.ClientAuth +import io.netty.handler.ssl.SslContext +import io.netty.handler.ssl.SslContextBuilder +import io.netty.handler.ssl.SslProvider import org.onap.dcae.collectors.veshv.domain.WireFrame import org.onap.dcae.collectors.veshv.main.config.ClientConfiguration +import org.onap.dcae.collectors.veshv.main.config.ClientSecurityConfiguration import org.onap.dcae.collectors.veshv.utils.logging.Logger import org.reactivestreams.Publisher import reactor.core.publisher.Flux @@ -37,11 +41,16 @@ import java.util.function.BiFunction */ class VesHvClient(configuration: ClientConfiguration) { - private val logger = Logger(VesHvClient::class) - private val client: TcpClient = TcpClient.create(configuration.vesHost, configuration.vesPort) + private val client: TcpClient = TcpClient.builder() + .options { opts -> + opts.host(configuration.vesHost) + .port(configuration.vesPort) + .sslContext(createSslContext(configuration.security)) + } + .build() fun send(messages: Flux) { - client.start(BiFunction { i, o -> handler(i, o, messages) }) + client.startAndAwait(BiFunction { i, o -> handler(i, o, messages) }) } // sending flux with multiple WireFrames not supported yet @@ -54,8 +63,24 @@ class VesHvClient(configuration: ClientConfiguration) { .asString(Charsets.UTF_8) .subscribe { str -> logger.info("Server response: $str") } + val frames = messages + .doOnNext { logger.info { "About to send message with ${it.payloadSize} B of payload" } } + .map { it.encode(nettyOutbound.alloc()) } + return nettyOutbound .options { it.flushOnEach() } - .send(messages.map { it.encode(ByteBufAllocator.DEFAULT) }) + .send(frames) + } + + private fun createSslContext(config: ClientSecurityConfiguration): SslContext = + SslContextBuilder.forClient() + .keyManager(config.cert.toFile(), config.privateKey.toFile()) + .trustManager(config.trustedCert.toFile()) + .sslProvider(SslProvider.OPENSSL) + .clientAuth(ClientAuth.REQUIRE) + .build() + + companion object { + private val logger = Logger(VesHvClient::class) } } diff --git a/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/main.kt b/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/main.kt index cd575683..a41035da 100644 --- a/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/main.kt +++ b/hv-collector-client-simulator/src/main/kotlin/org.onap.dcae.collectors.veshv.main/main.kt @@ -31,14 +31,17 @@ private val logger = getLogger("Simulator :: main") * @author Jakub Dudycz * @since June 2018 */ -fun main(args: Array) = try { - - val clientConfig = ArgBasedClientConfiguration.parse(args) - val messageFactory = MessageFactory() - val client = VesHvClient(clientConfig) - client.send(messageFactory.createMessageFlux(clientConfig.messagesAmount)) -} catch (e: Exception) { - logger.error(e.localizedMessage) - logger.debug("An error occurred when starting ves client", e) +fun main(args: Array) { + try { + val clientConfig = ArgBasedClientConfiguration.parse(args) + val messageFactory = MessageFactory() + val client = VesHvClient(clientConfig) + client.send(messageFactory.createMessageFlux(clientConfig.messagesAmount)) + } catch (e: ArgBasedClientConfiguration.WrongArgumentException) { + e.printHelp("java org.onap.dcae.collectors.veshv.main.MainKt") + } catch (e: Exception) { + logger.error(e.localizedMessage) + logger.debug("An error occurred when starting ves client", e) + } } -- cgit 1.2.3-korg