mirror of
https://github.com/corda/corda.git
synced 2024-12-20 21:43:14 +00:00
Removed SSLConfiguration parameter from CordaRPCClient, thus removing SSL support.
The current use of SSL RPC relies on access to the node's keystore file, and further to that some uses where using the NODE_USER to login on the p2p port.
This commit is contained in:
parent
b4e674c2fe
commit
4df8b427d2
@ -57,7 +57,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
|
||||
CordaFuture<StartedNode<Node>> nodeFuture = startNode(getALICE().getName(), 1, services, singletonList(rpcUser), emptyMap());
|
||||
node = nodeFuture.get();
|
||||
node.getInternals().registerCustomSchemas(Collections.singleton(CashSchemaV1.INSTANCE));
|
||||
client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcAddress()), null, getDefault(), false);
|
||||
client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcAddress()), getDefault(), false);
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -7,7 +7,6 @@ import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
|
||||
import net.corda.nodeapi.ConnectionDirection
|
||||
import net.corda.nodeapi.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
|
||||
import java.time.Duration
|
||||
|
||||
@ -34,9 +33,9 @@ data class CordaRPCClientConfiguration(
|
||||
}
|
||||
|
||||
/** @see RPCClient */
|
||||
//TODO Add SSL support
|
||||
class CordaRPCClient(
|
||||
hostAndPort: NetworkHostAndPort,
|
||||
sslConfiguration: SSLConfiguration? = null,
|
||||
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.default,
|
||||
initialiseSerialization: Boolean = true
|
||||
) {
|
||||
@ -50,7 +49,7 @@ class CordaRPCClient(
|
||||
}
|
||||
|
||||
private val rpcClient = RPCClient<CordaRPCOps>(
|
||||
tcpTransport(ConnectionDirection.Outbound(), hostAndPort, sslConfiguration),
|
||||
tcpTransport(ConnectionDirection.Outbound(), hostAndPort, config = null),
|
||||
configuration.toRpcClientConfiguration(),
|
||||
KRYO_RPC_CLIENT_CONTEXT
|
||||
)
|
||||
|
@ -228,6 +228,9 @@ Release 1.0
|
||||
|
||||
* ``@RPCSinceVersion``, ``RPCException`` and ``PermissionException`` have moved to ``net.corda.client.rpc``.
|
||||
|
||||
* Current implementation of SSL in ``CordaRPCClient`` has been removed until we have a better solution which doesn't rely
|
||||
on the node's keystore.
|
||||
|
||||
Milestone 14
|
||||
------------
|
||||
|
||||
|
@ -1,17 +1,15 @@
|
||||
package net.corda.nodeapi
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.utilities.toBase58String
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.messaging.MessageRecipientGroup
|
||||
import net.corda.core.messaging.MessageRecipients
|
||||
import net.corda.core.messaging.SingleMessageRecipient
|
||||
import net.corda.core.internal.read
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.toBase58String
|
||||
import net.corda.nodeapi.config.SSLConfiguration
|
||||
import java.security.KeyStore
|
||||
import java.security.PublicKey
|
||||
|
||||
/**
|
||||
@ -85,22 +83,6 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() {
|
||||
/** The config object is used to pass in the passwords for the certificate KeyStore and TrustStore */
|
||||
abstract val config: SSLConfiguration?
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
fun checkStorePasswords() {
|
||||
val config = config ?: return
|
||||
arrayOf(config.sslKeystore, config.nodeKeystore).forEach {
|
||||
it.read {
|
||||
KeyStore.getInstance("JKS").load(it, config.keyStorePassword.toCharArray())
|
||||
}
|
||||
}
|
||||
config.trustStoreFile.read {
|
||||
KeyStore.getInstance("JKS").load(it, config.trustStorePassword.toCharArray())
|
||||
}
|
||||
}
|
||||
|
||||
// Used for bridges creation.
|
||||
fun getArtemisPeerAddress(party: Party, address: NetworkHostAndPort, netMapName: CordaX500Name? = null): ArtemisPeerAddress {
|
||||
return if (party.name == netMapName) {
|
||||
|
@ -1,10 +1,7 @@
|
||||
package net.corda.services.messaging
|
||||
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.configureTestSSL
|
||||
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
|
||||
|
||||
/**
|
||||
@ -26,11 +23,4 @@ class MQSecurityAsRPCTest : MQSecurityTest() {
|
||||
override fun startAttacker(attacker: SimpleMQClient) {
|
||||
attacker.start(extraRPCUsers[0].username, extraRPCUsers[0].password, false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `login to a ssl port as a RPC user`() {
|
||||
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
|
||||
loginToRPC(alice.internals.configuration.p2pAddress, extraRPCUsers[0], configureTestSSL())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,8 +153,8 @@ abstract class MQSecurityTest : NodeBasedTest() {
|
||||
return client
|
||||
}
|
||||
|
||||
fun loginToRPC(target: NetworkHostAndPort, rpcUser: User, sslConfiguration: SSLConfiguration? = null): CordaRPCOps {
|
||||
return CordaRPCClient(target, sslConfiguration, initialiseSerialization = false).start(rpcUser.username, rpcUser.password).proxy
|
||||
fun loginToRPC(target: NetworkHostAndPort, rpcUser: User): CordaRPCOps {
|
||||
return CordaRPCClient(target, initialiseSerialization = false).start(rpcUser.username, rpcUser.password).proxy
|
||||
}
|
||||
|
||||
fun loginToRPCAndGetClientQueue(): String {
|
||||
|
@ -20,15 +20,11 @@ import net.corda.core.utilities.seconds
|
||||
import net.corda.finance.plugin.registerFinanceJSONMappers
|
||||
import net.corda.irs.contract.InterestRateSwap
|
||||
import net.corda.irs.utilities.uploadFile
|
||||
import net.corda.nodeapi.internal.ServiceInfo
|
||||
import net.corda.node.services.config.FullNodeConfiguration
|
||||
import net.corda.node.services.transactions.SimpleNotaryService
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.DUMMY_BANK_A
|
||||
import net.corda.testing.DUMMY_BANK_B
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.IntegrationTestCategory
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.nodeapi.internal.ServiceInfo
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.http.HttpApi
|
||||
import org.apache.commons.io.IOUtils
|
||||
@ -45,7 +41,7 @@ class IRSDemoTest : IntegrationTestCategory {
|
||||
val log = loggerFor<IRSDemoTest>()
|
||||
}
|
||||
|
||||
private val rpcUser = User("user", "password", emptySet())
|
||||
private val rpcUser = User("user", "password", setOf("ALL"))
|
||||
private val currentDate: LocalDate = LocalDate.now()
|
||||
private val futureDate: LocalDate = currentDate.plusMonths(6)
|
||||
private val maxWaitTime: Duration = 60.seconds
|
||||
@ -58,15 +54,16 @@ class IRSDemoTest : IntegrationTestCategory {
|
||||
providedName = DUMMY_NOTARY.name,
|
||||
advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))),
|
||||
startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(rpcUser)),
|
||||
startNode(providedName = DUMMY_BANK_B.name)).map { it.getOrThrow() }
|
||||
startNode(providedName = DUMMY_BANK_B.name))
|
||||
.map { it.getOrThrow() }
|
||||
|
||||
log.info("All nodes started")
|
||||
|
||||
val controllerAddrFuture = startWebserver(controller)
|
||||
val nodeAAddrFuture = startWebserver(nodeA)
|
||||
val nodeBAddrFuture = startWebserver(nodeB)
|
||||
val (controllerAddr, nodeAAddr, nodeBAddr) =
|
||||
listOf(controllerAddrFuture, nodeAAddrFuture, nodeBAddrFuture).map { it.getOrThrow().listenAddress }
|
||||
val (controllerAddr, nodeAAddr, nodeBAddr) = listOf(
|
||||
startWebserver(controller),
|
||||
startWebserver(nodeA),
|
||||
startWebserver(nodeB))
|
||||
.map { it.getOrThrow().listenAddress }
|
||||
|
||||
log.info("All webservers started")
|
||||
|
||||
|
@ -23,16 +23,14 @@ import net.corda.core.utilities.*
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.NodeStartup
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.nodeapi.internal.ServiceInfo
|
||||
import net.corda.nodeapi.internal.ServiceType
|
||||
import net.corda.node.services.config.*
|
||||
import net.corda.node.services.network.NetworkMapService
|
||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||
import net.corda.node.utilities.ServiceIdentityGenerator
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.config.SSLConfiguration
|
||||
import net.corda.nodeapi.config.parseAs
|
||||
import net.corda.nodeapi.internal.ServiceInfo
|
||||
import net.corda.nodeapi.internal.ServiceType
|
||||
import net.corda.nodeapi.internal.addShutdownHook
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
||||
@ -178,6 +176,10 @@ interface DriverDSLInternalInterface : DriverDSLExposedInterface {
|
||||
|
||||
sealed class NodeHandle {
|
||||
abstract val nodeInfo: NodeInfo
|
||||
/**
|
||||
* Interface to the node's RPC system. The first RPC user will be used to login if are any, otherwise a default one
|
||||
* will be added and that will be used.
|
||||
*/
|
||||
abstract val rpc: CordaRPCOps
|
||||
abstract val configuration: FullNodeConfiguration
|
||||
abstract val webAddress: NetworkHostAndPort
|
||||
@ -627,20 +629,21 @@ class DriverDSL(
|
||||
_executorService?.shutdownNow()
|
||||
}
|
||||
|
||||
private fun establishRpc(nodeAddress: NetworkHostAndPort, sslConfig: SSLConfiguration, processDeathFuture: CordaFuture<out Process>): CordaFuture<CordaRPCOps> {
|
||||
val client = CordaRPCClient(nodeAddress, sslConfig, initialiseSerialization = false)
|
||||
private fun establishRpc(config: FullNodeConfiguration, processDeathFuture: CordaFuture<out Process>): CordaFuture<CordaRPCOps> {
|
||||
val rpcAddress = config.rpcAddress!!
|
||||
val client = CordaRPCClient(rpcAddress, initialiseSerialization = false)
|
||||
val connectionFuture = poll(executorService, "RPC connection") {
|
||||
try {
|
||||
client.start(ArtemisMessagingComponent.NODE_USER, ArtemisMessagingComponent.NODE_USER)
|
||||
client.start(config.rpcUsers[0].username, config.rpcUsers[0].password)
|
||||
} catch (e: Exception) {
|
||||
if (processDeathFuture.isDone) throw e
|
||||
log.error("Exception $e, Retrying RPC connection at $nodeAddress")
|
||||
log.error("Exception $e, Retrying RPC connection at $rpcAddress")
|
||||
null
|
||||
}
|
||||
}
|
||||
return firstOf(connectionFuture, processDeathFuture) {
|
||||
if (it == processDeathFuture) {
|
||||
throw ListenProcessDeathException(nodeAddress, processDeathFuture.getOrThrow())
|
||||
throw ListenProcessDeathException(rpcAddress, processDeathFuture.getOrThrow())
|
||||
}
|
||||
val connection = connectionFuture.getOrThrow()
|
||||
shutdownManager.registerShutdown(connection::close)
|
||||
@ -696,7 +699,7 @@ class DriverDSL(
|
||||
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
|
||||
"networkMapService" to networkMapServiceConfigLookup(name),
|
||||
"useTestClock" to useTestClock,
|
||||
"rpcUsers" to rpcUsers.map { it.toMap() },
|
||||
"rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers.map { it.toMap() },
|
||||
"verifierType" to verifierType.name
|
||||
) + customOverrides
|
||||
)
|
||||
@ -709,14 +712,14 @@ class DriverDSL(
|
||||
portAllocation.nextHostAndPort() // rpcAddress
|
||||
val webAddress = portAllocation.nextHostAndPort()
|
||||
val name = CordaX500Name.parse(node.name)
|
||||
|
||||
val rpcUsers = node.rpcUsers
|
||||
val config = ConfigHelper.loadConfig(
|
||||
baseDirectory = baseDirectory(name),
|
||||
allowMissingConfig = true,
|
||||
configOverrides = node.config + mapOf(
|
||||
"extraAdvertisedServiceIds" to node.advertisedServices,
|
||||
"networkMapService" to networkMapServiceConfigLookup(name),
|
||||
"rpcUsers" to node.rpcUsers,
|
||||
"rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers,
|
||||
"notaryClusterAddresses" to node.notaryClusterAddresses
|
||||
)
|
||||
)
|
||||
@ -808,6 +811,7 @@ class DriverDSL(
|
||||
|
||||
override fun startDedicatedNetworkMapService(startInProcess: Boolean?): CordaFuture<NodeHandle> {
|
||||
val webAddress = portAllocation.nextHostAndPort()
|
||||
val rpcAddress = portAllocation.nextHostAndPort()
|
||||
val networkMapLegalName = networkMapStartStrategy.legalName
|
||||
val config = ConfigHelper.loadConfig(
|
||||
baseDirectory = baseDirectory(networkMapLegalName),
|
||||
@ -817,6 +821,8 @@ 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 webAddress.toString(),
|
||||
"rpcAddress" to rpcAddress.toString(),
|
||||
"rpcUsers" to defaultRpcUserList,
|
||||
"p2pAddress" to dedicatedNetworkMapAddress.toString(),
|
||||
"useTestClock" to useTestClock,
|
||||
"extraAdvertisedServiceIds" to listOf(ServiceInfo(NetworkMapService.type).toString())
|
||||
@ -838,7 +844,7 @@ class DriverDSL(
|
||||
}
|
||||
)
|
||||
return nodeAndThreadFuture.flatMap { (node, thread) ->
|
||||
establishRpc(nodeConfiguration.p2pAddress, nodeConfiguration, openFuture()).flatMap { rpc ->
|
||||
establishRpc(nodeConfiguration, openFuture()).flatMap { rpc ->
|
||||
rpc.waitUntilNetworkReady().map {
|
||||
NodeHandle.InProcess(rpc.nodeInfo(), rpc, nodeConfiguration, webAddress, node, thread)
|
||||
}
|
||||
@ -852,8 +858,7 @@ class DriverDSL(
|
||||
val processDeathFuture = poll(executorService, "process death") {
|
||||
if (process.isAlive) null else process
|
||||
}
|
||||
// We continue to use SSL enabled port for RPC when its for node user.
|
||||
establishRpc(nodeConfiguration.p2pAddress, nodeConfiguration, processDeathFuture).flatMap { rpc ->
|
||||
establishRpc(nodeConfiguration, processDeathFuture).flatMap { rpc ->
|
||||
// Call waitUntilNetworkReady in background in case RPC is failing over:
|
||||
val networkMapFuture = executorService.fork {
|
||||
rpc.waitUntilNetworkReady()
|
||||
@ -877,6 +882,8 @@ class DriverDSL(
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val defaultRpcUserList = listOf(User("default", "default", setOf("ALL")).toMap())
|
||||
|
||||
private val names = arrayOf(
|
||||
ALICE.name,
|
||||
BOB.name,
|
||||
|
@ -235,7 +235,7 @@ class NodeConfigTest {
|
||||
val webConfig = WebServerConfig(baseDir, nodeConfig)
|
||||
|
||||
assertEquals(localPort(20001), webConfig.webAddress)
|
||||
assertEquals(localPort(10001), webConfig.p2pAddress)
|
||||
assertEquals(localPort(40002), webConfig.rpcAddress)
|
||||
assertEquals("trustpass", webConfig.trustStorePassword)
|
||||
assertEquals("cordacadevpass", webConfig.keyStorePassword)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.corda.webserver
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.config.NodeSSLConfiguration
|
||||
import net.corda.nodeapi.config.getValue
|
||||
import java.nio.file.Path
|
||||
@ -15,6 +16,7 @@ class WebServerConfig(override val baseDirectory: Path, val config: Config) : No
|
||||
val exportJMXto: String get() = "http"
|
||||
val useHTTPS: Boolean by config
|
||||
val myLegalName: String by config
|
||||
val p2pAddress: NetworkHostAndPort by config // TODO: Use RPC port instead of P2P port (RPC requires authentication, P2P does not)
|
||||
val rpcAddress: NetworkHostAndPort by config
|
||||
val webAddress: NetworkHostAndPort by config
|
||||
val rpcUsers: List<User> by config
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import net.corda.client.jackson.JacksonSupport
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent
|
||||
import net.corda.webserver.WebServerConfig
|
||||
import net.corda.webserver.converters.CordaConverterProvider
|
||||
import net.corda.webserver.services.WebServerPluginRegistry
|
||||
@ -26,6 +25,7 @@ import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.io.Writer
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.nio.file.NoSuchFileException
|
||||
import java.util.*
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.ws.rs.core.MediaType
|
||||
@ -187,12 +187,12 @@ class NodeWebServer(val config: WebServerConfig) {
|
||||
try {
|
||||
return connectLocalRpcAsNodeUser()
|
||||
} catch (e: ActiveMQNotConnectedException) {
|
||||
log.debug("Could not connect to ${config.p2pAddress} due to exception: ", e)
|
||||
log.debug("Could not connect to ${config.rpcAddress} 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
|
||||
// exception of the same name
|
||||
} catch (e: java.nio.file.NoSuchFileException) {
|
||||
} catch (e: NoSuchFileException) {
|
||||
log.debug("Tried to open a file that doesn't yet exist, retrying", e)
|
||||
Thread.sleep(retryDelay)
|
||||
} catch (e: Throwable) {
|
||||
@ -205,9 +205,10 @@ class NodeWebServer(val config: WebServerConfig) {
|
||||
}
|
||||
|
||||
private fun connectLocalRpcAsNodeUser(): CordaRPCOps {
|
||||
log.info("Connecting to node at ${config.p2pAddress} as node user")
|
||||
val client = CordaRPCClient(config.p2pAddress, config)
|
||||
val connection = client.start(ArtemisMessagingComponent.NODE_USER, ArtemisMessagingComponent.NODE_USER)
|
||||
val rpcUser = config.rpcUsers.firstOrNull() ?: throw IllegalArgumentException("The node config has not specified any RPC users")
|
||||
log.info("Connecting to node at ${config.rpcAddress} as $rpcUser")
|
||||
val client = CordaRPCClient(config.rpcAddress)
|
||||
val connection = client.start(rpcUser.username, rpcUser.password)
|
||||
return connection.proxy
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user