Clean up messaging/RPC port configuration and docs (#296)

* Non-ssl artemis acceptor for RPC connection. (#271)

* New non-ssl acceptor in artemis server for RPC connection.

* Rename artemisAddress with messagingAddress

Rename artemisAddress with messagingAddress so that the node configuration file properties match
the code variable names.
Rename artemisPort to messagingPort in Gradle configuration to match node configuration naming.

* Add rpcPort configuration option for Gradle

* Update docs to reflect changes to RPC port configuration

* Renumber ports in example CorDapp to match numbering used elsewhere

* Restructure upgrade guide

* added config file checks on corda startup to make the upgrade path a bit smoother.
This commit is contained in:
Ross Nicoll 2017-03-17 10:32:14 +00:00 committed by Patrick Kuo
parent eb21458885
commit 486368d926
63 changed files with 402 additions and 366 deletions

View File

@ -3,7 +3,7 @@
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="net.corda.attachmentdemo.AttachmentDemoKt" />
<option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar " />
<option name="PROGRAM_PARAMETERS" value="--role=RECIPIENT --certificates=&quot;build/attachment-demo-nodes/Bank B/certificates&quot;" />
<option name="PROGRAM_PARAMETERS" value="--role=RECIPIENT" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />

View File

@ -3,7 +3,7 @@
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="net.corda.attachmentdemo.AttachmentDemoKt" />
<option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar " />
<option name="PROGRAM_PARAMETERS" value="--role SENDER --certificates=&quot;build/attachment-demo-nodes/Bank A/certificates&quot;" />
<option name="PROGRAM_PARAMETERS" value="--role SENDER" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />

View File

@ -3,7 +3,7 @@
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="net.corda.traderdemo.TraderDemoKt" />
<option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar " />
<option name="PROGRAM_PARAMETERS" value="--role BUYER --certificates=&quot;build/trader-demo-nodes/Bank A/certificates&quot;" />
<option name="PROGRAM_PARAMETERS" value="--role BUYER" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />

View File

@ -3,7 +3,7 @@
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="net.corda.traderdemo.TraderDemoKt" />
<option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar " />
<option name="PROGRAM_PARAMETERS" value="--role SELLER --certificates=&quot;build/trader-demo-nodes/Bank B/certificates&quot;" />
<option name="PROGRAM_PARAMETERS" value="--role SELLER" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />

View File

@ -163,23 +163,25 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Controller"
nearestCity "London"
advertisedServices = ["corda.notary.validating"]
artemisPort 10002
p2pPort 10002
cordapps = []
}
node {
name "Bank A"
nearestCity "London"
advertisedServices = []
artemisPort 10004
webPort 10005
p2pPort 10012
rpcPort 10013
webPort 10014
cordapps = []
}
node {
name "Bank B"
nearestCity "New York"
advertisedServices = []
artemisPort 10006
webPort 10007
p2pPort 10007
rpcPort 10008
webPort 10009
cordapps = []
}
}

View File

@ -12,10 +12,10 @@ import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.node.internal.Node
import net.corda.node.services.User
import net.corda.node.services.config.configureTestSSL
import net.corda.node.services.messaging.CordaRPCClient
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.testing.configureTestSSL
import net.corda.testing.node.NodeBasedTest
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.assertj.core.api.Assertions.assertThatExceptionOfType
@ -37,7 +37,7 @@ class CordaRPCClientTest : NodeBasedTest() {
@Before
fun setUp() {
node = startNode("Alice", rpcUsers = listOf(rpcUser), advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))).getOrThrow()
client = CordaRPCClient(node.configuration.artemisAddress, configureTestSSL())
client = CordaRPCClient(node.configuration.rpcAddress!!)
}
@After

View File

@ -25,8 +25,6 @@ import net.corda.flows.CashPaymentFlow
import net.corda.node.driver.DriverBasedTest
import net.corda.node.driver.driver
import net.corda.node.services.User
import net.corda.node.services.config.configureTestSSL
import net.corda.node.services.messaging.ArtemisMessagingComponent
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.SimpleNotaryService
@ -57,9 +55,10 @@ class NodeMonitorModelTest : DriverBasedTest() {
)
val aliceNodeFuture = startNode("Alice", rpcUsers = listOf(cashUser))
val notaryNodeFuture = startNode("Notary", advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type)))
aliceNode = aliceNodeFuture.getOrThrow().nodeInfo
notaryNode = notaryNodeFuture.getOrThrow().nodeInfo
val aliceNodeHandle = aliceNodeFuture.getOrThrow()
val notaryNodeHandle = notaryNodeFuture.getOrThrow()
aliceNode = aliceNodeHandle.nodeInfo
notaryNode = notaryNodeHandle.nodeInfo
newNode = { nodeName -> startNode(nodeName).getOrThrow().nodeInfo }
val monitor = NodeMonitorModel()
@ -70,7 +69,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
vaultUpdates = monitor.vaultUpdates.bufferUntilSubscribed()
networkMapUpdates = monitor.networkMap.bufferUntilSubscribed()
monitor.register(ArtemisMessagingComponent.toHostAndPort(aliceNode.address), configureTestSSL(), cashUser.username, cashUser.password)
monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
rpc = monitor.proxyObservable.value!!
runTest()
}

View File

