Add :node-api, factor out some artemis code

This commit is contained in:
Andras Slemmer 2017-03-17 10:33:01 +00:00
parent 486368d926
commit c3c1f3d801
24 changed files with 270 additions and 175 deletions

View File

@ -2,6 +2,7 @@ package net.corda.client.model
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
import net.corda.config.SSLConfiguration
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.StateMachineInfo import net.corda.core.messaging.StateMachineInfo
@ -11,7 +12,6 @@ import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.messaging.CordaRPCClient import net.corda.node.services.messaging.CordaRPCClient
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject

52
node-api/build.gradle Normal file
View File

@ -0,0 +1,52 @@
apply plugin: 'kotlin'
apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.publish-utils'
description 'Corda node Artemis API'
buildscript {
repositories {
mavenCentral()
}
}
repositories {
mavenLocal()
mavenCentral()
jcenter()
maven {
url 'http://oss.sonatype.org/content/repositories/snapshots'
}
maven {
url 'https://dl.bintray.com/kotlin/exposed'
}
}
sourceSets {
test {
resources {
srcDir "../config/test"
}
}
main {
resources {
srcDir "../config/dev"
}
}
}
dependencies {
compile project(":core")
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "org.jetbrains.kotlinx:kotlinx-support-jdk8:0.3"
compile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
compile "org.apache.activemq:artemis-server:${artemis_version}"
compile "org.apache.activemq:artemis-core-client:${artemis_version}"
compile "org.apache.activemq:artemis-commons:${artemis_version}"
runtime "org.apache.activemq:artemis-amqp-protocol:${artemis_version}"
// TypeSafe Config: for simple and human friendly config files.
compile "com.typesafe:config:$typesafe_config_version"
}

View File

@ -0,0 +1,75 @@
package net.corda.config
import com.google.common.net.HostAndPort
import com.typesafe.config.Config
import java.net.URL
import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.Paths
import java.time.Instant
import java.time.LocalDate
import java.util.*
import kotlin.reflect.KProperty
import kotlin.reflect.jvm.javaType
private fun <T : Enum<T>> enumBridge(clazz: Class<T>, enumValueString: String): T {
return java.lang.Enum.valueOf(clazz, enumValueString)
}
private class DummyEnum : Enum<DummyEnum>("", 0)
@Suppress("UNCHECKED_CAST", "PLATFORM_CLASS_MAPPED_TO_KOTLIN")
operator fun <T> Config.getValue(receiver: Any, metadata: KProperty<*>): T {
if (metadata.returnType.isMarkedNullable && !hasPath(metadata.name)) {
return null as T
}
val returnType = metadata.returnType.javaType
return when (metadata.returnType.javaType) {
String::class.java -> getString(metadata.name) as T
Int::class.java -> getInt(metadata.name) as T
Integer::class.java -> getInt(metadata.name) as T
Long::class.java -> getLong(metadata.name) as T
Double::class.java -> getDouble(metadata.name) as T
Boolean::class.java -> getBoolean(metadata.name) as T
LocalDate::class.java -> LocalDate.parse(getString(metadata.name)) as T
Instant::class.java -> Instant.parse(getString(metadata.name)) as T
HostAndPort::class.java -> HostAndPort.fromString(getString(metadata.name)) as T
Path::class.java -> Paths.get(getString(metadata.name)) as T
URL::class.java -> URL(getString(metadata.name)) as T
Properties::class.java -> getProperties(metadata.name) as T
else -> {
if (returnType is Class<*> && Enum::class.java.isAssignableFrom(returnType)) {
return enumBridge(returnType as Class<DummyEnum>, getString(metadata.name)) as T
}
throw IllegalArgumentException("Unsupported type ${metadata.returnType}")
}
}
}
/**
* Helper class for optional configurations
*/
class OptionalConfig<out T>(val conf: Config, val lambda: () -> T) {
operator fun getValue(receiver: Any, metadata: KProperty<*>): T {
return if (conf.hasPath(metadata.name)) conf.getValue(receiver, metadata) else lambda()
}
}
fun <T> Config.getOrElse(lambda: () -> T): OptionalConfig<T> = OptionalConfig(this, lambda)
fun Config.getProperties(path: String): Properties {
val obj = this.getObject(path)
val props = Properties()
for ((property, objectValue) in obj.entries) {
props.setProperty(property, objectValue.unwrapped().toString())
}
return props
}
@Suppress("UNCHECKED_CAST")
inline fun <reified T : Any> Config.getListOrElse(path: String, default: Config.() -> List<T>): List<T> {
return if (hasPath(path)) {
(if (T::class == String::class) getStringList(path) else getConfigList(path)) as List<T>
} else {
this.default()
}
}

