mirror of
https://github.com/corda/corda.git
synced 2025-01-31 00:24:59 +00:00
Merge pull request #1052 from corda/tudor-merge-21-06
Tudor merge 21 06
This commit is contained in:
commit
d0300261cb
@ -124,21 +124,25 @@ absolute path to the node's base directory.
|
||||
:maxRestartCount: Maximum number of times the flow will restart before resulting in an error.
|
||||
:backoffBase: The base of the exponential backoff, `t_{wait} = timeout * backoffBase^{retryCount}`.
|
||||
|
||||
: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. This is now deprecated in favour of the ``rpcSettings`` block.
|
||||
:rpcAddress: (Deprecated) 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. This is now deprecated in favour of the ``rpcSettings`` block.
|
||||
|
||||
:rpcSettings: Options for the RPC server.
|
||||
:rpcSettings: Options for the RPC server exposed by the Node.
|
||||
|
||||
:useSsl: (optional) boolean, indicates whether the node should require clients to use SSL for RPC connections, defaulted to ``false``.
|
||||
:address: host and port for the RPC server binding.
|
||||
:adminAddress: host and port for the RPC admin binding (this is the endpoint that the node process will connect to).
|
||||
:standAloneBroker: (optional) boolean, indicates whether the node will connect to a standalone broker for RPC, defaulted to ``false``.
|
||||
:address: (optional) host and port for the RPC server binding, if any.
|
||||
:adminAddress: (optional) host and port for the RPC admin binding (only required when ``useSsl`` is ``false``, because the node connects to Artemis using SSL to ensure admin privileges are not accessible outside the node).
|
||||
:ssl: (optional) SSL settings for the RPC server.
|
||||
:useSsl: (optional) boolean, indicates whether or not the node should require clients to use SSL for RPC connections, defaulted to ``false``.
|
||||
:ssl: (mandatory if ``useSsl=true``) SSL settings for the RPC server.
|
||||
|
||||
:keyStorePath: Absolute path to the key store containing the RPC SSL certificate.
|
||||
:keyStorePassword: Password for the key store.
|
||||
|
||||
.. note:: The RPC SSL certificate is used by RPC clients to authenticate the connection.
|
||||
The Node operator must provide RPC clients with a truststore containing the certificate they can trust.
|
||||
We advise Node operators to not use the P2P keystore for RPC.
|
||||
The node ships with a command line argument "--just-generate-rpc-ssl-settings", which generates a secure keystore
|
||||
and truststore that can be used to secure the RPC connection. You can use this if you have no special requirements.
|
||||
|
||||
:keyStorePassword: password for the key store.
|
||||
:trustStorePassword: password for the trust store.
|
||||
:certificatesDirectory: directory in which the stores will be searched, unless absolute paths are provided.
|
||||
:sslKeystore: absolute path to the ssl key store, defaulted to ``certificatesDirectory / "sslkeystore.jks"``.
|
||||
:trustStoreFile: absolute path to the trust store, defaulted to ``certificatesDirectory / "truststore.jks"``.
|
||||
|
||||
:security: Contains various nested fields controlling user authentication/authorization, in particular for RPC accesses. See
|
||||
:doc:`clientrpc` for details.
|
||||
@ -307,76 +311,16 @@ Simple notary configuration file:
|
||||
devMode : false
|
||||
compatibilityZoneURL : "https://cz.corda.net"
|
||||
|
||||
An example ``web-server.conf`` file is as follow:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
myLegalName : "O=Notary Service,OU=corda,L=London,C=GB"
|
||||
keyStorePassword : "cordacadevpass"
|
||||
trustStorePassword : "trustpass"
|
||||
rpcSettings = {
|
||||
useSsl = false
|
||||
standAloneBroker = false
|
||||
address : "my-corda-node:10003"
|
||||
adminAddress : "my-corda-node:10004"
|
||||
}
|
||||
rpcUsers : [{ username=user1, password=letmein, permissions=[ StartFlow.net.corda.protocols.CashProtocol ] }]
|
||||
|
||||
Configuring a node where the Corda Comatability Zone's registration and Network Map services exist on different URLs
|
||||
Configuring a node where the Corda Compatibility Zone's registration and Network Map services exist on different URLs
|
||||
|
||||
.. literalinclude:: example-code/src/main/resources/example-node-with-networkservices.conf
|
||||
|
||||
Fields
|
||||
------
|
||||
|
||||
The available config fields are listed below. ``baseDirectory`` is available as a substitution value, containing the absolute
|
||||
path to the node's base directory.
|
||||
|
||||
:myLegalName: The legal identity of the node acts as a human readable alias to the node's public key and several demos use
|
||||
this to lookup the NodeInfo.
|
||||
|
||||
:keyStorePassword: The password to unlock the KeyStore file (``<workspace>/certificates/sslkeystore.jks``) containing the
|
||||
node certificate and private key.
|
||||
|
||||
.. note:: This is the non-secret value for the development certificates automatically generated during the first node run.
|
||||
Longer term these keys will be managed in secure hardware devices.
|
||||
|
||||
:trustStorePassword: The password to unlock the Trust store file (``<workspace>/certificates/truststore.jks``) containing
|
||||
the Corda network root certificate. This is the non-secret value for the development certificates automatically
|
||||
generated during the first node run.
|
||||
|
||||
.. note:: Longer term these keys will be managed in secure hardware devices.
|
||||
|
||||
:rpcSettings: Options for the RPC server.
|
||||
|
||||
:useSsl: (optional) boolean, indicates whether the node should require clients to use SSL for RPC connections, defaulted to ``false``.
|
||||
:standAloneBroker: (optional) boolean, indicates whether the node will connect to a standalone broker for RPC, defaulted to ``false``.
|
||||
:address: (optional) host and port for the RPC server binding, if any.
|
||||
:adminAddress: (optional) host and port for the RPC admin binding (only required when ``useSsl`` is ``false``, because the node connects to Artemis using SSL to ensure admin privileges are not accessible outside the node).
|
||||
:ssl: (optional) SSL settings for the RPC client.
|
||||
|
||||
:keyStorePassword: password for the key store.
|
||||
:trustStorePassword: password for the trust store.
|
||||
:certificatesDirectory: directory in which the stores will be searched, unless absolute paths are provided.
|
||||
:sslKeystore: absolute path to the ssl key store, defaulted to ``certificatesDirectory / "sslkeystore.jks"``.
|
||||
:trustStoreFile: absolute path to the trust store, defaulted to ``certificatesDirectory / "truststore.jks"``.
|
||||
:trustStoreFile: absolute path to the trust store, defaulted to ``certificatesDirectory / "truststore.jks"``.
|
||||
|
||||
:rpcUsers: A list of users who are authorised to access the RPC system. Each user in the list is a config object with the
|
||||
following fields:
|
||||
|
||||
:username: Username consisting only of word characters (a-z, A-Z, 0-9 and _)
|
||||
:password: The password
|
||||
:permissions: A list of permissions for starting flows via RPC. To give the user the permission to start the flow
|
||||
``foo.bar.FlowClass``, add the string ``StartFlow.foo.bar.FlowClass`` to the list. If the list
|
||||
contains the string ``ALL``, the user can start any flow via RPC. This value is intended for administrator
|
||||
users and for development.
|
||||
|
||||
Fields Override
|
||||
---------------
|
||||
JVM options or environmental variables prefixed ``corda.`` can override ``node.conf`` fields.
|
||||
Provided system properties also can set value for absent fields in ``node.conf``.
|
||||
Example adding/overriding keyStore password when starting Corda node:
|
||||
JVM options or environmental variables prefixed with ``corda.`` can override ``node.conf`` fields.
|
||||
Provided system properties can also set values for absent fields in ``node.conf``.
|
||||
|
||||
This is an example of adding/overriding the keyStore password :
|
||||
|
||||
.. sourcecode:: shell
|
||||
|
||||
|
@ -58,7 +58,7 @@ object X509Utilities {
|
||||
const val CORDA_CLIENT_TLS = "cordaclienttls"
|
||||
const val CORDA_CLIENT_CA = "cordaclientca"
|
||||
|
||||
private val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days)
|
||||
val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days)
|
||||
|
||||
/**
|
||||
* Helper function to return the latest out of an instant and an optional date.
|
||||
|
@ -17,6 +17,9 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.services.Permissions.Companion.all
|
||||
import net.corda.nodeapi.BrokerRpcSslOptions
|
||||
import net.corda.core.messaging.ClientRpcSslOptions
|
||||
import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate
|
||||
import net.corda.node.utilities.saveToKeyStore
|
||||
import net.corda.node.utilities.saveToTrustStore
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_RPC_USER
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
@ -28,9 +31,6 @@ import net.corda.testing.driver.internal.RandomFree
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.internal.createKeyPairAndSelfSignedCertificate
|
||||
import net.corda.testing.internal.saveToKeyStore
|
||||
import net.corda.testing.internal.saveToTrustStore
|
||||
import net.corda.testing.internal.useSslRpcOverrides
|
||||
import net.corda.testing.node.User
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQException
|
||||
@ -41,6 +41,7 @@ import org.junit.ClassRule
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
class RpcSslTest : IntegrationTest() {
|
||||
companion object {
|
||||
@ -54,6 +55,7 @@ class RpcSslTest : IntegrationTest() {
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
|
||||
|
||||
@Test
|
||||
fun `RPC client using ssl is able to run a command`() {
|
||||
@ -61,7 +63,7 @@ class RpcSslTest : IntegrationTest() {
|
||||
var successfulLogin = false
|
||||
var failedLogin = false
|
||||
|
||||
val (keyPair, cert) = createKeyPairAndSelfSignedCertificate()
|
||||
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(testName)
|
||||
val keyStorePath = saveToKeyStore(tempFolder.root.toPath() / "keystore.jks", keyPair, cert)
|
||||
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
|
||||
|
||||
@ -98,11 +100,11 @@ class RpcSslTest : IntegrationTest() {
|
||||
val user = User("mark", "dadada", setOf(all()))
|
||||
var successful = false
|
||||
|
||||
val (keyPair, cert) = createKeyPairAndSelfSignedCertificate()
|
||||
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(testName)
|
||||
val keyStorePath = saveToKeyStore(tempFolder.root.toPath() / "keystore.jks", keyPair, cert)
|
||||
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
|
||||
|
||||
val (_, cert1) = createKeyPairAndSelfSignedCertificate()
|
||||
val (_, cert1) = createKeyPairAndSelfSignedTLSCertificate(testName)
|
||||
val trustStorePath = saveToTrustStore(tempFolder.root.toPath() / "truststore.jks", cert1)
|
||||
val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password")
|
||||
|
||||
@ -140,7 +142,7 @@ class RpcSslTest : IntegrationTest() {
|
||||
|
||||
@Test
|
||||
fun `The system RPC user can not connect to the rpc broker without the node's key`() {
|
||||
val (keyPair, cert) = createKeyPairAndSelfSignedCertificate()
|
||||
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(testName)
|
||||
val keyStorePath = saveToKeyStore(tempFolder.root.toPath() / "keystore.jks", keyPair, cert)
|
||||
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
|
||||
val trustStorePath = saveToTrustStore(tempFolder.root.toPath() / "truststore.jks", cert)
|
||||
|
@ -64,6 +64,8 @@ class NodeArgsParser : AbstractArgsParser<CmdLineOptions>() {
|
||||
private val isVersionArg = optionParser.accepts("version", "Print the version and exit")
|
||||
private val justGenerateNodeInfoArg = optionParser.accepts("just-generate-node-info",
|
||||
"Perform the node start-up task necessary to generate its nodeInfo, save it to disk, then quit")
|
||||
private val justGenerateRpcSslCertsArg = optionParser.accepts("just-generate-rpc-ssl-settings",
|
||||
"Generate the ssl keystore and truststore for a secure RPC connection.")
|
||||
private val bootstrapRaftClusterArg = optionParser.accepts("bootstrap-raft-cluster", "Bootstraps Raft cluster. The node forms a single node cluster (ignoring otherwise configured peer addresses), acting as a seed for other nodes to join the cluster.")
|
||||
private val clearNetworkMapCache = optionParser.accepts("clear-network-map-cache", "Clears local copy of network map, on node startup it will be restored from server or file system.")
|
||||
|
||||
@ -85,6 +87,7 @@ class NodeArgsParser : AbstractArgsParser<CmdLineOptions>() {
|
||||
val noLocalShell = optionSet.has(noLocalShellArg)
|
||||
val sshdServer = optionSet.has(sshdServerArg)
|
||||
val justGenerateNodeInfo = optionSet.has(justGenerateNodeInfoArg)
|
||||
val justGenerateRpcSslCerts = optionSet.has(justGenerateRpcSslCertsArg)
|
||||
val bootstrapRaftCluster = optionSet.has(bootstrapRaftClusterArg)
|
||||
val networkRootTrustStorePath = optionSet.valueOf(networkRootTrustStorePathArg)
|
||||
val networkRootTrustStorePassword = optionSet.valueOf(networkRootTrustStorePasswordArg)
|
||||
@ -109,6 +112,7 @@ class NodeArgsParser : AbstractArgsParser<CmdLineOptions>() {
|
||||
noLocalShell,
|
||||
sshdServer,
|
||||
justGenerateNodeInfo,
|
||||
justGenerateRpcSslCerts,
|
||||
bootstrapRaftCluster,
|
||||
unknownConfigKeysPolicy,
|
||||
devMode,
|
||||
@ -127,6 +131,7 @@ data class CmdLineOptions(val baseDirectory: Path,
|
||||
val noLocalShell: Boolean,
|
||||
val sshdServer: Boolean,
|
||||
val justGenerateNodeInfo: Boolean,
|
||||
val justGenerateRpcSslCerts: Boolean,
|
||||
val bootstrapRaftCluster: Boolean,
|
||||
val unknownConfigKeysPolicy: UnknownConfigKeysPolicy,
|
||||
val devMode: Boolean,
|
||||
|
@ -261,7 +261,7 @@ open class Node(configuration: NodeConfiguration,
|
||||
val rpcBrokerDirectory: Path = baseDirectory / "brokers" / "rpc"
|
||||
with(rpcOptions) {
|
||||
rpcBroker = if (useSsl) {
|
||||
ArtemisRpcBroker.withSsl(configuration, this.address, adminAddress, sslConfig, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell())
|
||||
ArtemisRpcBroker.withSsl(configuration, this.address, adminAddress, sslConfig!!, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell())
|
||||
} else {
|
||||
ArtemisRpcBroker.withoutSsl(configuration, this.address, adminAddress, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell())
|
||||
}
|
||||
|
@ -16,11 +16,8 @@ import com.typesafe.config.ConfigRenderOptions
|
||||
import io.netty.channel.unix.Errors
|
||||
import net.corda.core.cordapp.Cordapp
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.internal.Emoji
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.concurrent.thenMatch
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.randomOrNull
|
||||
import net.corda.core.utilities.Try
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.CmdLineOptions
|
||||
@ -34,8 +31,11 @@ import net.corda.node.services.config.NodeConfigurationImpl
|
||||
import net.corda.node.services.config.shouldStartLocalShell
|
||||
import net.corda.node.services.config.shouldStartSSHDaemon
|
||||
import net.corda.node.services.transactions.bftSMaRtSerialFilter
|
||||
import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate
|
||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||
import net.corda.node.utilities.registration.NodeRegistrationHelper
|
||||
import net.corda.node.utilities.saveToKeyStore
|
||||
import net.corda.node.utilities.saveToTrustStore
|
||||
import net.corda.node.utilities.registration.UnableToRegisterNodeWithDoormanException
|
||||
import net.corda.nodeapi.internal.addShutdownHook
|
||||
import net.corda.nodeapi.internal.config.UnknownConfigurationKeysException
|
||||
@ -47,12 +47,14 @@ import org.fusesource.jansi.Ansi
|
||||
import org.fusesource.jansi.AnsiConsole
|
||||
import org.slf4j.bridge.SLF4JBridgeHandler
|
||||
import sun.misc.VMSupport
|
||||
import java.io.Console
|
||||
import java.io.RandomAccessFile
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.net.InetAddress
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/** This class is responsible for starting a Node from command line arguments. */
|
||||
open class NodeStartup(val args: Array<String>) {
|
||||
@ -188,6 +190,70 @@ open class NodeStartup(val args: Array<String>) {
|
||||
node.generateAndSaveNodeInfo()
|
||||
return
|
||||
}
|
||||
if (cmdlineOptions.justGenerateRpcSslCerts) {
|
||||
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(conf.myLegalName.x500Principal)
|
||||
|
||||
val keyStorePath = conf.baseDirectory / "certificates" / "rpcsslkeystore.jks"
|
||||
val trustStorePath = conf.baseDirectory / "certificates" / "export" / "rpcssltruststore.jks"
|
||||
|
||||
if (keyStorePath.exists() || trustStorePath.exists()) {
|
||||
println("Found existing RPC SSL keystores. Command was already run. Exiting..")
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
val console: Console? = System.console()
|
||||
|
||||
when (console) {
|
||||
// In this case, the JVM is not connected to the console so we need to exit
|
||||
null -> {
|
||||
println("Not connected to console. Exiting")
|
||||
exitProcess(1)
|
||||
}
|
||||
// Otherwise we can proceed normally
|
||||
else -> {
|
||||
while (true) {
|
||||
val keystorePassword1 = console.readPassword("Enter the keystore password => ")
|
||||
val keystorePassword2 = console.readPassword("Re-enter the keystore password => ")
|
||||
if (!keystorePassword1.contentEquals(keystorePassword2)) {
|
||||
println("The keystore passwords don't match.")
|
||||
continue
|
||||
}
|
||||
saveToKeyStore(keyStorePath, keyPair, cert, String(keystorePassword1), "rpcssl")
|
||||
println("The keystore was saved to: $keyStorePath .")
|
||||
break
|
||||
}
|
||||
|
||||
while (true) {
|
||||
val trustStorePassword1 = console.readPassword("Enter the truststore password => ")
|
||||
val trustStorePassword2 = console.readPassword("Re-enter the truststore password => ")
|
||||
if (!trustStorePassword1.contentEquals(trustStorePassword2)) {
|
||||
println("The truststore passwords don't match.")
|
||||
continue
|
||||
}
|
||||
|
||||
saveToTrustStore(trustStorePath, cert, String(trustStorePassword1), "rpcssl")
|
||||
println("The truststore was saved to: $trustStorePath .")
|
||||
println("You need to distribute this file along with the password in a secure way to all RPC clients.")
|
||||
break
|
||||
}
|
||||
|
||||
val dollar = '$'
|
||||
println("""
|
||||
|
|
||||
|The SSL certificates were generated successfully.
|
||||
|
|
||||
|Add this snippet to the "rpcSettings" section of your node.conf:
|
||||
| useSsl=true
|
||||
| ssl {
|
||||
| keyStorePath=$dollar{baseDirectory}/certificates/rpcsslkeystore.jks
|
||||
| keyStorePassword=the_above_password
|
||||
| }
|
||||
|""".trimMargin())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val startedNode = node.start()
|
||||
logLoadedCorDapps(startedNode.services.cordappProvider.cordapps)
|
||||
startedNode.internals.nodeReadyFuture.thenMatch({
|
||||
|
@ -286,7 +286,7 @@ data class NodeConfigurationImpl(
|
||||
|
||||
override val rpcOptions: NodeRpcOptions
|
||||
get() {
|
||||
return actualRpcSettings.asOptions(BrokerRpcSslOptions(baseDirectory / "certificates" / "nodekeystore.jks", keyStorePassword))
|
||||
return actualRpcSettings.asOptions()
|
||||
}
|
||||
|
||||
private fun validateTlsCertCrlConfig(): List<String> {
|
||||
@ -332,10 +332,11 @@ data class NodeConfigurationImpl(
|
||||
|
||||
private fun validateRpcSettings(options: NodeRpcSettings): List<String> {
|
||||
val errors = mutableListOf<String>()
|
||||
if (options.address != null) {
|
||||
if (!options.useSsl && options.adminAddress == null) {
|
||||
errors += "'rpcSettings.adminAddress': missing. Property is mandatory when 'rpcSettings.useSsl' is false (default)."
|
||||
}
|
||||
if (options.adminAddress == null) {
|
||||
errors += "'rpcSettings.adminAddress': missing"
|
||||
}
|
||||
if (options.useSsl && options.ssl == null) {
|
||||
errors += "'rpcSettings.ssl': missing (rpcSettings.useSsl was set to true)."
|
||||
}
|
||||
return errors
|
||||
}
|
||||
@ -434,13 +435,13 @@ data class NodeRpcSettings(
|
||||
val useSsl: Boolean = false,
|
||||
val ssl: BrokerRpcSslOptions?
|
||||
) {
|
||||
fun asOptions(fallbackSslOptions: BrokerRpcSslOptions): NodeRpcOptions {
|
||||
fun asOptions(): NodeRpcOptions {
|
||||
return object : NodeRpcOptions {
|
||||
override val address = this@NodeRpcSettings.address!!
|
||||
override val adminAddress = this@NodeRpcSettings.adminAddress!!
|
||||
override val standAloneBroker = this@NodeRpcSettings.standAloneBroker
|
||||
override val useSsl = this@NodeRpcSettings.useSsl
|
||||
override val sslConfig = this@NodeRpcSettings.ssl ?: fallbackSslOptions
|
||||
override val sslConfig = this@NodeRpcSettings.ssl
|
||||
|
||||
override fun toString(): String {
|
||||
return "address: $address, adminAddress: $adminAddress, standAloneBroker: $standAloneBroker, useSsl: $useSsl, sslConfig: $sslConfig"
|
||||
|
@ -18,5 +18,5 @@ interface NodeRpcOptions {
|
||||
val adminAddress: NetworkHostAndPort
|
||||
val standAloneBroker: Boolean
|
||||
val useSsl: Boolean
|
||||
val sslConfig: BrokerRpcSslOptions
|
||||
val sslConfig: BrokerRpcSslOptions?
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package net.corda.node.utilities
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
import java.nio.file.Path
|
||||
import java.security.KeyPair
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Duration
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
fun createKeyPairAndSelfSignedTLSCertificate(x500Principal: X500Principal): Pair<KeyPair, X509Certificate> {
|
||||
val rpcKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val selfSignCert = createSelfSignedTLSCertificate(x500Principal, rpcKeyPair)
|
||||
return Pair(rpcKeyPair, selfSignCert)
|
||||
}
|
||||
|
||||
fun createSelfSignedTLSCertificate(subject: X500Principal,
|
||||
keyPair: KeyPair,
|
||||
validityWindow: Pair<Duration, Duration> = X509Utilities.DEFAULT_VALIDITY_WINDOW): X509Certificate {
|
||||
val window = X509Utilities.getCertificateValidityWindow(validityWindow.first, validityWindow.second)
|
||||
return X509Utilities.createCertificate(CertificateType.TLS, subject, keyPair, subject, keyPair.public, window)
|
||||
}
|
||||
|
||||
fun saveToKeyStore(keyStorePath: Path, rpcKeyPair: KeyPair, selfSignCert: X509Certificate, password: String = "password", alias: String = "Key"): Path {
|
||||
val keyStore = loadOrCreateKeyStore(keyStorePath, password)
|
||||
keyStore.addOrReplaceKey(alias, rpcKeyPair.private, password.toCharArray(), arrayOf(selfSignCert))
|
||||
keyStore.save(keyStorePath, password)
|
||||
return keyStorePath
|
||||
}
|
||||
|
||||
fun saveToTrustStore(trustStorePath: Path, selfSignCert: X509Certificate, password: String = "password", alias: String = "Key"): Path {
|
||||
val trustStore = loadOrCreateKeyStore(trustStorePath, password)
|
||||
trustStore.addOrReplaceCertificate(alias, selfSignCert)
|
||||
trustStore.save(trustStorePath, password)
|
||||
return trustStorePath
|
||||
}
|
@ -52,6 +52,7 @@ class NodeArgsParserTest {
|
||||
noLocalShell = false,
|
||||
sshdServer = false,
|
||||
justGenerateNodeInfo = false,
|
||||
justGenerateRpcSslCerts = false,
|
||||
bootstrapRaftCluster = false,
|
||||
unknownConfigKeysPolicy = UnknownConfigKeysPolicy.FAIL,
|
||||
devMode = false,
|
||||
|
@ -24,6 +24,9 @@ import net.corda.node.internal.security.RPCSecurityManagerImpl
|
||||
import net.corda.node.services.Permissions.Companion.all
|
||||
import net.corda.node.services.messaging.InternalRPCMessagingClient
|
||||
import net.corda.node.services.messaging.RPCServerConfiguration
|
||||
import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate
|
||||
import net.corda.node.utilities.saveToKeyStore
|
||||
import net.corda.node.utilities.saveToTrustStore
|
||||
import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport
|
||||
import net.corda.nodeapi.BrokerRpcSslOptions
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
@ -31,10 +34,7 @@ import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.driver.internal.RandomFree
|
||||
import net.corda.testing.internal.createKeyPairAndSelfSignedCertificate
|
||||
import net.corda.testing.internal.createNodeSslConfig
|
||||
import net.corda.testing.internal.saveToKeyStore
|
||||
import net.corda.testing.internal.saveToTrustStore
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQConnectionTimedOutException
|
||||
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -43,6 +43,7 @@ import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.nio.file.Path
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
class ArtemisRpcTests {
|
||||
private val ports: PortAllocation = RandomFree
|
||||
@ -59,9 +60,11 @@ class ArtemisRpcTests {
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
|
||||
|
||||
@Test
|
||||
fun rpc_with_ssl_enabled() {
|
||||
val (rpcKeyPair, selfSignCert) = createKeyPairAndSelfSignedCertificate()
|
||||
val (rpcKeyPair, selfSignCert) = createKeyPairAndSelfSignedTLSCertificate(testName)
|
||||
val keyStorePath = saveToKeyStore(tempFile("rpcKeystore.jks"), rpcKeyPair, selfSignCert)
|
||||
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
|
||||
val trustStorePath = saveToTrustStore(tempFile("rpcTruststore.jks"), selfSignCert)
|
||||
@ -76,7 +79,7 @@ class ArtemisRpcTests {
|
||||
|
||||
@Test
|
||||
fun rpc_with_no_ssl_on_client_side_and_ssl_on_server_side() {
|
||||
val (rpcKeyPair, selfSignCert) = createKeyPairAndSelfSignedCertificate()
|
||||
val (rpcKeyPair, selfSignCert) = createKeyPairAndSelfSignedTLSCertificate(testName)
|
||||
val keyStorePath = saveToKeyStore(tempFile("rpcKeystore.jks"), rpcKeyPair, selfSignCert)
|
||||
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
|
||||
// here client sslOptions are passed null (as in, do not use SSL)
|
||||
@ -87,12 +90,12 @@ class ArtemisRpcTests {
|
||||
|
||||
@Test
|
||||
fun rpc_client_certificate_untrusted_to_server() {
|
||||
val (rpcKeyPair, selfSignCert) = createKeyPairAndSelfSignedCertificate()
|
||||
val (rpcKeyPair, selfSignCert) = createKeyPairAndSelfSignedTLSCertificate(testName)
|
||||
val keyStorePath = saveToKeyStore(tempFile("rpcKeystore.jks"), rpcKeyPair, selfSignCert)
|
||||
|
||||
// create another keypair and certificate and add that to the client truststore
|
||||
// the ssl connection should not
|
||||
val (_, selfSignCert1) = createKeyPairAndSelfSignedCertificate()
|
||||
val (_, selfSignCert1) = createKeyPairAndSelfSignedTLSCertificate(testName)
|
||||
val trustStorePath = saveToTrustStore(tempFile("rpcTruststore.jks"), selfSignCert1)
|
||||
|
||||
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
|
||||
|
@ -28,7 +28,6 @@ import net.corda.serialization.internal.amqp.AMQP_ENABLED
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.security.KeyPair
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
@Suppress("unused")
|
||||
@ -150,24 +149,3 @@ fun createNodeSslConfig(path: Path, name: CordaX500Name = CordaX500Name("MegaCor
|
||||
|
||||
return sslConfig
|
||||
}
|
||||
|
||||
fun createKeyPairAndSelfSignedCertificate(): Pair<KeyPair, X509Certificate> {
|
||||
val rpcKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
|
||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, rpcKeyPair)
|
||||
return Pair(rpcKeyPair, selfSignCert)
|
||||
}
|
||||
|
||||
fun saveToKeyStore(keyStorePath: Path, rpcKeyPair: KeyPair, selfSignCert: X509Certificate, password: String = "password"): Path {
|
||||
val keyStore = loadOrCreateKeyStore(keyStorePath, password)
|
||||
keyStore.addOrReplaceKey("Key", rpcKeyPair.private, password.toCharArray(), arrayOf(selfSignCert))
|
||||
keyStore.save(keyStorePath, password)
|
||||
return keyStorePath
|
||||
}
|
||||
|
||||
fun saveToTrustStore(trustStorePath: Path, selfSignCert: X509Certificate, password: String = "password"): Path {
|
||||
val trustStore = loadOrCreateKeyStore(trustStorePath, password)
|
||||
trustStore.addOrReplaceCertificate("Key", selfSignCert)
|
||||
trustStore.save(trustStorePath, password)
|
||||
return trustStorePath
|
||||
}
|
@ -22,6 +22,9 @@ import net.corda.node.services.Permissions.Companion.all
|
||||
import net.corda.node.services.config.shell.toShellConfig
|
||||
import net.corda.nodeapi.BrokerRpcSslOptions
|
||||
import net.corda.core.messaging.ClientRpcSslOptions
|
||||
import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate
|
||||
import net.corda.node.utilities.saveToKeyStore
|
||||
import net.corda.node.utilities.saveToTrustStore
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.DUMMY_BANK_A_NAME
|
||||
@ -33,9 +36,6 @@ import net.corda.testing.driver.internal.RandomFree
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.internal.createKeyPairAndSelfSignedCertificate
|
||||
import net.corda.testing.internal.saveToKeyStore
|
||||
import net.corda.testing.internal.saveToTrustStore
|
||||
import net.corda.testing.internal.useSslRpcOverrides
|
||||
import net.corda.testing.node.User
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
||||
@ -47,6 +47,7 @@ import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class InteractiveShellIntegrationTest : IntegrationTest() {
|
||||
@ -61,6 +62,8 @@ class InteractiveShellIntegrationTest : IntegrationTest() {
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
|
||||
|
||||
@Test
|
||||
fun `shell should not log in with invalid credentials`() {
|
||||
val user = User("u", "p", setOf())
|
||||
@ -98,7 +101,7 @@ class InteractiveShellIntegrationTest : IntegrationTest() {
|
||||
val user = User("mark", "dadada", setOf(all()))
|
||||
var successful = false
|
||||
|
||||
val (keyPair, cert) = createKeyPairAndSelfSignedCertificate()
|
||||
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(testName)
|
||||
val keyStorePath = saveToKeyStore(tempFolder.root.toPath() / "keystore.jks", keyPair, cert)
|
||||
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
|
||||
|
||||
@ -125,11 +128,11 @@ class InteractiveShellIntegrationTest : IntegrationTest() {
|
||||
@Test
|
||||
fun `shell shoud not log in with invalid truststore`() {
|
||||
val user = User("mark", "dadada", setOf("ALL"))
|
||||
val (keyPair, cert) = createKeyPairAndSelfSignedCertificate()
|
||||
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(testName)
|
||||
val keyStorePath = saveToKeyStore(tempFolder.root.toPath() / "keystore.jks", keyPair, cert)
|
||||
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
|
||||
|
||||
val (_, cert1) = createKeyPairAndSelfSignedCertificate()
|
||||
val (_, cert1) = createKeyPairAndSelfSignedTLSCertificate(testName)
|
||||
val trustStorePath = saveToTrustStore(tempFolder.root.toPath() / "truststore.jks", cert1)
|
||||
val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password")
|
||||
|
||||
@ -209,7 +212,7 @@ class InteractiveShellIntegrationTest : IntegrationTest() {
|
||||
Permissions.invokeRpc(CordaRPCOps::registeredFlows),
|
||||
Permissions.invokeRpc(CordaRPCOps::nodeInfo)/*all()*/))
|
||||
|
||||
val (keyPair, cert) = createKeyPairAndSelfSignedCertificate()
|
||||
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(testName)
|
||||
val keyStorePath = saveToKeyStore(tempFolder.root.toPath() / "keystore.jks", keyPair, cert)
|
||||
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
|
||||
val trustStorePath = saveToTrustStore(tempFolder.root.toPath() / "truststore.jks", cert)
|
||||
|
Loading…
x
Reference in New Issue
Block a user