@ -52,8 +52,8 @@ class NodeMonitorModel {
* Register for updates to/from a given vault.
* TODO provide an unsubscribe mechanism
*/
fun register(nodeHostAndPort: HostAndPort, sslConfig: SSLConfiguration, username: String, password: String) {
val client = CordaRPCClient(nodeHostAndPort, sslConfig){
fun register(nodeHostAndPort: HostAndPort, username: String, password: String) {
val client = CordaRPCClient(nodeHostAndPort){
maxRetryInterval = 10.seconds.toMillis()
}
client.start(username, password)

View File

@ -2,11 +2,12 @@ myLegalName : "Bank A"
nearestCity : "London"
keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass"
artemisAddress : "localhost:31337"
webAddress : "localhost:31339"
p2pAddress : "localhost:10002"
rpcAddress : "localhost:10003"
webAddress : "localhost:10004"
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : {
address : "localhost:12345"
address : "localhost:10000"
legalName : "Network Map Service"
}
useHTTPS : false

View File

@ -2,11 +2,12 @@ myLegalName : "Bank B"
nearestCity : "London"
keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass"
artemisAddress : "localhost:31338"
webAddress : "localhost:31340"
p2pAddress : "localhost:10005"
rpcAddress : "localhost:10006"
webAddress : "localhost:10007"
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : {
address : "localhost:12345"
address : "localhost:10000"
legalName : "Network Map Service"
}
useHTTPS : false

View File

@ -2,7 +2,7 @@ myLegalName : "Notary Service"
nearestCity : "London"
keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass"
artemisAddress : "localhost:12345"
webAddress : "localhost:12346"
p2pAddress : "localhost:10000"
webAddress : "localhost:10001"
extraAdvertisedServiceIds : [ "corda.notary.validating" ]
useHTTPS : false
useHTTPS : false

View File

@ -14,6 +14,15 @@ API changes:
using strings like "1000.00 USD" when writing. You can use any format supported by ``Amount.parseCurrency``
as input.
Milestone 10
------------
* Configuration:
* Replace ``artemisPort`` with ``p2pPort`` in Gradle configuration
* Replace ``artemisAddress`` with ``p2pAddress`` in node configuration
* Added ``rpcAddress`` in node configuration
Milestone 9.1
-------------

View File

@ -38,8 +38,9 @@ NetworkMapService plus Simple Notary configuration file.
nearestCity : "London"
keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass"
artemisAddress : "localhost:12345"
webAddress : "localhost:12346"
p2pAddress : "localhost:12345"
rpcAddress : "localhost:12346"
webAddress : "localhost:12347"
extraAdvertisedServiceIds : []
useHTTPS : false
devMode : true
@ -74,13 +75,15 @@ path to the node's base directory.
Currently the defaults in ``/node/src/main/resources/reference.conf`` are as shown in the first example. This is currently
the only configuration that has been tested, although in the future full support for other storage layers will be validated.
:artemisAddress: The host and port on which the node is available for protocol operations over ArtemisMQ.
:messagingServerAddress: The address of the ArtemisMQ broker instance. If not provided the node will run one locally.
:p2pAddress: The host and port on which the node is available for protocol operations over ArtemisMQ.
.. note:: In practice the ArtemisMQ messaging services bind to all local addresses on the specified port. However,
note that the host is the included as the advertised entry in the NetworkMapService. As a result the value listed
here must be externally accessible when running nodes across a cluster of machines.
:messagingServerAddress: The address of the ArtemisMQ broker instance. If not provided the node will run one locally.
:rpcAddress: The address of the RPC system on which RPC requests can be made to the node. If not provided then the node will run without RPC.
:webAddress: The host and port on which the bundled webserver will listen if it is started.

View File

@ -214,24 +214,27 @@ is a three node example;
name "Controller"
nearestCity "London"
advertisedServices = [ "corda.notary.validating" ]
artemisPort 12345
webPort 12346
p2pPort 10002
rpcPort 10003
webPort 10004
cordapps []
}
node {
name "NodeA"
nearestCity "London"
advertisedServices = []
artemisPort 31337
webPort 31339
p2pPort 10005
rpcPort 10006
webPort 10007
cordapps []
}
node {
name "NodeB"
nearestCity "New York"
advertisedServices = []
artemisPort 31338
webPort 31340
p2pPort 10008
rpcPort 10009
webPort 10010
cordapps []
}
}

View File

@ -80,16 +80,18 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Notary"
nearestCity "London"
advertisedServices = ["corda.notary.validating"]
artemisPort 10002
webPort 10003
p2pPort 10002
rpcPort 10003
webPort 10004
cordapps = []
}
node {
name "Alice"
nearestCity "London"
advertisedServices = []
artemisPort 10004
webPort 10005
p2pPort 10005
rpcPort 10006
webPort 10007
cordapps = []
rpcUsers = [
['user' : "user",

View File

@ -2,7 +2,7 @@ myLegalName : "Notary Service"
nearestCity : "London"
keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass"
artemisAddress : "my-network-map:10000"
p2pAddress : "my-network-map:10000"
webAddress : "localhost:10001"
extraAdvertisedServiceIds : []
useHTTPS : false

View File

@ -8,8 +8,9 @@ dataSourceProperties : {
"dataSource.user" : sa
"dataSource.password" : ""
}
artemisAddress : "my-corda-node:10002"
webAddress : "localhost:10003"
p2pAddress : "my-corda-node:10002"
rpcAddress : "my-corda-node:10003"
webAddress : "localhost:10004"
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : {
address : "my-network-map:10000"
@ -21,4 +22,4 @@ rpcUsers : [
]
devMode : true
// Certificate signing service will be hosted by R3 in the near future.
//certificateSigningService : "https://testnet.certificate.corda.net"
//certificateSigningService : "https://testnet.certificate.corda.net"

View File

@ -4,6 +4,15 @@ Release notes
Here are release notes for each snapshot release from M9 onwards. This includes guidance on how to upgrade code from
the previous milestone release.
Milestone 10
------------
Important: There are configuration changes in M10 due to the split of the Artemis port into separate P2P and RPC
ports. To upgrade, you *must*:
1. In Gradle build configurations replace any references to ``artemisPort`` with ``p2pPort``.
2. In node configurations replace ``artemisAddress`` with ``p2pAddress``.
Milestone 9
-----------
@ -46,4 +55,4 @@ clients.
There have also been dozens of bug fixes, performance improvements and usability tweaks. Upgrading is definitely
worthwhile and will only take a few minutes for most apps.
For a full list of changes please see :doc:`change-log`.
For a full list of changes please see :doc:`change-log`.

View File

@ -235,7 +235,7 @@ To run from IntelliJ:
4. Run ``Bank Of Corda Demo: Run Web Cash Issue`` to request issuance of some cash on behalf of Big Corporation via HTTP
.. note:: To verify that the Bank of Corda node is alive and running, navigate to the following URL:
http://localhost:10005/api/bank/date
http://localhost:10007/api/bank/date
.. note:: The Bank of Corda node explicitly advertises with a node service type as follows:
``advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer"))))``
@ -259,8 +259,8 @@ Launch the Explorer application to visualize the issuance and transfer of cash f
Using the following login details:
- For the Bank of Corda node: localhost / port 10004 / username bankUser / password test
- For the Big Corporation node: localhost / port 10006 / username bigCorpUser / password test
- For the Bank of Corda node: localhost / port 10006 / username bankUser / password test
- For the Big Corporation node: localhost / port 10009 / username bigCorpUser / password test
See https://docs.corda.net/node-explorer.html for further details on usage.

View File

@ -42,11 +42,13 @@ An example configuration:
The most important fields regarding network configuration are:
* ``artemisAddress``: This specifies a host and port. Note that the address bound will **NOT** be ``my-corda-node``,
but rather ``::`` (all addresses on all interfaces). The hostname specified is the hostname *that must be externally
resolvable by other nodes in the network*. In the above configuration this is the resolvable name of a machine in a vpn.
* ``webAddress``: The address the webserver should bind. Note that the port should be distinct from that of ``artemisAddress``
if they are on the same machine.
* ``p2pAddress``: This specifies a host and port to which Artemis will bind for messaging with other nodes. Note that the
address bound will **NOT** be ``my-corda-node``, but rather ``::`` (all addresses on all network interfaces). The hostname specified
is the hostname *that must be externally resolvable by other nodes in the network*. In the above configuration this is the
resolvable name of a machine in a VPN.
* ``rpcAddress``: The address to which Artemis will bind for RPC calls.
* ``webAddress``: The address the webserver should bind. Note that the port must be distinct from that of ``p2pAddress``
and ``rpcAddress`` if they are on the same machine.
* ``networkMapService``: Details of the node running the network map service. If it's this node that's running the service
then this field must not be specified.

View File

@ -807,16 +807,18 @@ like to deploy for testing. See further details below:
name "Controller" // Artemis name of node to be deployed.
nearestCity "London" // For use with the network visualiser.
advertisedServices = ["corda.notary.validating"] // A list of services you wish the node to offer.
artemisPort 10002
webPort 10003 // Usually 1 higher than the Artemis port.
p2pPort 10002
rpcPort 10003 // Usually 1 higher than the messaging port.
webPort 10004 // Usually 1 higher than the RPC port.
cordapps = [] // Add package names of CordaApps.
}
node { // Create an additional node.
name "NodeA"
nearestCity "London"
advertisedServices = []
artemisPort 10004
webPort 10005
p2pPort 10005
rpcPort 10006
webPort 10007
cordapps = []
}
...
@ -875,9 +877,9 @@ You must now edit the configuration file for each node, including the
controller. Open each node's config file (`[nodeName]/node.conf`), and make
the following changes:
* Change the artemis address to the machine's ip address (e.g.
`artemisAddress="10.18.0.166:10006"`)
* Change the network map service details to the ip address of the machine where the
* Change the Artemis messaging address to the machine's IP address (e.g.
`p2pAddress="10.18.0.166:10006"`)
* Change the network map service details to the IP address of the machine where the
controller node is running and to its legal name (e.g. `networkMapService.address="10.18.0.166:10002"` and
`networkMapService.legalName=controller`) (please note that the controller will not have the `networkMapService` config)

View File

@ -92,7 +92,7 @@ class Cordform extends DefaultTask {
Node networkMapNode = getNodeByName(networkMapNodeName)
nodes.each {
if(it != networkMapNode) {
it.networkMapAddress(networkMapNode.getArtemisAddress(), networkMapNodeName)
it.networkMapAddress(networkMapNode.getP2PAddress(), networkMapNodeName)
}
it.build(directory.toFile())
}

View File

@ -80,13 +80,23 @@ class Node {
}
/**
* Set the artemis port for this node.
* Set the Artemis P2P port for this node.
*
* @param artemisPort The artemis messaging queue port.
* @param p2pPort The Artemis messaging queue port.
*/
void artemisPort(Integer artemisPort) {
config = config.withValue("artemisAddress",
ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$artemisPort".toString()))
void p2pPort(Integer p2pPort) {
config = config.withValue("p2pAddress",
ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$p2pPort".toString()))
}
/**
* Set the Artemis RPC port for this node.
*
* @param rpcPort The Artemis RPC queue port.
*/
void rpcPort(Integer rpcPort) {
config = config.withValue("rpcAddress",
ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$rpcPort".toString()))
}
/**
@ -140,10 +150,10 @@ class Node {
/**
* Get the artemis address for this node.
*
* @return This node's artemis address.
* @return This node's P2P address.
*/
String getArtemisAddress() {
return config.getString("artemisAddress")
String getP2PAddress() {
return config.getString("p2pAddress")
}
/**

View File

@ -14,6 +14,9 @@ import org.junit.Test
* Runs the security tests with the attacker pretending to be a node on the network.
*/
class MQSecurityAsNodeTest : MQSecurityTest() {
override fun createAttacker(): SimpleMQClient {
return clientTo(alice.configuration.p2pAddress)
}
override fun startAttacker(attacker: SimpleMQClient) {
attacker.start(PEER_USER, PEER_USER) // Login as a peer
@ -26,7 +29,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
@Test
fun `only the node running the broker can login using the special node user`() {
val attacker = clientTo(alice.configuration.artemisAddress)
val attacker = clientTo(alice.configuration.p2pAddress)
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
attacker.start(NODE_USER, NODE_USER)
}
@ -34,7 +37,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
@Test
fun `login as the default cluster user`() {
val attacker = clientTo(alice.configuration.artemisAddress)
val attacker = clientTo(alice.configuration.p2pAddress)
assertThatExceptionOfType(ActiveMQClusterSecurityException::class.java).isThrownBy {
attacker.start(ActiveMQDefaultConfiguration.getDefaultClusterUser(), ActiveMQDefaultConfiguration.getDefaultClusterPassword())
}
@ -42,9 +45,25 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
@Test
fun `login without a username and password`() {
val attacker = clientTo(alice.configuration.artemisAddress)
val attacker = clientTo(alice.configuration.p2pAddress)
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
attacker.start()
}
}
}
@Test
fun `login to a non ssl port as a node user`() {
val attacker = clientTo(alice.configuration.rpcAddress!!, sslConfiguration = null)
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
attacker.start(NODE_USER, NODE_USER, enableSSL = false)
}
}
@Test
fun `login to a non ssl port as a peer user`() {
val attacker = clientTo(alice.configuration.rpcAddress!!, sslConfiguration = null)
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
attacker.start(PEER_USER, PEER_USER, enableSSL = false) // Login as a peer
}
}
}

View File

@ -2,14 +2,35 @@ package net.corda.services.messaging
import net.corda.node.services.User
import net.corda.testing.messaging.SimpleMQClient
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.Test
/**
* Runs the security tests with the attacker being a valid RPC user of Alice.
*/
class MQSecurityAsRPCTest : MQSecurityTest() {
override fun createAttacker(): SimpleMQClient {
return clientTo(alice.configuration.rpcAddress!!)
}
@Test
fun `send message on logged in user's RPC address`() {
val user1Queue = loginToRPCAndGetClientQueue()
assertSendAttackFails(user1Queue)
}
override val extraRPCUsers = listOf(User("evil", "pass", permissions = emptySet()))
override fun startAttacker(attacker: SimpleMQClient) {
attacker.loginToRPC(extraRPCUsers[0])
}
}
@Test
fun `login to a ssl port as a RPC user`() {
val attacker = clientTo(alice.configuration.p2pAddress)
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
attacker.loginToRPC(extraRPCUsers[0], enableSSL = true)
}
}
}

View File

@ -14,7 +14,6 @@ import net.corda.core.utilities.unwrap
import net.corda.node.internal.Node
import net.corda.node.services.User
import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.config.configureTestSSL
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.NETWORK_MAP_QUEUE
@ -24,6 +23,7 @@ import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.PEE
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.RPC_QUEUE_REMOVALS_QUEUE
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.RPC_REQUESTS_QUEUE
import net.corda.node.services.messaging.CordaRPCClientImpl
import net.corda.testing.configureTestSSL
import net.corda.testing.messaging.SimpleMQClient
import net.corda.testing.node.NodeBasedTest
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException
@ -49,12 +49,14 @@ abstract class MQSecurityTest : NodeBasedTest() {
@Before
fun start() {
alice = startNode("Alice", rpcUsers = extraRPCUsers + rpcUser).getOrThrow()
attacker = clientTo(alice.configuration.artemisAddress)
attacker = createAttacker()
startAttacker(attacker)
}
open val extraRPCUsers: List<User> get() = emptyList()
abstract fun createAttacker(): SimpleMQClient
abstract fun startAttacker(attacker: SimpleMQClient)
@After
@ -112,12 +114,6 @@ abstract class MQSecurityTest : NodeBasedTest() {
assertConsumeAttackFails(user1Queue)
}
@Test
fun `send message on logged in user's RPC address`() {
val user1Queue = loginToRPCAndGetClientQueue()
assertSendAttackFails(user1Queue)
}
@Test
fun `create queue for valid RPC user`() {
val user1Queue = "$CLIENTS_PREFIX${rpcUser.username}.rpc.${random63BitValue()}"
@ -152,26 +148,26 @@ abstract class MQSecurityTest : NodeBasedTest() {
assertAllQueueCreationAttacksFail(randomQueue)
}
fun clientTo(target: HostAndPort, config: SSLConfiguration = configureTestSSL()): SimpleMQClient {
val client = SimpleMQClient(target, config)
fun clientTo(target: HostAndPort, sslConfiguration: SSLConfiguration? = configureTestSSL()): SimpleMQClient {
val client = SimpleMQClient(target, sslConfiguration)
clients += client
return client
}
fun loginToRPC(target: HostAndPort, rpcUser: User): SimpleMQClient {
val client = clientTo(target)
fun loginToRPC(target: HostAndPort, rpcUser: User, sslConfiguration: SSLConfiguration? = null): SimpleMQClient {
val client = clientTo(target, sslConfiguration)
client.loginToRPC(rpcUser)
return client
}
fun SimpleMQClient.loginToRPC(rpcUser: User): CordaRPCOps {
start(rpcUser.username, rpcUser.password)
fun SimpleMQClient.loginToRPC(rpcUser: User, enableSSL: Boolean = false): CordaRPCOps {
start(rpcUser.username, rpcUser.password, enableSSL)
val clientImpl = CordaRPCClientImpl(session, ReentrantLock(), rpcUser.username)
return clientImpl.proxyFor(CordaRPCOps::class.java, timeout = 1.seconds)
}
fun loginToRPCAndGetClientQueue(): String {
val rpcClient = loginToRPC(alice.configuration.artemisAddress, rpcUser)
val rpcClient = loginToRPC(alice.configuration.rpcAddress!!, rpcUser)
val clientQueueQuery = SimpleString("$CLIENTS_PREFIX${rpcUser.username}.rpc.*")
return rpcClient.session.addressQuery(clientQueueQuery).queueNames.single().toString()
}

View File

@ -32,7 +32,7 @@ class P2PSecurityTest : NodeBasedTest() {
val incorrectNetworkMapName = random63BitValue().toString()
val node = startNode("Bob", configOverrides = mapOf(
"networkMapService" to mapOf(
"address" to networkMapNode.configuration.artemisAddress.toString(),
"address" to networkMapNode.configuration.p2pAddress.toString(),
"legalName" to incorrectNetworkMapName
)
))
@ -57,7 +57,7 @@ class P2PSecurityTest : NodeBasedTest() {
val config = TestNodeConfiguration(
baseDirectory = tempFolder.root.toPath() / legalName,
myLegalName = legalName,
networkMapService = NetworkMapInfo(networkMapNode.configuration.artemisAddress, networkMapNode.info.legalIdentity.name))
networkMapService = NetworkMapInfo(networkMapNode.configuration.p2pAddress, networkMapNode.info.legalIdentity.name))
config.configureWithDevSSLCertificate() // This creates the node's TLS cert with the CN as the legal name
return SimpleNode(config).apply { start() }
}
@ -68,4 +68,4 @@ class P2PSecurityTest : NodeBasedTest() {
val request = RegistrationRequest(registration.toWire(identity.private), net.myAddress)
return net.sendRequest<NetworkMapService.RegistrationResponse>(NetworkMapService.REGISTER_TOPIC, request, networkMapNode.net.myAddress)
}
}
}

View File

@ -1,6 +1,7 @@
@file:JvmName("Corda")
package net.corda.node
import com.typesafe.config.Config
import com.jcabi.manifests.Manifests
import com.typesafe.config.ConfigException
import joptsimple.OptionException
@ -80,7 +81,9 @@ fun main(args: Array<String>) {
printBasicNodeInfo("Logs can be found in", System.getProperty("log-path"))
val conf = try {
FullNodeConfiguration(cmdlineOptions.baseDirectory, cmdlineOptions.loadConfig())
val conf = cmdlineOptions.loadConfig()
checkConfigVersion(conf)
FullNodeConfiguration(cmdlineOptions.baseDirectory, conf)
} catch (e: ConfigException) {
println("Unable to load the configuration file: ${e.rootCause.message}")
exitProcess(2)
@ -110,7 +113,7 @@ fun main(args: Array<String>) {
log.info("VM ${info.vmName} ${info.vmVendor} ${info.vmVersion}")
log.info("Machine: ${InetAddress.getLocalHost().hostName}")
log.info("Working Directory: ${cmdlineOptions.baseDirectory}")
log.info("Starting as node on ${conf.artemisAddress}")
log.info("Starting as node on ${conf.p2pAddress}")
try {
cmdlineOptions.baseDirectory.createDirectories()
@ -138,6 +141,16 @@ fun main(args: Array<String>) {
exitProcess(0)
}
private fun checkConfigVersion(conf: Config) {
// TODO: Remove this check in future milestone.
if (conf.hasPath("artemisAddress")) {
// artemisAddress has been renamed to p2pAddress in M10.
println("artemisAddress has been renamed to p2pAddress in M10, please upgrade your configuration file and start Corda node again.")
println("Corda will now exit...")
exitProcess(1)
}
}
private fun checkJavaVersion() {
// Check we're not running a version of Java with a known bug: https://github.com/corda/corda/issues/83
try {

View File

@ -105,7 +105,7 @@ data class NodeHandle(
val configuration: FullNodeConfiguration,
val process: Process
) {
fun rpcClientToNode(): CordaRPCClient = CordaRPCClient(configuration.artemisAddress, configuration)
fun rpcClientToNode(): CordaRPCClient = CordaRPCClient(configuration.rpcAddress!!)
}
sealed class PortAllocation {
@ -342,16 +342,18 @@ open class DriverDSL(
override fun startNode(providedName: String?, advertisedServices: Set<ServiceInfo>,
rpcUsers: List<User>, customOverrides: Map<String, Any?>): ListenableFuture<NodeHandle> {
val messagingAddress = portAllocation.nextHostAndPort()
val apiAddress = portAllocation.nextHostAndPort()
val p2pAddress = portAllocation.nextHostAndPort()
val rpcAddress = portAllocation.nextHostAndPort()
val webAddress = portAllocation.nextHostAndPort()
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
val name = providedName ?: "${pickA(name)}-${messagingAddress.port}"
val name = providedName ?: "${pickA(name)}-${p2pAddress.port}"
val baseDirectory = driverDirectory / name
val configOverrides = mapOf(
"myLegalName" to name,
"artemisAddress" to messagingAddress.toString(),
"webAddress" to apiAddress.toString(),
"p2pAddress" to p2pAddress.toString(),
"rpcAddress" to rpcAddress.toString(),
"webAddress" to webAddress.toString(),
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
"networkMapService" to mapOf(
"address" to networkMapAddress.toString(),
@ -379,7 +381,8 @@ open class DriverDSL(
val processFuture = startNode(executorService, configuration, quasarJarPath, debugPort)
registerProcess(processFuture)
return processFuture.flatMap { process ->
establishRpc(messagingAddress, configuration).flatMap { rpc ->
// We continue to use SSL enabled port for RPC when its for node user.
establishRpc(p2pAddress, configuration).flatMap { rpc ->
rpc.waitUntilRegisteredWithNetworkMap().map {
NodeHandle(rpc.nodeIdentity(), rpc, configuration, process)
}
@ -465,7 +468,7 @@ open class DriverDSL(
// TODO: remove the webAddress as NMS doesn't need to run a web server. This will cause all
// node port numbers to be shifted, so all demos and docs need to be updated accordingly.
"webAddress" to apiAddress,
"artemisAddress" to networkMapAddress.toString(),
"p2pAddress" to networkMapAddress.toString(),
"useTestClock" to useTestClock
)
)
@ -536,7 +539,7 @@ open class DriverDSL(
val process = builder.start()
// TODO There is a race condition here. Even though the messaging address is bound it may be the case that
// the handlers for the advertised services are not yet registered. Needs rethinking.
return addressMustBeBound(executorService, nodeConf.artemisAddress).map { process }
return addressMustBeBound(executorService, nodeConf.p2pAddress).map { process }
}
private fun startWebserver(
@ -554,7 +557,7 @@ open class DriverDSL(
emptyList()
val javaArgs = listOf(path) +
listOf("-Dname=node-${nodeConf.artemisAddress}-webserver") + debugPortArg +
listOf("-Dname=node-${nodeConf.p2pAddress}-webserver") + debugPortArg +
listOf(
"-cp", classpath, className,
"--base-directory", nodeConf.baseDirectory.toString())

View File

@ -27,6 +27,7 @@ import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.node.services.transactions.RaftUniquenessProvider
import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.node.utilities.AddressUtils
import net.corda.node.services.transactions.*
import net.corda.node.utilities.AffinityExecutor
import org.slf4j.Logger
import java.io.RandomAccessFile
@ -141,9 +142,9 @@ class Node(override val configuration: FullNodeConfiguration,
private fun makeLocalMessageBroker(): HostAndPort {
with(configuration) {
val useHost = tryDetectIfNotPublicHost(artemisAddress.hostText)
val useAddress = useHost?.let { HostAndPort.fromParts(it, artemisAddress.port) } ?: artemisAddress
messageBroker = ArtemisMessagingServer(this, useAddress, services.networkMapCache, userService)
val useHost = tryDetectIfNotPublicHost(p2pAddress.hostText)
val useAddress = useHost?.let { HostAndPort.fromParts(it, p2pAddress.port) } ?: p2pAddress
messageBroker = ArtemisMessagingServer(this, useAddress, rpcAddress, services.networkMapCache, userService)
return useAddress
}
}

View File

@ -15,7 +15,6 @@ import net.corda.core.div
import net.corda.core.exists
import net.corda.core.utilities.loggerFor
import java.net.URL
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.time.Instant
@ -49,6 +48,9 @@ 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
@ -101,7 +103,7 @@ inline fun <reified T : Any> Config.getListOrElse(path: String, default: Config.
*/
fun NodeConfiguration.configureWithDevSSLCertificate() = configureDevKeyAndTrustStores(myLegalName)
private fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: String) {
fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: String) {
certificatesDirectory.createDirectories()
if (!trustStoreFile.exists()) {
javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordatruststore.jks").copyTo(trustStoreFile)
@ -113,15 +115,3 @@ private fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: String)
X509Utilities.createKeystoreForSSL(keyStoreFile, keyStorePassword, keyStorePassword, caKeyStore, "cordacadevkeypass", myLegalName)
}
}
// TODO Move this to CoreTestUtils.kt once we can pry this from the explorer
@JvmOverloads
fun configureTestSSL(legalName: String = "Mega Corp."): SSLConfiguration = object : SSLConfiguration {
override val certificatesDirectory = Files.createTempDirectory("certs")
override val keyStorePassword: String get() = "cordacadevpass"
override val trustStorePassword: String get() = "trustpass"
init {
configureDevKeyAndTrustStores(legalName)
}
}

View File

@ -65,15 +65,16 @@ class FullNodeConfiguration(override val baseDirectory: Path, val config: Config
User(username, password, permissions)
}
val useHTTPS: Boolean by config
val artemisAddress: HostAndPort by config
val p2pAddress: HostAndPort by config
val rpcAddress: HostAndPort? by config
val webAddress: HostAndPort by config
// TODO This field is slightly redundant as artemisAddress is sufficient to hold the address of the node's MQ broker.
// TODO This field is slightly redundant as p2pAddress is sufficient to hold the address of the node's MQ broker.
// Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one
val messagingServerAddress: HostAndPort? by config.getOrElse { null }
val messagingServerAddress: HostAndPort? by config
val extraAdvertisedServiceIds: List<String> = config.getListOrElse<String>("extraAdvertisedServiceIds") { emptyList() }
val useTestClock: Boolean by config.getOrElse { false }
val notaryNodeId: Int? by config.getOrElse { null }
val notaryNodeAddress: HostAndPort? by config.getOrElse { null }
val notaryNodeId: Int? by config
val notaryNodeAddress: HostAndPort? by config
val notaryClusterAddresses: List<HostAndPort> = config
.getListOrElse<String>("notaryClusterAddresses") { emptyList() }
.map { HostAndPort.fromString(it) }

View File

@ -22,7 +22,7 @@ import java.security.KeyStore
/**
* The base class for Artemis services that defines shared data structures and SSL transport configuration.
*/
abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() {
abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() {
companion object {
init {
System.setProperty("org.jboss.logging.provider", "slf4j")
@ -88,6 +88,7 @@ abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() {
fun asPeer(peerIdentity: CompositeKey, hostAndPort: HostAndPort): NodeAddress {
return NodeAddress("$PEERS_PREFIX${peerIdentity.toBase58String()}", hostAndPort)
}
fun asService(serviceIdentity: CompositeKey, hostAndPort: HostAndPort): NodeAddress {
return NodeAddress("$SERVICES_PREFIX${serviceIdentity.toBase58String()}", hostAndPort)
}
@ -137,7 +138,10 @@ abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() {
}
}
protected fun tcpTransport(direction: ConnectionDirection, host: String, port: Int): TransportConfiguration {
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
@ -151,7 +155,7 @@ abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() {
TransportConstants.PROTOCOLS_PROP_NAME to "CORE,AMQP"
)
if (config != null) {
if (config != null && enableSSL) {
config.keyStoreFile.expectedOnDefaultFileSystem()
config.trustStoreFile.expectedOnDefaultFileSystem()
val tlsOptions = mapOf<String, Any?>(

View File

@ -81,7 +81,8 @@ import javax.security.cert.X509Certificate
*/
@ThreadSafe
class ArtemisMessagingServer(override val config: NodeConfiguration,
val myHostPort: HostAndPort,
val p2pHostPort: HostAndPort,
val rpcHostPort: HostAndPort?,
val networkMapCache: NetworkMapCache,
val userService: RPCUserService) : ArtemisMessagingComponent() {
companion object {
@ -139,7 +140,10 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
registerPostQueueDeletionCallback { address, qName -> log.debug { "Queue deleted: $qName for $address" } }
}
activeMQServer.start()
printBasicNodeInfo("Node ${this.config.myLegalName} listening on address", myHostPort.toString())
printBasicNodeInfo("Node ${this.config.myLegalName} listening on address", p2pHostPort.toString())
if (rpcHostPort != null) {
printBasicNodeInfo("Node ${this.config.myLegalName} RPC service listening on address", rpcHostPort.toString())
}
}
private fun createArtemisConfig(): Configuration = ConfigurationImpl().apply {
@ -147,7 +151,11 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
bindingsDirectory = (artemisDir / "bindings").toString()
journalDirectory = (artemisDir / "journal").toString()
largeMessagesDirectory = (artemisDir / "large-messages").toString()
acceptorConfigurations = setOf(tcpTransport(Inbound, "0.0.0.0", myHostPort.port))
val acceptors = mutableSetOf(tcpTransport(Inbound, "0.0.0.0", p2pHostPort.port))
if (rpcHostPort != null) {
acceptors.add(tcpTransport(Inbound, "0.0.0.0", rpcHostPort.port, enableSSL = false))
}
acceptorConfigurations = acceptors
// Enable built in message deduplication. Note we still have to do our own as the delayed commits
// and our own definition of commit mean that the built in deduplication cannot remove all duplicates.
idCacheSize = 2000 // Artemis Default duplicate cache size i.e. a guess
@ -160,15 +168,15 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
// by having its password be an unknown securely random 128-bit value.
clusterPassword = BigInteger(128, newSecureRandom()).toString(16)
queueConfigurations = listOf(
queueConfig(NETWORK_MAP_QUEUE, durable = true),
queueConfig(P2P_QUEUE, durable = true),
// Create an RPC queue: this will service locally connected clients only (not via a bridge) and those
// clients must have authenticated. We could use a single consumer for everything and perhaps we should,
// but these queues are not worth persisting.
queueConfig(RPC_REQUESTS_QUEUE, durable = false),
// The custom name for the queue is intentional - we may wish other things to subscribe to the
// NOTIFICATIONS_ADDRESS with different filters in future
queueConfig(RPC_QUEUE_REMOVALS_QUEUE, address = NOTIFICATIONS_ADDRESS, filter = "_AMQ_NotifType = 1", durable = false)
queueConfig(NETWORK_MAP_QUEUE, durable = true),
queueConfig(P2P_QUEUE, durable = true),
// Create an RPC queue: this will service locally connected clients only (not via a bridge) and those
// clients must have authenticated. We could use a single consumer for everything and perhaps we should,
// but these queues are not worth persisting.
queueConfig(RPC_REQUESTS_QUEUE, durable = false),
// The custom name for the queue is intentional - we may wish other things to subscribe to the
// NOTIFICATIONS_ADDRESS with different filters in future
queueConfig(RPC_QUEUE_REMOVALS_QUEUE, address = NOTIFICATIONS_ADDRESS, filter = "_AMQ_NotifType = 1", durable = false)
)
configureAddressSecurity()
}
@ -290,8 +298,8 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
fun deployBridges(node: NodeInfo) {
gatherAddresses(node)
.filter { queueExists(it.queueName) && !bridgeExists(it.bridgeName) }
.forEach { deployBridge(it, node.legalIdentity.name) }
.filter { queueExists(it.queueName) && !bridgeExists(it.bridgeName) }
.forEach { deployBridge(it, node.legalIdentity.name) }
}
fun destroyBridges(node: NodeInfo) {
@ -397,8 +405,7 @@ private class VerifyingNettyConnector(configuration: MutableMap<String, Any>?,
threadPool: Executor?,
scheduledThreadPool: ScheduledExecutorService?,
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 expectedCommonName = configuration?.get(ArtemisMessagingComponent.VERIFY_PEER_COMMON_NAME) as? String
@ -480,15 +487,15 @@ class NodeLoginModule : LoginModule {
val username = nameCallback.name ?: throw FailedLoginException("Username not provided")
val password = String(passwordCallback.password ?: throw FailedLoginException("Password not provided"))
val certificates = certificateCallback.certificates
log.info("Processing login for $username")
val validatedUser = if (username == PEER_USER || username == NODE_USER) {
val certificates = certificateCallback.certificates ?: throw FailedLoginException("No TLS?")
authenticateNode(certificates, username)
} else {
// Otherwise assume they're an RPC user
authenticateRpcUser(password, username)
val validatedUser = when (determineUserRole(certificates, username)) {
PEER_ROLE -> authenticatePeer(certificates)
NODE_ROLE -> authenticateNode(certificates)
RPC_ROLE -> authenticateRpcUser(password, username)
else -> throw FailedLoginException("Peer does not belong on our network")
}
principals += UserPrincipal(validatedUser)
@ -496,24 +503,24 @@ class NodeLoginModule : LoginModule {
return loginSucceeded
}
private fun authenticateNode(certificates: Array<X509Certificate>, username: String): String {
private fun authenticateNode(certificates: Array<X509Certificate>): String {
val peerCertificate = certificates.first()
val role = if (username == NODE_USER) {
if (peerCertificate.publicKey != ourPublicKey) {
throw FailedLoginException("Only the node can login as $NODE_USER")
}
NODE_ROLE
} else {
val theirRootCAPublicKey = certificates.last().publicKey
if (theirRootCAPublicKey != ourRootCAPublicKey) {
throw FailedLoginException("Peer does not belong on our network. Their root CA: $theirRootCAPublicKey")
}
PEER_ROLE // This enables the peer to send to our P2P address
if (peerCertificate.publicKey != ourPublicKey) {
throw FailedLoginException("Only the node can login as $NODE_USER")
}
principals += RolePrincipal(role)
principals += RolePrincipal(NODE_ROLE)
return peerCertificate.subjectDN.name
}
private fun authenticatePeer(certificates: Array<X509Certificate>): String {
val theirRootCAPublicKey = certificates.last().publicKey
if (theirRootCAPublicKey != ourRootCAPublicKey) {
throw FailedLoginException("Peer does not belong on our network. Their root CA: $theirRootCAPublicKey")
}
principals += RolePrincipal(PEER_ROLE)
return certificates.first().subjectDN.name
}
private fun authenticateRpcUser(password: String, username: String): String {
val rpcUser = userService.getUser(username) ?: throw FailedLoginException("User does not exist")
if (password != rpcUser.password) {
@ -526,6 +533,18 @@ class NodeLoginModule : LoginModule {
return username
}
private fun determineUserRole(certificates: Array<X509Certificate>?, username: String): String? {
return if (username == PEER_USER || username == NODE_USER) {
certificates ?: throw FailedLoginException("No TLS?")
if (username == PEER_USER) PEER_ROLE else NODE_ROLE
} else if (certificates == null) {
// Assume they're an RPC user if its from a non-ssl connection
RPC_ROLE
} else {
null
}
}
override fun commit(): Boolean {
val result = loginSucceeded
if (result) {

View File

@ -24,10 +24,10 @@ import javax.annotation.concurrent.ThreadSafe
* useful tasks. See the documentation for [proxy] or review the docsite to learn more about how this API works.
*
* @param host The hostname and messaging port of the node.
* @param config If specified, the SSL configuration to use. If not specified, SSL will be disabled and the node will not be authenticated, nor will RPC traffic be encrypted.
* @param config If specified, the SSL configuration to use. If not specified, SSL will be disabled and the node will only be authenticated on non-SSL RPC port, the RPC traffic with not be encrypted when SSL is disabled.
*/
@ThreadSafe
class CordaRPCClient(val host: HostAndPort, override val config: SSLConfiguration?, val serviceConfigurationOverride: (ServerLocator.() -> Unit)? = null) : Closeable, ArtemisMessagingComponent() {
class CordaRPCClient(val host: HostAndPort, override val config: SSLConfiguration? = null, val serviceConfigurationOverride: (ServerLocator.() -> Unit)? = null) : Closeable, ArtemisMessagingComponent() {
private companion object {
val log = loggerFor<CordaRPCClient>()
}
@ -52,7 +52,7 @@ class CordaRPCClient(val host: HostAndPort, override val config: SSLConfiguratio
check(!running)
log.logElapsedTime("Startup") {
checkStorePasswords()
val serverLocator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport(Outbound(), host.hostText, host.port)).apply {
val serverLocator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport(Outbound(), host.hostText, host.port, enableSSL = config != null)).apply {
// TODO: Put these in config file or make it user configurable?
threadPoolMaxSize = 1
confirmationWindowSize = 100000 // a guess

View File

@ -48,6 +48,7 @@ class ArtemisMessagingTests {
@Rule @JvmField val temporaryFolder = TemporaryFolder()
val hostAndPort = freeLocalHostAndPort()
val rpcHostAndPort = freeLocalHostAndPort()
val topic = "platform.self"
val identity = generateKeyPair()
@ -232,8 +233,8 @@ class ArtemisMessagingTests {
}
}
private fun createMessagingServer(local: HostAndPort = hostAndPort): ArtemisMessagingServer {
return ArtemisMessagingServer(config, local, networkMapCache, userService).apply {
private fun createMessagingServer(local: HostAndPort = hostAndPort, rpc: HostAndPort = rpcHostAndPort): ArtemisMessagingServer {
return ArtemisMessagingServer(config, local, rpc, networkMapCache, userService).apply {
config.configureWithDevSSLCertificate()
messagingServer = this
}

View File

@ -153,7 +153,7 @@ class NodeWebServer(val config: FullNodeConfiguration) {
try {
return connectLocalRpcAsNodeUser()
} catch (e: ActiveMQNotConnectedException) {
log.debug("Could not connect to ${config.artemisAddress} due to exception: ", e)
log.debug("Could not connect to ${config.p2pAddress} due to exception: ", e)
Thread.sleep(retryDelay)
// This error will happen if the server has yet to create the keystore
// Keep the fully qualified package name due to collisions with the Kotlin stdlib
@ -166,8 +166,8 @@ class NodeWebServer(val config: FullNodeConfiguration) {
}
private fun connectLocalRpcAsNodeUser(): CordaRPCOps {
log.info("Connecting to node at ${config.artemisAddress} as node user")
val client = CordaRPCClient(config.artemisAddress, config)
log.info("Connecting to node at ${config.p2pAddress} as node user")
val client = CordaRPCClient(config.p2pAddress, config)
client.start(ArtemisMessagingComponent.NODE_USER, ArtemisMessagingComponent.NODE_USER)
return client.proxy()
}
@ -176,4 +176,4 @@ class NodeWebServer(val config: FullNodeConfiguration) {
val pluginRegistries: List<CordaPluginRegistry> by lazy {
ServiceLoader.load(CordaPluginRegistry::class.java).toList()
}
}
}

View File

@ -1 +1 @@
gradlePluginsVersion=0.8.3
gradlePluginsVersion=0.10.0

View File

@ -69,8 +69,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Controller"
nearestCity "London"
advertisedServices ["corda.notary.validating"]
artemisPort 10002
webPort 10003
p2pPort 10002
rpcPort 10003
webPort 10004
cordapps = []
rpcUsers = ext.rpcUsers
}
@ -78,8 +79,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank A"
nearestCity "London"
advertisedServices = []
artemisPort 10004
webPort 10005
p2pPort 10005
rpcPort 10006
webPort 10007
cordapps = []
rpcUsers = ext.rpcUsers
}
@ -87,8 +89,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank B"
nearestCity "New York"
advertisedServices = []
artemisPort 10006
webPort 10007
p2pPort 10008
rpcPort 10009
webPort 10010
cordapps = []
rpcUsers = ext.rpcUsers
}

View File

@ -28,7 +28,6 @@ fun main(args: Array<String>) {
val parser = OptionParser()
val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java).required()
val certsPath = parser.accepts("certificates").withRequiredArg()
val options = try {
parser.parse(*args)
} catch (e: Exception) {
@ -40,16 +39,16 @@ fun main(args: Array<String>) {
val role = options.valueOf(roleArg)!!
when (role) {
Role.SENDER -> {
val host = HostAndPort.fromString("localhost:10004")
val host = HostAndPort.fromString("localhost:10006")
println("Connecting to sender node ($host)")
CordaRPCClient(host, sslConfigFor("BankA", options.valueOf(certsPath))).use("demo", "demo") {
CordaRPCClient(host).use("demo", "demo") {
sender(this)
}
}
Role.RECIPIENT -> {
val host = HostAndPort.fromString("localhost:10006")
val host = HostAndPort.fromString("localhost:10009")
println("Connecting to the recipient node ($host)")
CordaRPCClient(host, sslConfigFor("BankB", options.valueOf(certsPath))).use("demo", "demo") {
CordaRPCClient(host).use("demo", "demo") {
recipient(this)
}
}
@ -111,7 +110,6 @@ private fun printHelp(parser: OptionParser) {
parser.printHelpOn(System.out)
}
// TODO: Take this out once we have a dedicated RPC port and allow SSL on it to be optional.
private fun sslConfigFor(nodename: String, certsPath: String?): SSLConfiguration {
return object : SSLConfiguration {

View File

@ -66,16 +66,18 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Notary"
nearestCity "London"
advertisedServices = ["corda.notary.validating"]
artemisPort 10002
webPort 10003
p2pPort 10002
rpcPort 10003
webPort 10004
cordapps = []
}
node {
name "BankOfCorda"
nearestCity "London"
advertisedServices = ["corda.issuer.USD"]
artemisPort 10004
webPort 10005
p2pPort 10005
rpcPort 10006
webPort 10007
cordapps = []
rpcUsers = [
['user' : "bankUser",
@ -88,8 +90,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "BigCorporation"
nearestCity "New York"
advertisedServices = []
artemisPort 10006
webPort 10007
p2pPort 10008
rpcPort 10009
webPort 10010
cordapps = []
rpcUsers = [
['user' : "bigCorpUser",

View File

@ -66,13 +66,13 @@ private class BankOfCordaDriver {
when (role) {
Role.ISSUE_CASH_RPC -> {
println("Requesting Cash via RPC ...")
val result = BankOfCordaClientApi(HostAndPort.fromString("localhost:10004")).requestRPCIssue(requestParams)
val result = BankOfCordaClientApi(HostAndPort.fromString("localhost:10006")).requestRPCIssue(requestParams)
if (result is SignedTransaction)
println("Success!! You transaction receipt is ${result.tx.id}")
}
Role.ISSUE_CASH_WEB -> {
println("Requesting Cash via Web ...")
val result = BankOfCordaClientApi(HostAndPort.fromString("localhost:10005")).requestWebIssue(requestParams)
val result = BankOfCordaClientApi(HostAndPort.fromString("localhost:10007")).requestWebIssue(requestParams)
if (result)
println("Successfully processed Cash Issue request")
}

View File

@ -9,7 +9,6 @@ import net.corda.core.messaging.startFlow
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.SignedTransaction
import net.corda.flows.IssuerFlow.IssuanceRequester
import net.corda.node.services.config.configureTestSSL
import net.corda.node.services.messaging.CordaRPCClient
import net.corda.testing.http.HttpApi
@ -26,11 +25,12 @@ class BankOfCordaClientApi(val hostAndPort: HostAndPort) {
val api = HttpApi.fromHostAndPort(hostAndPort, apiRoot)
return api.postJson("issue-asset-request", params)
}
/**
* RPC API
*/
fun requestRPCIssue(params: IssueRequestParams): SignedTransaction {
val client = CordaRPCClient(hostAndPort, configureTestSSL())
val client = CordaRPCClient(hostAndPort)
// TODO: privileged security controls required
client.start("bankUser", "test")
val proxy = client.proxy()

View File

@ -70,8 +70,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Notary"
nearestCity "London"
advertisedServices = ["corda.notary.validating", "corda.interest_rates"]
artemisPort 10002
webPort 10003
p2pPort 10002
rpcPort 10003
webPort 10004
cordapps = []
useTestClock true
}
@ -79,8 +80,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank A"
nearestCity "London"
advertisedServices = []
artemisPort 10004
webPort 10005
p2pPort 10005
rpcPort 10006
webPort 10007
cordapps = []
useTestClock true
}
@ -88,8 +90,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank B"
nearestCity "New York"
advertisedServices = []
artemisPort 10006
webPort 10007
p2pPort 10008
rpcPort 10009
webPort 10010
cordapps = []
useTestClock true
}

View File

@ -55,7 +55,7 @@ class IRSDemoTest : IntegrationTestCategory {
}
fun getFixingDateObservable(config: FullNodeConfiguration): BlockingObservable<LocalDate?> {
val client = CordaRPCClient(config.artemisAddress, config)
val client = CordaRPCClient(config.rpcAddress!!)
client.start("user", "password")
val proxy = client.proxy()
val vaultUpdates = proxy.vaultAndUpdates().second

View File

@ -30,9 +30,9 @@ fun main(args: Array<String>) {
val role = options.valueOf(roleArg)!!
val value = options.valueOf(valueArg)
when (role) {
Role.UploadRates -> IRSDemoClientApi(HostAndPort.fromString("localhost:10003")).runUploadRates()
Role.Trade -> IRSDemoClientApi(HostAndPort.fromString("localhost:10005")).runTrade(value)
Role.Date -> IRSDemoClientApi(HostAndPort.fromString("localhost:10007")).runDateChange(value)
Role.UploadRates -> IRSDemoClientApi(HostAndPort.fromString("localhost:10004")).runUploadRates()
Role.Trade -> IRSDemoClientApi(HostAndPort.fromString("localhost:10007")).runTrade(value)
Role.Date -> IRSDemoClientApi(HostAndPort.fromString("localhost:10010")).runDateChange(value)
}
}

View File

@ -89,8 +89,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build', 'generat
name "Party"
nearestCity "London"
advertisedServices = []
artemisPort 10002
webPort 10003
p2pPort 10002
rpcPort 10003
webPort 10004
cordapps = []
rpcUsers = [['user': "demo", 'password': "demo", 'permissions': [
'StartFlow.net.corda.notarydemo.flows.DummyIssueAndMove',
@ -101,16 +102,18 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build', 'generat
name "Counterparty"
nearestCity "New York"
advertisedServices = []
artemisPort 10004
webPort 10005
p2pPort 10005
rpcPort 10006
webPort 10007
cordapps = []
}
node {
name "Notary 1"
nearestCity "London"
advertisedServices = [advertisedNotary]
artemisPort 10007
webPort 10008
p2pPort 10008
rpcPort 10009
webPort 10010
cordapps = []
notaryNodePort 11002
}
@ -118,8 +121,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build', 'generat
name "Notary 2"
nearestCity "London"
advertisedServices = [advertisedNotary]
artemisPort 10010
webPort 10011
p2pPort 10011
rpcPort 10012
webPort 10013
cordapps = []
notaryNodePort 11004
notaryClusterAddresses = ["localhost:11002"]
@ -128,8 +132,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build', 'generat
name "Notary 3"
nearestCity "London"
advertisedServices = [advertisedNotary]
artemisPort 10013
webPort 10014
p2pPort 10014
rpcPort 10015
webPort 10016
cordapps = []
notaryNodePort 11006
notaryClusterAddresses = ["localhost:11002"]

View File

@ -18,10 +18,9 @@ import java.nio.file.Paths
import kotlin.system.exitProcess
fun main(args: Array<String>) {
val certPath = getCertPath(args)
val host = HostAndPort.fromString("localhost:10002")
val host = HostAndPort.fromString("localhost:10003")
println("Connecting to the recipient node ($host)")
CordaRPCClient(host, sslConfigFor("Party", certPath)).use("demo", "demo") {
CordaRPCClient(host).use("demo", "demo") {
val api = NotaryDemoClientApi(this)
api.startNotarisation()
}

View File

@ -85,7 +85,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Controller"
nearestCity "London"
advertisedServices = ["corda.notary.validating"]
artemisPort 10002
p2pPort 10002
webPort 10003
cordapps = []
}
@ -93,7 +93,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank A"
nearestCity "London"
advertisedServices = []
artemisPort 10004
p2pPort 10004
webPort 10005
cordapps = []
}
@ -101,7 +101,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank B"
nearestCity "New York"
advertisedServices = []
artemisPort 10006
p2pPort 10006
webPort 10007
cordapps = []
}
@ -109,7 +109,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank C"
nearestCity "tokyo"
advertisedServices = []
artemisPort 10008
p2pPort 10008
webPort 10009
cordapps = []
}

View File

@ -81,16 +81,17 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Notary"
nearestCity "London"
advertisedServices = ["corda.notary.validating"]
artemisPort 10002
webPort 10003
p2pPort 10002
webPort 10004
cordapps = []
}
node {
name "Bank A"
nearestCity "London"
advertisedServices = []
artemisPort 10004
webPort 10005
p2pPort 10005
rpcPort 10006
webPort 10007
cordapps = []
rpcUsers = ext.rpcUsers
}
@ -98,8 +99,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank B"
nearestCity "New York"
advertisedServices = []
artemisPort 10006
webPort 10007
p2pPort 10008
rpcPort 10009
webPort 10010
cordapps = []
rpcUsers = ext.rpcUsers
}
@ -107,8 +109,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "BankOfCorda"
nearestCity "London"
advertisedServices = []
artemisPort 10008
webPort 10009
p2pPort 10011
webPort 10012
cordapps = []
}
}

View File

@ -27,7 +27,7 @@ class TraderDemoTest : NodeBasedTest() {
).getOrThrow()
val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map {
val client = CordaRPCClient(it.configuration.artemisAddress, it.configuration)
val client = CordaRPCClient(it.configuration.rpcAddress!!)
client.start(demoUser[0].username, demoUser[0].password).proxy()
}

View File

@ -31,7 +31,6 @@ private class TraderDemo {
fun main(args: Array<String>) {
val parser = OptionParser()
val certsPath = parser.accepts("certificates").withRequiredArg()
val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java).required()
val options = try {
@ -46,13 +45,13 @@ private class TraderDemo {
// will contact the buyer and actually make something happen.
val role = options.valueOf(roleArg)!!
if (role == Role.BUYER) {
val host = HostAndPort.fromString("localhost:10004")
CordaRPCClient(host, sslConfigFor("BankA", options.valueOf(certsPath))).use("demo", "demo") {
val host = HostAndPort.fromString("localhost:10006")
CordaRPCClient(host).use("demo", "demo") {
TraderDemoClientApi(this).runBuyer()
}
} else {
val host = HostAndPort.fromString("localhost:10006")
CordaRPCClient(host, sslConfigFor("BankB", options.valueOf(certsPath))).use("demo", "demo") {
val host = HostAndPort.fromString("localhost:10009")
CordaRPCClient(host).use("demo", "demo") {
TraderDemoClientApi(this).runSeller(1000.DOLLARS, "Bank A")
}
}
@ -66,13 +65,4 @@ private class TraderDemo {
""".trimIndent())
parser.printHelpOn(System.out)
}
// TODO: Take this out once we have a dedicated RPC port and allow SSL on it to be optional.
private fun sslConfigFor(nodename: String, certsPath: String?): SSLConfiguration {
return object : SSLConfiguration {
override val keyStorePassword: String = "cordacadevpass"
override val trustStorePassword: String = "trustpass"
override val certificatesDirectory: Path = if (certsPath != null) Paths.get(certsPath) else Paths.get("build") / "nodes" / nodename / "certificates"
}
}
}

View File

@ -5,6 +5,7 @@ package net.corda.testing
import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.ListenableFuture
import com.typesafe.config.Config
import net.corda.core.contracts.StateRef
import net.corda.core.crypto.*
import net.corda.core.flows.FlowLogic
@ -19,6 +20,8 @@ import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.node.internal.AbstractNode
import net.corda.node.internal.NetworkMapInfo
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.statemachine.FlowStateMachineImpl
import net.corda.node.utilities.AddOrRemove.ADD
import net.corda.testing.node.MockIdentityService
@ -26,6 +29,7 @@ import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties
import java.net.ServerSocket
import java.net.URL
import java.nio.file.Files
import java.nio.file.Path
import java.security.KeyPair
import java.util.*
@ -163,3 +167,16 @@ data class TestNodeConfiguration(
override val exportJMXto: String = "",
override val devMode: Boolean = true,
override val certificateSigningService: URL = URL("http://localhost")) : NodeConfiguration
fun Config.getHostAndPort(name: String) = HostAndPort.fromString(getString(name))
@JvmOverloads
fun configureTestSSL(legalName: String = "Mega Corp."): SSLConfiguration = object : SSLConfiguration {
override val certificatesDirectory = Files.createTempDirectory("certs")
override val keyStorePassword: String get() = "cordacadevpass"
override val trustStorePassword: String get() = "trustpass"
init {
configureDevKeyAndTrustStores(legalName)
}
}

View File

@ -2,22 +2,22 @@ package net.corda.testing.messaging
import com.google.common.net.HostAndPort
import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.config.configureTestSSL
import net.corda.node.services.messaging.ArtemisMessagingComponent
import net.corda.node.services.messaging.ArtemisMessagingComponent.ConnectionDirection.Outbound
import net.corda.testing.configureTestSSL
import org.apache.activemq.artemis.api.core.client.*
/**
* As the name suggests this is a simple client for connecting to MQ brokers.
*/
class SimpleMQClient(val target: HostAndPort,
override val config: SSLConfiguration = configureTestSSL("SimpleMQClient")) : ArtemisMessagingComponent() {
override val config: SSLConfiguration? = configureTestSSL("SimpleMQClient")) : ArtemisMessagingComponent() {
lateinit var sessionFactory: ClientSessionFactory
lateinit var session: ClientSession
lateinit var producer: ClientProducer
fun start(username: String? = null, password: String? = null) {
val tcpTransport = tcpTransport(Outbound(), target.hostText, target.port)
fun start(username: String? = null, password: String? = null, enableSSL: Boolean = true) {
val tcpTransport = tcpTransport(Outbound(), target.hostText, target.port, enableSSL)
val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {
isBlockOnNonDurableSend = true
threadPoolMaxSize = 1

View File

@ -73,7 +73,7 @@ abstract class NodeBasedTest {
rpcUsers,
mapOf(
"networkMapService" to mapOf(
"address" to networkMapNode.configuration.artemisAddress.toString(),
"address" to networkMapNode.configuration.p2pAddress.toString(),
"legalName" to networkMapNode.info.legalIdentity.name
)
) + configOverrides
@ -121,7 +121,8 @@ abstract class NodeBasedTest {
allowMissingConfig = true,
configOverrides = mapOf(
"myLegalName" to legalName,
"artemisAddress" to freeLocalHostAndPort().toString(),
"p2pAddress" to freeLocalHostAndPort().toString(),
"rpcAddress" to freeLocalHostAndPort().toString(),
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
"rpcUsers" to rpcUsers.map {
mapOf(
@ -141,4 +142,4 @@ abstract class NodeBasedTest {
}
return node
}
}
}

View File

@ -24,14 +24,14 @@ import kotlin.concurrent.thread
* This is a bare-bones node which can only send and receive messages. It doesn't register with a network map service or
* any other such task that would make it functionable in a network and thus left to the user to do so manually.
*/
class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeLocalHostAndPort()) : AutoCloseable {
class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeLocalHostAndPort(), rpcAddress: HostAndPort = freeLocalHostAndPort()) : AutoCloseable {
private val databaseWithCloseable: Pair<Closeable, Database> = configureDatabase(config.dataSourceProperties)
val database: Database get() = databaseWithCloseable.second
val userService = RPCUserServiceImpl(config)
val identity: KeyPair = generateKeyPair()
val executor = ServiceAffinityExecutor(config.myLegalName, 1)
val broker = ArtemisMessagingServer(config, address, InMemoryNetworkMapCache(), userService)
val broker = ArtemisMessagingServer(config, address, rpcAddress, InMemoryNetworkMapCache(), userService)
val networkMapRegistrationFuture: SettableFuture<Unit> = SettableFuture.create<Unit>()
val net = databaseTransaction(database) {
NodeMessagingClient(

View File

@ -14,7 +14,6 @@ import net.corda.client.model.Models
import net.corda.client.model.observableValue
import net.corda.core.contracts.GBP
import net.corda.core.contracts.USD
import net.corda.core.messaging.startFlow
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.ServiceType
import net.corda.explorer.model.CordaViewModel
@ -28,7 +27,6 @@ import net.corda.flows.IssuerFlow.IssuanceRequester
import net.corda.node.driver.PortAllocation
import net.corda.node.driver.driver
import net.corda.node.services.User
import net.corda.node.services.messaging.ArtemisMessagingComponent
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.SimpleNotaryService
import org.apache.commons.lang.SystemUtils
@ -141,7 +139,7 @@ fun main(args: Array<String>) {
val issuerNodeUSD = issuerUSD.get()
arrayOf(notaryNode, aliceNode, bobNode, issuerNodeGBP, issuerNodeUSD).forEach {
println("${it.nodeInfo.legalIdentity} started on ${ArtemisMessagingComponent.toHostAndPort(it.nodeInfo.address)}")
println("${it.nodeInfo.legalIdentity} started on ${it.configuration.rpcAddress}")
}
val parser = OptionParser("S")

View File

@ -30,9 +30,6 @@ class SettingsModel(path: Path = Paths.get("conf")) : Component(), Observable {
private var username: String by config
private var reportingCurrency: Currency by config
private var fullscreen: Boolean by config
private var certificatesDir: Path by config
private var keyStorePassword: String by config
private var trustStorePassword: String by config
// Create observable Properties.
val reportingCurrencyProperty = writableConfigProperty(SettingsModel::reportingCurrency)
@ -41,10 +38,6 @@ class SettingsModel(path: Path = Paths.get("conf")) : Component(), Observable {
val portProperty = writableConfigProperty(SettingsModel::port)
val usernameProperty = writableConfigProperty(SettingsModel::username)
val fullscreenProperty = writableConfigProperty(SettingsModel::fullscreen)
val certificatesDirProperty = writableConfigProperty(SettingsModel::certificatesDir)
// TODO : We should encrypt all passwords in config file.
val keyStorePasswordProperty = writableConfigProperty(SettingsModel::keyStorePassword)
val trustStorePasswordProperty = writableConfigProperty(SettingsModel::trustStorePassword)
init {
load()

View File

@ -5,16 +5,16 @@ import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView
import javafx.beans.property.SimpleIntegerProperty
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.objectProperty
import net.corda.core.exists
import net.corda.explorer.model.SettingsModel
import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.config.configureTestSSL
import org.controlsfx.dialog.ExceptionDialog
import tornadofx.*
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.system.exitProcess
class LoginView : View() {
@ -26,7 +26,6 @@ class LoginView : View() {
private val passwordTextField by fxid<PasswordField>()
private val rememberMeCheckBox by fxid<CheckBox>()
private val fullscreenCheckBox by fxid<CheckBox>()
private val certificateButton by fxid<Button>()
private val portProperty = SimpleIntegerProperty()
private val rememberMe by objectProperty(SettingsModel::rememberMeProperty)
@ -34,9 +33,6 @@ class LoginView : View() {
private val host by objectProperty(SettingsModel::hostProperty)
private val port by objectProperty(SettingsModel::portProperty)
private val fullscreen by objectProperty(SettingsModel::fullscreenProperty)
private val certificatesDir by objectProperty(SettingsModel::certificatesDirProperty)
private val keyStorePasswordProperty by objectProperty(SettingsModel::keyStorePasswordProperty)
private val trustStorePasswordProperty by objectProperty(SettingsModel::trustStorePasswordProperty)
fun login() {
val status = Dialog<LoginStatus>().apply {
@ -46,7 +42,7 @@ class LoginView : View() {
ButtonBar.ButtonData.OK_DONE -> try {
root.isDisable = true
// TODO : Run this async to avoid UI lockup.
getModel<NodeMonitorModel>().register(HostAndPort.fromParts(hostTextField.text, portProperty.value), configureSSL(), usernameTextField.text, passwordTextField.text)
getModel<NodeMonitorModel>().register(HostAndPort.fromParts(hostTextField.text, portProperty.value), usernameTextField.text, passwordTextField.text)
if (!rememberMe.value) {
username.value = ""
host.value = ""
@ -79,18 +75,6 @@ class LoginView : View() {
if (status != LoginStatus.loggedIn) login()
}
private fun configureSSL(): SSLConfiguration {
val sslConfig = object : SSLConfiguration {
override val certificatesDirectory: Path get() = certificatesDir.get()
override val keyStorePassword: String get() = keyStorePasswordProperty.get()
override val trustStorePassword: String get() = trustStorePasswordProperty.get()
}
// TODO : Don't use dev certificates.
return if (sslConfig.keyStoreFile.exists()) sslConfig else configureTestSSL().apply {
alert(Alert.AlertType.WARNING, "", "KeyStore not found in certificates directory.\nDEV certificates will be used by default.")
}
}
init {
// Restrict text field to Integer only.
portTextField.textFormatter = intFormatter().apply { portProperty.bind(this.valueProperty()) }
@ -99,44 +83,6 @@ class LoginView : View() {
usernameTextField.textProperty().bindBidirectional(username)
hostTextField.textProperty().bindBidirectional(host)
portTextField.textProperty().bindBidirectional(port)
certificateButton.setOnAction {
Dialog<ButtonType>().apply {
title = "Certificates Settings"
initOwner(root.scene.window)
dialogPane.content = gridpane {
vgap = 10.0
hgap = 5.0
row("Certificates Directory :") {
textfield {
prefWidth = 400.0
textProperty().bind(certificatesDir.map(Path::toString))
isEditable = false
}
button {
graphic = FontAwesomeIconView(FontAwesomeIcon.FOLDER_OPEN_ALT)
maxHeight = Double.MAX_VALUE
setOnAction {
chooseDirectory(owner = dialogPane.scene.window) {
initialDirectoryProperty().bind(certificatesDir.map(Path::toFile))
}?.let {
certificatesDir.set(it.toPath())
}
}
}
}
row("KeyStore Password :") { passwordfield(keyStorePasswordProperty) }
row("TrustStore Password :") { passwordfield(trustStorePasswordProperty) }
}
dialogPane.buttonTypes.addAll(ButtonType.APPLY, ButtonType.CANCEL)
}.showAndWait().get().let {
when (it) {
ButtonType.APPLY -> getModel<SettingsModel>().commit()
// Discard changes.
else -> getModel<SettingsModel>().load()
}
}
}
certificateButton.tooltip("Certificate Configuration")
}
private enum class LoginStatus {

View File

@ -26,23 +26,15 @@
<Label text="Corda Node :" GridPane.halignment="RIGHT"/>
<TextField fx:id="hostTextField" promptText="Host" GridPane.columnIndex="1"/>
<TextField fx:id="portTextField" prefWidth="100" promptText="Port" GridPane.columnIndex="2"/>
<Button id="certificateButton" fx:id="certificateButton" GridPane.columnIndex="3" styleClass="certificateButton">
<padding>
<Insets right="6"/>
</padding>
<graphic>
<FontAwesomeIconView styleClass="certificateIcon" glyphName="LOCK" glyphSize="20"/>
</graphic>
</Button>
<Label text="Username :" GridPane.rowIndex="1" GridPane.halignment="RIGHT"/>
<TextField fx:id="usernameTextField" promptText="Username" GridPane.columnIndex="1"
GridPane.columnSpan="3" GridPane.rowIndex="1"/>
GridPane.columnSpan="2" GridPane.rowIndex="1"/>
<Label text="Password :" GridPane.rowIndex="2" GridPane.halignment="RIGHT"/>
<PasswordField fx:id="passwordTextField" promptText="Password" GridPane.columnIndex="1"
GridPane.columnSpan="3" GridPane.rowIndex="2"/>
GridPane.columnSpan="2" GridPane.rowIndex="2"/>
<HBox spacing="20" GridPane.columnIndex="1" GridPane.rowIndex="3" GridPane.columnSpan="3">
<HBox spacing="20" GridPane.columnIndex="1" GridPane.rowIndex="3" GridPane.columnSpan="2">
<CheckBox fx:id="rememberMeCheckBox" text="Remember me"/>
<CheckBox fx:id="fullscreenCheckBox" text="Fullscreen mode"/>
</HBox>

View File

@ -71,8 +71,6 @@ class ConnectionManager(private val username: String, private val jSch: JSch) {
nodeHost: String,
remoteMessagingPort: Int,
localTunnelAddress: HostAndPort,
certificatesBaseDirectory: Path,
remoteCertificatesDirectory: Path,
rpcUsername: String,
rpcPassword: String
): NodeConnection {
@ -87,19 +85,7 @@ class ConnectionManager(private val username: String, private val jSch: JSch) {
session.setPortForwardingL(localTunnelAddress.port, localTunnelAddress.hostText, remoteMessagingPort)
log.info("Tunnel created!")
val certificatesDirectory = certificatesBaseDirectory / nodeHost
val sslKeyStoreFileName = "sslkeystore.jks"
val trustStoreFileName = "truststore.jks"
log.info("Copying server certificates to $certificatesDirectory")
certificatesDirectory.createDirectories()
val channel = session.openChannel("sftp") as ChannelSftp
channel.connect()
channel.get((remoteCertificatesDirectory / sslKeyStoreFileName).toString(), certificatesDirectory.toString())
channel.get((remoteCertificatesDirectory / trustStoreFileName).toString(), certificatesDirectory.toString())
channel.disconnect()
log.info("Certificates copied!")
val connection = NodeConnection(nodeHost, session, localTunnelAddress, certificatesDirectory, rpcUsername, rpcPassword)
val connection = NodeConnection(nodeHost, session, localTunnelAddress, rpcUsername, rpcPassword)
connection.startClient()
return connection
}
@ -122,7 +108,6 @@ fun <A> connectToNodes(
nodeHostsAndCertificatesPaths: List<Pair<String, Path>>,
remoteMessagingPort: Int,
tunnelPortAllocation: PortAllocation,
certificatesBaseDirectory: Path,
rpcUsername: String,
rpcPassword: String,
withConnections: (List<NodeConnection>) -> A
@ -133,8 +118,6 @@ fun <A> connectToNodes(
nodeHost = nodeHostAndCertificatesPath.first,
remoteMessagingPort = remoteMessagingPort,
localTunnelAddress = tunnelPortAllocation.nextHostAndPort(),
certificatesBaseDirectory = certificatesBaseDirectory,
remoteCertificatesDirectory = nodeHostAndCertificatesPath.second,
rpcUsername = rpcUsername,
rpcPassword = rpcPassword
)
@ -157,17 +140,9 @@ class NodeConnection(
val hostName: String,
private val jSchSession: Session,
private val localTunnelAddress: HostAndPort,
private val certificatesDirectory: Path,
private val rpcUsername: String,
private val rpcPassword: String
) : Closeable {
private val sslConfig = object : SSLConfiguration {
override val certificatesDirectory = this@NodeConnection.certificatesDirectory
override val keyStorePassword: String get() = "cordacadevpass"
override val trustStorePassword: String get() = "trustpass"
}
private var client: CordaRPCClient? = null
private var _proxy: CordaRPCOps? = null
val proxy: CordaRPCOps get() = _proxy ?: throw IllegalStateException("proxy requested, but the client is not running")
@ -202,7 +177,7 @@ class NodeConnection(
return action()
} finally {
log.info("Starting new RPC proxy to $hostName, tunnel at $localTunnelAddress")
val newClient = CordaRPCClient(localTunnelAddress, sslConfig)
val newClient = CordaRPCClient(localTunnelAddress)
// TODO expose these somehow?
newClient.start(rpcUsername, rpcPassword)
val newProxy = newClient.proxy()
@ -213,7 +188,7 @@ class NodeConnection(
fun startClient() {
log.info("Creating RPC proxy to $hostName, tunnel at $localTunnelAddress")
val client = CordaRPCClient(localTunnelAddress, sslConfig)
val client = CordaRPCClient(localTunnelAddress)
client.start(rpcUsername, rpcPassword)
val proxy = client.proxy()
log.info("Proxy created")

View File

@ -163,7 +163,6 @@ fun runLoadTests(configuration: LoadTestConfiguration, tests: List<Pair<LoadTest
configuration.nodeHosts.map { it to configuration.remoteNodeDirectory / "certificates" },
configuration.remoteMessagingPort,
PortAllocation.Incremental(configuration.localTunnelStartingPort),
configuration.localCertificatesBaseDirectory,
configuration.rpcUsername,
configuration.rpcPassword
) { connections ->

View File

@ -3,7 +3,7 @@
localCertificatesBaseDirectory = "build/load-test/certificates"
localTunnelStartingPort = 10000
remoteNodeDirectory = "/opt/corda"
remoteMessagingPort = 10002
remoteMessagingPort = 10003
remoteSystemdServiceName = "corda"
rpcUsername = "corda"
rpcPassword = "rgb"
rpcPassword = "rgb"