View File

@ -0,0 +1,12 @@
package net.corda.config
import net.corda.core.div
import java.nio.file.Path
interface SSLConfiguration {
val keyStorePassword: String
val trustStorePassword: String
val certificatesDirectory: Path
val keyStoreFile: Path get() = certificatesDirectory / "sslkeystore.jks"
val trustStoreFile: Path get() = certificatesDirectory / "truststore.jks"
}

View File

@ -0,0 +1,85 @@
package net.corda.node
import com.google.common.net.HostAndPort
import net.corda.config.SSLConfiguration
import org.apache.activemq.artemis.api.core.TransportConfiguration
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants
import java.nio.file.FileSystems
import java.nio.file.Path
sealed class ConnectionDirection {
object Inbound : ConnectionDirection()
class Outbound(val expectedCommonName: String? = null) : ConnectionDirection()
}
class ArtemisTcpTransport {
companion object {
const val VERIFY_PEER_COMMON_NAME = "corda.verifyPeerCommonName"
// Restrict enabled Cipher Suites to AES and GCM as minimum for the bulk cipher.
// Our self-generated certificates all use ECDSA for handshakes, but we allow classical RSA certificates to work
// in case we need to use keytool certificates in some demos
private val CIPHER_SUITES = listOf(
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"
)
fun tcpTransport(
direction: ConnectionDirection,
hostAndPort: HostAndPort,
config: SSLConfiguration?,
acceptorFactoryClassName: String = NettyAcceptorFactory::class.java.name,
connectorFactoryClassName: String = NettyConnectorFactory::class.java.name,
enableSSL: Boolean = true
): TransportConfiguration {
val options = mutableMapOf<String, Any?>(
// Basic TCP target details
TransportConstants.HOST_PROP_NAME to hostAndPort.hostText,
TransportConstants.PORT_PROP_NAME to hostAndPort.port,
// Turn on AMQP support, which needs the protocol jar on the classpath.
// Unfortunately we cannot disable core protocol as artemis only uses AMQP for interop
// It does not use AMQP messages for its own messages e.g. topology and heartbeats
// TODO further investigate how to ensure we use a well defined wire level protocol for Node to Node communications
TransportConstants.PROTOCOLS_PROP_NAME to "CORE,AMQP"
)
if (config != null && enableSSL) {
config.keyStoreFile.expectedOnDefaultFileSystem()
config.trustStoreFile.expectedOnDefaultFileSystem()
val tlsOptions = mapOf<String, Any?>(
// Enable TLS transport layer with client certs and restrict to at least SHA256 in handshake
// and AES encryption
TransportConstants.SSL_ENABLED_PROP_NAME to true,
TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS",
TransportConstants.KEYSTORE_PATH_PROP_NAME to config.keyStoreFile,
TransportConstants.KEYSTORE_PASSWORD_PROP_NAME to config.keyStorePassword, // TODO proper management of keystores and password
TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME to "JKS",
TransportConstants.TRUSTSTORE_PATH_PROP_NAME to config.trustStoreFile,
TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to config.trustStorePassword,
TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME to CIPHER_SUITES.joinToString(","),
TransportConstants.ENABLED_PROTOCOLS_PROP_NAME to "TLSv1.2",
TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to true,
VERIFY_PEER_COMMON_NAME to (direction as? ConnectionDirection.Outbound)?.expectedCommonName
)
options.putAll(tlsOptions)
}
val factoryName = when (direction) {
is ConnectionDirection.Inbound -> acceptorFactoryClassName
is ConnectionDirection.Outbound -> connectorFactoryClassName
}
return TransportConfiguration(factoryName, options)
}
}
}
fun Path.expectedOnDefaultFileSystem() {
require(fileSystem == FileSystems.getDefault()) { "Artemis only uses the default file system" }
}

View File

@ -68,6 +68,7 @@ dependencies {
compile (project(':node-schemas')) { compile (project(':node-schemas')) {
exclude group: 'javassist', module: 'javassist' exclude group: 'javassist', module: 'javassist'
} }
compile project(':node-api')
compile "com.google.code.findbugs:jsr305:3.0.1" compile "com.google.code.findbugs:jsr305:3.0.1"

View File

@ -2,6 +2,7 @@ package net.corda.services.messaging
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import net.corda.config.SSLConfiguration
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.composite import net.corda.core.crypto.composite
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
@ -13,7 +14,6 @@ import net.corda.core.seconds
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.node.internal.Node import net.corda.node.internal.Node
import net.corda.node.services.User import net.corda.node.services.User
import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.CLIENTS_PREFIX import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.CLIENTS_PREFIX
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.NETWORK_MAP_QUEUE import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.NETWORK_MAP_QUEUE

View File

@ -8,6 +8,7 @@ import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.SettableFuture
import com.typesafe.config.Config import com.typesafe.config.Config
import com.typesafe.config.ConfigRenderOptions import com.typesafe.config.ConfigRenderOptions
import net.corda.config.SSLConfiguration
import net.corda.core.* import net.corda.core.*
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
@ -18,7 +19,6 @@ import net.corda.core.utilities.loggerFor
import net.corda.node.services.User import net.corda.node.services.User
import net.corda.node.services.config.ConfigHelper import net.corda.node.services.config.ConfigHelper
import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.config.FullNodeConfiguration
import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.messaging.ArtemisMessagingComponent import net.corda.node.services.messaging.ArtemisMessagingComponent
import net.corda.node.services.messaging.CordaRPCClient import net.corda.node.services.messaging.CordaRPCClient
import net.corda.node.services.messaging.NodeMessagingClient import net.corda.node.services.messaging.NodeMessagingClient

View File

@ -3,11 +3,11 @@
package net.corda.node.services.config package net.corda.node.services.config
import com.google.common.net.HostAndPort
import com.typesafe.config.Config import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigParseOptions
import com.typesafe.config.ConfigRenderOptions import com.typesafe.config.ConfigRenderOptions
import net.corda.config.SSLConfiguration
import net.corda.core.copyTo import net.corda.core.copyTo
import net.corda.core.createDirectories import net.corda.core.createDirectories
import net.corda.core.crypto.X509Utilities import net.corda.core.crypto.X509Utilities
@ -15,13 +15,8 @@ import net.corda.core.div
import net.corda.core.exists import net.corda.core.exists
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import java.net.URL import java.net.URL
import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths
import java.time.Instant
import java.time.LocalDate
import java.util.*
import kotlin.reflect.KProperty
import kotlin.reflect.jvm.javaType
object ConfigHelper { object ConfigHelper {
private val log = loggerFor<ConfigHelper>() private val log = loggerFor<ConfigHelper>()
@ -46,57 +41,6 @@ object ConfigHelper {
} }
} }
@Suppress("UNCHECKED_CAST", "PLATFORM_CLASS_MAPPED_TO_KOTLIN")
operator fun <T> Config.getValue(receiver: Any, metadata: KProperty<*>): T {
if (metadata.returnType.isMarkedNullable && !hasPath(metadata.name)) {
return null as T
}
return when (metadata.returnType.javaType) {
String::class.java -> getString(metadata.name) as T
Int::class.java -> getInt(metadata.name) as T
Integer::class.java -> getInt(metadata.name) as T
Long::class.java -> getLong(metadata.name) as T
Double::class.java -> getDouble(metadata.name) as T
Boolean::class.java -> getBoolean(metadata.name) as T
LocalDate::class.java -> LocalDate.parse(getString(metadata.name)) as T
Instant::class.java -> Instant.parse(getString(metadata.name)) as T
HostAndPort::class.java -> HostAndPort.fromString(getString(metadata.name)) as T
Path::class.java -> Paths.get(getString(metadata.name)) as T
URL::class.java -> URL(getString(metadata.name)) as T
Properties::class.java -> getProperties(metadata.name) as T
else -> throw IllegalArgumentException("Unsupported type ${metadata.returnType}")
}
}
/**
* Helper class for optional configurations
*/
class OptionalConfig<out T>(val conf: Config, val lambda: () -> T) {
operator fun getValue(receiver: Any, metadata: KProperty<*>): T {
return if (conf.hasPath(metadata.name)) conf.getValue(receiver, metadata) else lambda()
}
}
fun <T> Config.getOrElse(lambda: () -> T): OptionalConfig<T> = OptionalConfig(this, lambda)
fun Config.getProperties(path: String): Properties {
val obj = this.getObject(path)
val props = Properties()
for ((property, objectValue) in obj.entries) {
props.setProperty(property, objectValue.unwrapped().toString())
}
return props
}
@Suppress("UNCHECKED_CAST")
inline fun <reified T : Any> Config.getListOrElse(path: String, default: Config.() -> List<T>): List<T> {
return if (hasPath(path)) {
(if (T::class == String::class) getStringList(path) else getConfigList(path)) as List<T>
} else {
this.default()
}
}
/** /**
* Strictly for dev only automatically construct a server certificate/private key signed from * Strictly for dev only automatically construct a server certificate/private key signed from
* the CA certs in Node resources. Then provision KeyStores into certificates folder under node path. * the CA certs in Node resources. Then provision KeyStores into certificates folder under node path.

View File

@ -2,6 +2,10 @@ package net.corda.node.services.config
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.typesafe.config.Config import com.typesafe.config.Config
import net.corda.config.SSLConfiguration
import net.corda.config.getListOrElse
import net.corda.config.getOrElse
import net.corda.config.getValue
import net.corda.core.div import net.corda.core.div
import net.corda.core.node.NodeVersionInfo import net.corda.core.node.NodeVersionInfo
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
@ -15,13 +19,6 @@ import java.net.URL
import java.nio.file.Path import java.nio.file.Path
import java.util.* import java.util.*
interface SSLConfiguration {
val keyStorePassword: String
val trustStorePassword: String
val certificatesDirectory: Path
val keyStoreFile: Path get() = certificatesDirectory / "sslkeystore.jks"
val trustStoreFile: Path get() = certificatesDirectory / "truststore.jks"
}
interface NodeConfiguration : SSLConfiguration { interface NodeConfiguration : SSLConfiguration {
val baseDirectory: Path val baseDirectory: Path

View File

@ -2,6 +2,7 @@ package net.corda.node.services.messaging
import com.google.common.annotations.VisibleForTesting import com.google.common.annotations.VisibleForTesting
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import net.corda.config.SSLConfiguration
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.messaging.MessageRecipientGroup import net.corda.core.messaging.MessageRecipientGroup
import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.MessageRecipients
@ -9,14 +10,6 @@ import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.read import net.corda.core.read
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.messaging.ArtemisMessagingComponent.ConnectionDirection.Inbound
import net.corda.node.services.messaging.ArtemisMessagingComponent.ConnectionDirection.Outbound
import org.apache.activemq.artemis.api.core.TransportConfiguration
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants
import java.nio.file.FileSystems
import java.nio.file.Path
import java.security.KeyStore import java.security.KeyStore
/** /**
@ -43,8 +36,6 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() {
const val NOTIFICATIONS_ADDRESS = "${INTERNAL_PREFIX}activemq.notifications" const val NOTIFICATIONS_ADDRESS = "${INTERNAL_PREFIX}activemq.notifications"
const val NETWORK_MAP_QUEUE = "${INTERNAL_PREFIX}networkmap" const val NETWORK_MAP_QUEUE = "${INTERNAL_PREFIX}networkmap"
const val VERIFY_PEER_COMMON_NAME = "corda.verifyPeerCommonName"
/** /**
* Assuming the passed in target address is actually an ArtemisAddress will extract the host and port of the node. This should * Assuming the passed in target address is actually an ArtemisAddress will extract the host and port of the node. This should
* only be used in unit tests and the internals of the messaging services to keep addressing opaque for the future. * only be used in unit tests and the internals of the messaging services to keep addressing opaque for the future.
@ -111,19 +102,6 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() {
/** The config object is used to pass in the passwords for the certificate KeyStore and TrustStore */ /** The config object is used to pass in the passwords for the certificate KeyStore and TrustStore */
abstract val config: SSLConfiguration? abstract val config: SSLConfiguration?
// Restrict enabled Cipher Suites to AES and GCM as minimum for the bulk cipher.
// Our self-generated certificates all use ECDSA for handshakes, but we allow classical RSA certificates to work
// in case we need to use keytool certificates in some demos
private val CIPHER_SUITES = listOf(
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"
)
/** /**
* Returns nothing if the keystore was opened OK or throws if not. Useful to check the password, as * Returns nothing if the keystore was opened OK or throws if not. Useful to check the password, as
* unfortunately Artemis tends to bury the exception when the password is wrong. * unfortunately Artemis tends to bury the exception when the password is wrong.
@ -137,57 +115,4 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() {
KeyStore.getInstance("JKS").load(it, config.trustStorePassword.toCharArray()) KeyStore.getInstance("JKS").load(it, config.trustStorePassword.toCharArray())
} }
} }
protected fun tcpTransport(direction: ConnectionDirection, host: String, port: Int, enableSSL: Boolean = true): TransportConfiguration {
// Will throw exception if enableSSL = true but config is missing
require(config != null || !enableSSL) { "SSL configuration cannot be null when SSL is enabled." }
val config = config
val options = mutableMapOf<String, Any?>(
// Basic TCP target details
TransportConstants.HOST_PROP_NAME to host,
TransportConstants.PORT_PROP_NAME to port,
// Turn on AMQP support, which needs the protocol jar on the classpath.
// Unfortunately we cannot disable core protocol as artemis only uses AMQP for interop
// It does not use AMQP messages for its own messages e.g. topology and heartbeats
// TODO further investigate how to ensure we use a well defined wire level protocol for Node to Node communications
TransportConstants.PROTOCOLS_PROP_NAME to "CORE,AMQP"
)
if (config != null && enableSSL) {
config.keyStoreFile.expectedOnDefaultFileSystem()
config.trustStoreFile.expectedOnDefaultFileSystem()
val tlsOptions = mapOf<String, Any?>(
// Enable TLS transport layer with client certs and restrict to at least SHA256 in handshake
// and AES encryption
TransportConstants.SSL_ENABLED_PROP_NAME to true,
TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS",
TransportConstants.KEYSTORE_PATH_PROP_NAME to config.keyStoreFile,
TransportConstants.KEYSTORE_PASSWORD_PROP_NAME to config.keyStorePassword, // TODO proper management of keystores and password
TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME to "JKS",
TransportConstants.TRUSTSTORE_PATH_PROP_NAME to config.trustStoreFile,
TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to config.trustStorePassword,
TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME to CIPHER_SUITES.joinToString(","),
TransportConstants.ENABLED_PROTOCOLS_PROP_NAME to "TLSv1.2",
TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to true,
VERIFY_PEER_COMMON_NAME to (direction as? Outbound)?.expectedCommonName
)
options.putAll(tlsOptions)
}
val factoryName = when (direction) {
is Inbound -> NettyAcceptorFactory::class.java.name
is Outbound -> VerifyingNettyConnectorFactory::class.java.name
}
return TransportConfiguration(factoryName, options)
}
protected fun Path.expectedOnDefaultFileSystem() {
require(fileSystem == FileSystems.getDefault()) { "Artemis only uses the default file system" }
}
protected sealed class ConnectionDirection {
object Inbound : ConnectionDirection()
class Outbound(val expectedCommonName: String? = null) : ConnectionDirection()
}
} }

View File

@ -16,14 +16,15 @@ import net.corda.core.node.services.NetworkMapCache.MapChange
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.utilities.debug import net.corda.core.utilities.debug
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.ArtemisTcpTransport
import net.corda.node.ConnectionDirection
import net.corda.node.expectedOnDefaultFileSystem
import net.corda.node.printBasicNodeInfo import net.corda.node.printBasicNodeInfo
import net.corda.node.services.RPCUserService import net.corda.node.services.RPCUserService
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.CLIENTS_PREFIX import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.CLIENTS_PREFIX
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.NODE_USER import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.NODE_USER
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.PEER_USER import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.PEER_USER
import net.corda.node.services.messaging.ArtemisMessagingComponent.ConnectionDirection.Inbound
import net.corda.node.services.messaging.ArtemisMessagingComponent.ConnectionDirection.Outbound
import net.corda.node.services.messaging.NodeLoginModule.Companion.NODE_ROLE import net.corda.node.services.messaging.NodeLoginModule.Companion.NODE_ROLE
import net.corda.node.services.messaging.NodeLoginModule.Companion.PEER_ROLE import net.corda.node.services.messaging.NodeLoginModule.Companion.PEER_ROLE
import net.corda.node.services.messaging.NodeLoginModule.Companion.RPC_ROLE import net.corda.node.services.messaging.NodeLoginModule.Companion.RPC_ROLE
@ -151,9 +152,9 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
bindingsDirectory = (artemisDir / "bindings").toString() bindingsDirectory = (artemisDir / "bindings").toString()
journalDirectory = (artemisDir / "journal").toString() journalDirectory = (artemisDir / "journal").toString()
largeMessagesDirectory = (artemisDir / "large-messages").toString() largeMessagesDirectory = (artemisDir / "large-messages").toString()
val acceptors = mutableSetOf(tcpTransport(Inbound, "0.0.0.0", p2pHostPort.port)) val acceptors = mutableSetOf(verifyingTcpTransport(ConnectionDirection.Inbound, "0.0.0.0", p2pHostPort.port))
if (rpcHostPort != null) { if (rpcHostPort != null) {
acceptors.add(tcpTransport(Inbound, "0.0.0.0", rpcHostPort.port, enableSSL = false)) acceptors.add(verifyingTcpTransport(ConnectionDirection.Inbound, "0.0.0.0", rpcHostPort.port, enableSSL = false))
} }
acceptorConfigurations = acceptors acceptorConfigurations = acceptors
// Enable built in message deduplication. Note we still have to do our own as the delayed commits // Enable built in message deduplication. Note we still have to do our own as the delayed commits
@ -191,8 +192,8 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
} }
/** /**
* Authenticated clients connecting to us fall in one of three groups: * Authenticated clients connecting to us fall in one of the following groups:
* 1. The node hosting us and any of its logically connected components. These are given full access to all valid queues. * 1. The node itself. It is given full access to all valid queues.
* 2. Peers on the same network as us. These are only given permission to send to our P2P inbound queue. * 2. Peers on the same network as us. These are only given permission to send to our P2P inbound queue.
* 3. RPC users. These are only given sufficient access to perform RPC with us. * 3. RPC users. These are only given sufficient access to perform RPC with us.
*/ */
@ -327,6 +328,12 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
deployBridge(address.queueName, address.hostAndPort, legalName) deployBridge(address.queueName, address.hostAndPort, legalName)
} }
private fun verifyingTcpTransport(direction: ConnectionDirection, host: String, port: Int, enableSSL: Boolean = true) =
ArtemisTcpTransport.tcpTransport(direction, HostAndPort.fromParts(host, port), config,
enableSSL = enableSSL,
connectorFactoryClassName = VerifyingNettyConnectorFactory::class.java.name
)
/** /**
* All nodes are expected to have a public facing address called [ArtemisMessagingComponent.P2P_QUEUE] for receiving * All nodes are expected to have a public facing address called [ArtemisMessagingComponent.P2P_QUEUE] for receiving
* messages from other nodes. When we want to send a message to a node we send it to our internal address/queue for it, * messages from other nodes. When we want to send a message to a node we send it to our internal address/queue for it,
@ -334,7 +341,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
* P2P address. * P2P address.
*/ */
private fun deployBridge(queueName: String, target: HostAndPort, legalName: String) { private fun deployBridge(queueName: String, target: HostAndPort, legalName: String) {
val tcpTransport = tcpTransport(Outbound(expectedCommonName = legalName), target.hostText, target.port) val tcpTransport = verifyingTcpTransport(ConnectionDirection.Outbound(expectedCommonName = legalName), target.hostText, target.port)
tcpTransport.params[ArtemisMessagingServer::class.java.name] = this tcpTransport.params[ArtemisMessagingServer::class.java.name] = this
// We intentionally overwrite any previous connector config in case the peer legal name changed // We intentionally overwrite any previous connector config in case the peer legal name changed
activeMQServer.configuration.addConnectorConfiguration(target.toString(), tcpTransport) activeMQServer.configuration.addConnectorConfiguration(target.toString(), tcpTransport)
@ -407,7 +414,7 @@ private class VerifyingNettyConnector(configuration: MutableMap<String, Any>?,
protocolManager: ClientProtocolManager?) : protocolManager: ClientProtocolManager?) :
NettyConnector(configuration, handler, listener, closeExecutor, threadPool, scheduledThreadPool, protocolManager) { NettyConnector(configuration, handler, listener, closeExecutor, threadPool, scheduledThreadPool, protocolManager) {
private val server = configuration?.get(ArtemisMessagingServer::class.java.name) as? ArtemisMessagingServer private val server = configuration?.get(ArtemisMessagingServer::class.java.name) as? ArtemisMessagingServer
private val expectedCommonName = configuration?.get(ArtemisMessagingComponent.VERIFY_PEER_COMMON_NAME) as? String private val expectedCommonName = configuration?.get(ArtemisTcpTransport.VERIFY_PEER_COMMON_NAME) as? String
override fun createConnection(): Connection? { override fun createConnection(): Connection? {
val connection = super.createConnection() as NettyConnection? val connection = super.createConnection() as NettyConnection?

View File

@ -1,14 +1,15 @@
package net.corda.node.services.messaging package net.corda.node.services.messaging
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import net.corda.config.SSLConfiguration
import net.corda.core.ThreadBox import net.corda.core.ThreadBox
import net.corda.core.logElapsedTime import net.corda.core.logElapsedTime
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.minutes import net.corda.core.minutes
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.services.config.SSLConfiguration import net.corda.node.ArtemisTcpTransport.Companion.tcpTransport
import net.corda.node.services.messaging.ArtemisMessagingComponent.ConnectionDirection.Outbound import net.corda.node.ConnectionDirection
import org.apache.activemq.artemis.api.core.ActiveMQException import org.apache.activemq.artemis.api.core.ActiveMQException
import org.apache.activemq.artemis.api.core.client.ActiveMQClient import org.apache.activemq.artemis.api.core.client.ActiveMQClient
import org.apache.activemq.artemis.api.core.client.ClientSession import org.apache.activemq.artemis.api.core.client.ClientSession
@ -52,7 +53,7 @@ class CordaRPCClient(val host: HostAndPort, override val config: SSLConfiguratio
check(!running) check(!running)
log.logElapsedTime("Startup") { log.logElapsedTime("Startup") {
checkStorePasswords() checkStorePasswords()
val serverLocator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport(Outbound(), host.hostText, host.port, enableSSL = config != null)).apply { val serverLocator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport(ConnectionDirection.Outbound(), host, config, enableSSL = config != null)).apply {
// TODO: Put these in config file or make it user configurable? // TODO: Put these in config file or make it user configurable?
threadPoolMaxSize = 1 threadPoolMaxSize = 1
confirmationWindowSize = 100000 // a guess confirmationWindowSize = 100000 // a guess

View File

@ -12,10 +12,11 @@ import net.corda.core.serialization.opaque
import net.corda.core.success import net.corda.core.success
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
import net.corda.node.ArtemisTcpTransport
import net.corda.node.ConnectionDirection
import net.corda.node.services.RPCUserService import net.corda.node.services.RPCUserService
import net.corda.node.services.api.MessagingServiceInternal import net.corda.node.services.api.MessagingServiceInternal
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.messaging.ArtemisMessagingComponent.ConnectionDirection.Outbound
import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.statemachine.StateMachineManager
import net.corda.node.utilities.* import net.corda.node.utilities.*
import org.apache.activemq.artemis.api.core.ActiveMQObjectClosedException import org.apache.activemq.artemis.api.core.ActiveMQObjectClosedException
@ -129,7 +130,7 @@ class NodeMessagingClient(override val config: NodeConfiguration,
log.info("Connecting to server: $serverHostPort") log.info("Connecting to server: $serverHostPort")
// TODO Add broker CN to config for host verification in case the embedded broker isn't used // TODO Add broker CN to config for host verification in case the embedded broker isn't used
val tcpTransport = tcpTransport(Outbound(), serverHostPort.hostText, serverHostPort.port) val tcpTransport = ArtemisTcpTransport.tcpTransport(ConnectionDirection.Outbound(), serverHostPort, config)
val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport) val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport)
clientFactory = locator.createSessionFactory() clientFactory = locator.createSessionFactory()

View File

@ -20,7 +20,7 @@ import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.services.config.SSLConfiguration import net.corda.config.SSLConfiguration
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import java.nio.file.Path import java.nio.file.Path
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture

View File

@ -2,6 +2,7 @@ package net.corda.attachmentdemo
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import joptsimple.OptionParser import joptsimple.OptionParser
import net.corda.config.SSLConfiguration
import net.corda.core.contracts.TransactionType import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
@ -11,7 +12,6 @@ import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.Emoji import net.corda.core.utilities.Emoji
import net.corda.flows.FinalityFlow import net.corda.flows.FinalityFlow
import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.messaging.CordaRPCClient import net.corda.node.services.messaging.CordaRPCClient
import net.corda.testing.ALICE_KEY import net.corda.testing.ALICE_KEY
import java.nio.file.Path import java.nio.file.Path

View File

@ -3,6 +3,7 @@ package net.corda.notarydemo
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.Futures
import joptsimple.OptionParser import joptsimple.OptionParser
import net.corda.config.SSLConfiguration
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.toStringShort
import net.corda.core.div import net.corda.core.div
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
@ -10,7 +11,6 @@ import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.flows.NotaryFlow import net.corda.flows.NotaryFlow
import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.messaging.CordaRPCClient import net.corda.node.services.messaging.CordaRPCClient
import net.corda.notarydemo.flows.DummyIssueAndMove import net.corda.notarydemo.flows.DummyIssueAndMove
import java.nio.file.Path import java.nio.file.Path

View File

@ -2,10 +2,10 @@ package net.corda.traderdemo
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import joptsimple.OptionParser import joptsimple.OptionParser
import net.corda.config.SSLConfiguration
import net.corda.core.contracts.DOLLARS import net.corda.core.contracts.DOLLARS
import net.corda.core.div import net.corda.core.div
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.messaging.CordaRPCClient import net.corda.node.services.messaging.CordaRPCClient
import org.slf4j.Logger import org.slf4j.Logger
import java.nio.file.Path import java.nio.file.Path

View File

@ -4,6 +4,7 @@ rootProject.name = 'corda-project'
include 'finance' include 'finance'
include 'finance:isolated' include 'finance:isolated'
include 'core' include 'core'
include 'node-api'
include 'node-schemas' include 'node-schemas'
include 'node' include 'node'
include 'node:capsule' include 'node:capsule'

View File

@ -6,6 +6,7 @@ package net.corda.testing
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import com.typesafe.config.Config import com.typesafe.config.Config
import net.corda.config.SSLConfiguration
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
@ -20,7 +21,6 @@ import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.node.internal.AbstractNode import net.corda.node.internal.AbstractNode
import net.corda.node.internal.NetworkMapInfo import net.corda.node.internal.NetworkMapInfo
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.node.services.config.configureDevKeyAndTrustStores
import net.corda.node.services.statemachine.FlowStateMachineImpl import net.corda.node.services.statemachine.FlowStateMachineImpl
import net.corda.node.utilities.AddOrRemove.ADD import net.corda.node.utilities.AddOrRemove.ADD

View File

@ -1,9 +1,10 @@
package net.corda.testing.messaging package net.corda.testing.messaging
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import net.corda.node.services.config.SSLConfiguration import net.corda.config.SSLConfiguration
import net.corda.node.ArtemisTcpTransport
import net.corda.node.ConnectionDirection
import net.corda.node.services.messaging.ArtemisMessagingComponent import net.corda.node.services.messaging.ArtemisMessagingComponent
import net.corda.node.services.messaging.ArtemisMessagingComponent.ConnectionDirection.Outbound
import net.corda.testing.configureTestSSL import net.corda.testing.configureTestSSL
import org.apache.activemq.artemis.api.core.client.* import org.apache.activemq.artemis.api.core.client.*
@ -17,7 +18,7 @@ class SimpleMQClient(val target: HostAndPort,
lateinit var producer: ClientProducer lateinit var producer: ClientProducer
fun start(username: String? = null, password: String? = null, enableSSL: Boolean = true) { fun start(username: String? = null, password: String? = null, enableSSL: Boolean = true) {
val tcpTransport = tcpTransport(Outbound(), target.hostText, target.port, enableSSL) val tcpTransport = ArtemisTcpTransport.tcpTransport(ConnectionDirection.Outbound(), target, config, enableSSL = enableSSL)
val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply { val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {
isBlockOnNonDurableSend = true isBlockOnNonDurableSend = true
threadPoolMaxSize = 1 threadPoolMaxSize = 1

View File

@ -1,20 +1,13 @@
package net.corda.explorer.views package net.corda.explorer.views
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView
import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleIntegerProperty
import javafx.scene.control.* import javafx.scene.control.*
import javafx.stage.FileChooser
import net.corda.client.fxutils.map
import net.corda.client.model.NodeMonitorModel import net.corda.client.model.NodeMonitorModel
import net.corda.client.model.objectProperty import net.corda.client.model.objectProperty
import net.corda.explorer.model.SettingsModel import net.corda.explorer.model.SettingsModel
import net.corda.node.services.config.SSLConfiguration
import org.controlsfx.dialog.ExceptionDialog import org.controlsfx.dialog.ExceptionDialog
import tornadofx.* import tornadofx.View
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.system.exitProcess import kotlin.system.exitProcess
class LoginView : View() { class LoginView : View() {

View File

@ -11,7 +11,7 @@ import net.corda.core.createDirectories
import net.corda.core.div import net.corda.core.div
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.node.driver.PortAllocation import net.corda.node.driver.PortAllocation
import net.corda.node.services.config.SSLConfiguration import net.corda.config.SSLConfiguration
import net.corda.node.services.messaging.CordaRPCClient import net.corda.node.services.messaging.CordaRPCClient
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream

View File

@ -1,8 +1,8 @@
package net.corda.loadtest package net.corda.loadtest
import com.typesafe.config.Config import com.typesafe.config.Config
import net.corda.config.getValue
import java.nio.file.Path import java.nio.file.Path
import net.corda.node.services.config.*
/** /**
* @param sshUser The UNIX username to use for SSH auth. * @param sshUser The UNIX username to use for SSH auth.