Add cmdline option for network root truststore and password (#2407)

* add cmdline option for network root truststore and password, instead of using node's truststore configuration to avoid confusion.

* revert line auto format

* fix failing integration test

* address PR issue
This commit is contained in:
Patrick Kuo 2018-01-29 13:43:16 +00:00 committed by GitHub
parent 4851d9ca6a
commit 93054a9590
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 90 additions and 66 deletions

View File

@ -1,6 +1,5 @@
package net.corda.node.utilities.registration package net.corda.node.utilities.registration
import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.concurrent.transpose import net.corda.core.internal.concurrent.transpose
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
@ -26,7 +25,6 @@ import net.corda.testing.node.internal.CompatibilityZoneParams
import net.corda.testing.node.internal.internalDriver import net.corda.testing.node.internal.internalDriver
import net.corda.testing.node.internal.network.NetworkMapServer import net.corda.testing.node.internal.network.NetworkMapServer
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.junit.After import org.junit.After
@ -38,12 +36,10 @@ import java.io.InputStream
import java.net.URL import java.net.URL
import java.security.KeyPair import java.security.KeyPair
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.CertPathValidatorException
import java.security.cert.Certificate import java.security.cert.Certificate
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
import javax.security.auth.x500.X500Principal
import javax.ws.rs.* import javax.ws.rs.*
import javax.ws.rs.core.MediaType import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response import javax.ws.rs.core.Response
@ -116,28 +112,6 @@ class NodeRegistrationTest {
).returnValue.getOrThrow() ).returnValue.getOrThrow()
} }
} }
@Test
fun `node registration wrong root cert`() {
val someRootCert = X509Utilities.createSelfSignedCACertificate(
X500Principal("CN=Integration Test Corda Node Root CA,O=R3 Ltd,L=London,C=GB"),
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
val compatibilityZone = CompatibilityZoneParams(
URL("http://$serverHostAndPort"),
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
rootCert = someRootCert)
internalDriver(
portAllocation = portAllocation,
compatibilityZone = compatibilityZone,
initialiseSerialization = false,
notarySpecs = listOf(NotarySpec(notaryName)),
startNodesInProcess = true // We need to run the nodes in the same process so that we can capture the correct exception
) {
assertThatThrownBy {
defaultNotaryNode.getOrThrow()
}.isInstanceOf(CertPathValidatorException::class.java)
}
}
} }
@Path("certificate") @Path("certificate")

View File

@ -1,6 +1,5 @@
package net.corda.node package net.corda.node
import com.typesafe.config.ConfigException
import joptsimple.OptionParser import joptsimple.OptionParser
import joptsimple.util.EnumConverter import joptsimple.util.EnumConverter
import net.corda.core.internal.div import net.corda.core.internal.div
@ -34,6 +33,10 @@ class ArgsParser {
private val sshdServerArg = optionParser.accepts("sshd", "Enables SSHD server for node administration.") private val sshdServerArg = optionParser.accepts("sshd", "Enables SSHD server for node administration.")
private val noLocalShellArg = optionParser.accepts("no-local-shell", "Do not start the embedded shell locally.") private val noLocalShellArg = optionParser.accepts("no-local-shell", "Do not start the embedded shell locally.")
private val isRegistrationArg = optionParser.accepts("initial-registration", "Start initial node registration with Corda network to obtain certificate from the permissioning server.") private val isRegistrationArg = optionParser.accepts("initial-registration", "Start initial node registration with Corda network to obtain certificate from the permissioning server.")
private val networkRootTruststorePathArg = optionParser.accepts("network-root-truststore", "Network root trust store obtained from network operator.")
.withRequiredArg()
private val networkRootTruststorePasswordArg = optionParser.accepts("network-root-truststore-password", "Network root trust store password obtained from network operator.")
.withRequiredArg()
private val isVersionArg = optionParser.accepts("version", "Print the version and exit") private val isVersionArg = optionParser.accepts("version", "Print the version and exit")
private val justGenerateNodeInfoArg = optionParser.accepts("just-generate-node-info", 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") "Perform the node start-up task necessary to generate its nodeInfo, save it to disk, then quit")
@ -56,8 +59,21 @@ class ArgsParser {
val sshdServer = optionSet.has(sshdServerArg) val sshdServer = optionSet.has(sshdServerArg)
val justGenerateNodeInfo = optionSet.has(justGenerateNodeInfoArg) val justGenerateNodeInfo = optionSet.has(justGenerateNodeInfoArg)
val bootstrapRaftCluster = optionSet.has(bootstrapRaftClusterArg) val bootstrapRaftCluster = optionSet.has(bootstrapRaftClusterArg)
return CmdLineOptions(baseDirectory, configFile, help, loggingLevel, logToConsole, isRegistration, isVersion, val networkRootTruststorePath = optionSet.valueOf(networkRootTruststorePathArg)?.let { Paths.get(it).normalize().toAbsolutePath() }
noLocalShell, sshdServer, justGenerateNodeInfo, bootstrapRaftCluster) val networkRootTruststorePassword = optionSet.valueOf(networkRootTruststorePasswordArg)
return CmdLineOptions(baseDirectory,
configFile,
help,
loggingLevel,
logToConsole,
isRegistration,
networkRootTruststorePath,
networkRootTruststorePassword,
isVersion,
noLocalShell,
sshdServer,
justGenerateNodeInfo,
bootstrapRaftCluster)
} }
fun printHelp(sink: PrintStream) = optionParser.printHelpOn(sink) fun printHelp(sink: PrintStream) = optionParser.printHelpOn(sink)
@ -69,6 +85,8 @@ data class CmdLineOptions(val baseDirectory: Path,
val loggingLevel: Level, val loggingLevel: Level,
val logToConsole: Boolean, val logToConsole: Boolean,
val isRegistration: Boolean, val isRegistration: Boolean,
val networkRootTruststorePath: Path?,
val networkRootTruststorePassword: String?,
val isVersion: Boolean, val isVersion: Boolean,
val noLocalShell: Boolean, val noLocalShell: Boolean,
val sshdServer: Boolean, val sshdServer: Boolean,
@ -78,6 +96,8 @@ data class CmdLineOptions(val baseDirectory: Path,
val config = ConfigHelper.loadConfig(baseDirectory, configFile).parseAsNodeConfiguration() val config = ConfigHelper.loadConfig(baseDirectory, configFile).parseAsNodeConfiguration()
if (isRegistration) { if (isRegistration) {
requireNotNull(config.compatibilityZoneURL) { "Compatibility Zone Url must be provided in registration mode." } requireNotNull(config.compatibilityZoneURL) { "Compatibility Zone Url must be provided in registration mode." }
requireNotNull(networkRootTruststorePath) { "Network root trust store path must be provided in registration mode." }
requireNotNull(networkRootTruststorePassword) { "Network root trust store password must be provided in registration mode." }
} }
return config return config
} }

View File

@ -2,8 +2,11 @@ package net.corda.node.internal
import com.jcabi.manifests.Manifests import com.jcabi.manifests.Manifests
import joptsimple.OptionException import joptsimple.OptionException
import net.corda.core.internal.* import net.corda.core.internal.Emoji
import net.corda.core.internal.concurrent.thenMatch 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.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.* import net.corda.node.*
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
@ -91,7 +94,8 @@ open class NodeStartup(val args: Array<String>) {
banJavaSerialisation(conf) banJavaSerialisation(conf)
preNetworkRegistration(conf) preNetworkRegistration(conf)
if (shouldRegisterWithNetwork(cmdlineOptions, conf)) { if (shouldRegisterWithNetwork(cmdlineOptions, conf)) {
registerWithNetwork(cmdlineOptions, conf) // Null checks for [compatibilityZoneURL], [rootTruststorePath] and [rootTruststorePassword] has been done in [CmdLineOptions.loadConfig]
registerWithNetwork(conf, cmdlineOptions.networkRootTruststorePath!!, cmdlineOptions.networkRootTruststorePassword!!)
return true return true
} }
logStartupInfo(versionInfo, cmdlineOptions, conf) logStartupInfo(versionInfo, cmdlineOptions, conf)
@ -179,7 +183,7 @@ open class NodeStartup(val args: Array<String>) {
return !(!cmdlineOptions.isRegistration || compatibilityZoneURL == null) return !(!cmdlineOptions.isRegistration || compatibilityZoneURL == null)
} }
open protected fun registerWithNetwork(cmdlineOptions: CmdLineOptions, conf: NodeConfiguration) { open protected fun registerWithNetwork(conf: NodeConfiguration, networkRootTruststorePath: Path, networkRootTruststorePassword: String) {
val compatibilityZoneURL = conf.compatibilityZoneURL!! val compatibilityZoneURL = conf.compatibilityZoneURL!!
println() println()
println("******************************************************************") println("******************************************************************")
@ -187,7 +191,7 @@ open class NodeStartup(val args: Array<String>) {
println("* Registering as a new participant with Corda network *") println("* Registering as a new participant with Corda network *")
println("* *") println("* *")
println("******************************************************************") println("******************************************************************")
NetworkRegistrationHelper(conf, HTTPNetworkRegistrationService(compatibilityZoneURL)).buildKeystore() NetworkRegistrationHelper(conf, HTTPNetworkRegistrationService(compatibilityZoneURL), networkRootTruststorePath, networkRootTruststorePassword).buildKeystore()
} }
open protected fun loadConfigFile(cmdlineOptions: CmdLineOptions): NodeConfiguration = cmdlineOptions.loadConfig() open protected fun loadConfigFile(cmdlineOptions: CmdLineOptions): NodeConfiguration = cmdlineOptions.loadConfig()

View File

@ -6,14 +6,15 @@ import net.corda.core.internal.*
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
import net.corda.nodeapi.internal.crypto.x509
import org.bouncycastle.openssl.jcajce.JcaPEMWriter import org.bouncycastle.openssl.jcajce.JcaPEMWriter
import org.bouncycastle.util.io.pem.PemObject import org.bouncycastle.util.io.pem.PemObject
import java.io.StringWriter import java.io.StringWriter
import java.nio.file.Path
import java.security.KeyPair import java.security.KeyPair
import java.security.KeyStore import java.security.KeyStore
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
@ -22,7 +23,10 @@ import java.security.cert.X509Certificate
* Helper for managing the node registration process, which checks for any existing certificates and requests them if * Helper for managing the node registration process, which checks for any existing certificates and requests them if
* needed. * needed.
*/ */
class NetworkRegistrationHelper(private val config: NodeConfiguration, private val certService: NetworkRegistrationService) { class NetworkRegistrationHelper(private val config: NodeConfiguration,
private val certService: NetworkRegistrationService,
networkRootTrustStorePath: Path,
networkRootTruststorePassword: String) {
private companion object { private companion object {
val pollInterval = 10.seconds val pollInterval = 10.seconds
const val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key" const val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key"
@ -31,20 +35,16 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt" private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt"
// TODO: Use different password for private key. // TODO: Use different password for private key.
private val privateKeyPassword = config.keyStorePassword private val privateKeyPassword = config.keyStorePassword
private val rootTrustStore: X509KeyStore
private val rootCert: X509Certificate private val rootCert: X509Certificate
init { init {
require(config.trustStoreFile.exists()) { require(networkRootTrustStorePath.exists()) {
"${config.trustStoreFile} does not exist. This file must contain the root CA cert of your compatibility zone. " + "$networkRootTrustStorePath does not exist. This file must contain the root CA cert of your compatibility zone. " +
"Please contact your CZ operator." "Please contact your CZ operator."
} }
val rootCert = config.loadTrustStore().internal.getCertificate(CORDA_ROOT_CA) rootTrustStore = X509KeyStore.fromFile(networkRootTrustStorePath, networkRootTruststorePassword)
require(rootCert != null) { rootCert = rootTrustStore.getCertificate(CORDA_ROOT_CA)
"${config.trustStoreFile} does not contain a certificate with the key $CORDA_ROOT_CA." +
"This file must contain the root CA cert of your compatibility zone. " +
"Please contact your CZ operator."
}
this.rootCert = rootCert.x509
} }
/** /**
@ -109,7 +109,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole") throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole")
} }
println("Checking root of the certificate path is what we expect.") // Validate certificate chain returned from the doorman with the root cert obtained via out-of-band process, to prevent MITM attack on doorman server.
X509Utilities.validateCertificateChain(rootCert, certificates) X509Utilities.validateCertificateChain(rootCert, certificates)
println("Certificate signing request approved, storing private key with the certificate chain.") println("Certificate signing request approved, storing private key with the certificate chain.")
@ -119,6 +119,14 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
nodeKeyStore.save() nodeKeyStore.save()
println("Node private key and certificate stored in ${config.nodeKeystore}.") println("Node private key and certificate stored in ${config.nodeKeystore}.")
// Save root certificates to trust store.
config.loadTrustStore(createNew = true).update {
println("Generating trust store for corda node.")
// Assumes certificate chain always starts with client certificate and end with root certificate.
setCertificate(CORDA_ROOT_CA, certificates.last())
}
println("Node trust store stored in ${config.trustStoreFile}.")
config.loadSslKeyStore(createNew = true).update { config.loadSslKeyStore(createNew = true).update {
println("Generating SSL certificate for node messaging service.") println("Generating SSL certificate for node messaging service.")
val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)

View File

@ -7,6 +7,7 @@ import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.Test import org.junit.Test
import org.slf4j.event.Level import org.slf4j.event.Level
import java.nio.file.Paths import java.nio.file.Paths
import kotlin.test.assertEquals
class ArgsParserTest { class ArgsParserTest {
private val parser = ArgsParser() private val parser = ArgsParser()
@ -25,7 +26,9 @@ class ArgsParserTest {
noLocalShell = false, noLocalShell = false,
sshdServer = false, sshdServer = false,
justGenerateNodeInfo = false, justGenerateNodeInfo = false,
bootstrapRaftCluster = false)) bootstrapRaftCluster = false,
networkRootTruststorePassword = null,
networkRootTruststorePath = null))
} }
@Test @Test
@ -110,8 +113,11 @@ class ArgsParserTest {
@Test @Test
fun `initial-registration`() { fun `initial-registration`() {
val cmdLineOptions = parser.parse("--initial-registration") val cmdLineOptions = parser.parse("--initial-registration", "--network-root-truststore", "/truststore/file.jks", "--network-root-truststore-password", "password-test")
assertThat(cmdLineOptions.isRegistration).isTrue() assertThat(cmdLineOptions.isRegistration).isTrue()
assertEquals(Paths.get("/truststore/file.jks"), cmdLineOptions.networkRootTruststorePath)
assertEquals("password-test", cmdLineOptions.networkRootTruststorePassword)
} }
@Test @Test

View File

@ -10,9 +10,11 @@ import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.internal.x500Name import net.corda.core.internal.x500Name
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
@ -35,10 +37,13 @@ class NetworkRegistrationHelperTest {
private val nodeLegalName = ALICE_NAME private val nodeLegalName = ALICE_NAME
private lateinit var config: NodeConfiguration private lateinit var config: NodeConfiguration
private val networkRootTrustStoreFileName = "network-root-truststore.jks"
private val networkRootTrustStorePassword = "network-root-truststore-password"
@Before @Before
fun init() { fun init() {
val baseDirectory = fs.getPath("/baseDir").createDirectories() val baseDirectory = fs.getPath("/baseDir").createDirectories()
abstract class AbstractNodeConfiguration : NodeConfiguration abstract class AbstractNodeConfiguration : NodeConfiguration
config = rigorousMock<AbstractNodeConfiguration>().also { config = rigorousMock<AbstractNodeConfiguration>().also {
doReturn(baseDirectory).whenever(it).baseDirectory doReturn(baseDirectory).whenever(it).baseDirectory
@ -62,7 +67,7 @@ class NetworkRegistrationHelperTest {
val nodeCaCertPath = createNodeCaCertPath() val nodeCaCertPath = createNodeCaCertPath()
saveTrustStoreWithRootCa(nodeCaCertPath.last()) saveNetworkTrustStore(nodeCaCertPath.last())
createRegistrationHelper(nodeCaCertPath).buildKeystore() createRegistrationHelper(nodeCaCertPath).buildKeystore()
val nodeKeystore = config.loadNodeKeyStore() val nodeKeystore = config.loadNodeKeyStore()
@ -105,7 +110,7 @@ class NetworkRegistrationHelperTest {
@Test @Test
fun `node CA with incorrect cert role`() { fun `node CA with incorrect cert role`() {
val nodeCaCertPath = createNodeCaCertPath(type = CertificateType.TLS) val nodeCaCertPath = createNodeCaCertPath(type = CertificateType.TLS)
saveTrustStoreWithRootCa(nodeCaCertPath.last()) saveNetworkTrustStore(nodeCaCertPath.last())
val registrationHelper = createRegistrationHelper(nodeCaCertPath) val registrationHelper = createRegistrationHelper(nodeCaCertPath)
assertThatExceptionOfType(CertificateRequestException::class.java) assertThatExceptionOfType(CertificateRequestException::class.java)
.isThrownBy { registrationHelper.buildKeystore() } .isThrownBy { registrationHelper.buildKeystore() }
@ -116,7 +121,7 @@ class NetworkRegistrationHelperTest {
fun `node CA with incorrect subject`() { fun `node CA with incorrect subject`() {
val invalidName = CordaX500Name("Foo", "MU", "GB") val invalidName = CordaX500Name("Foo", "MU", "GB")
val nodeCaCertPath = createNodeCaCertPath(legalName = invalidName) val nodeCaCertPath = createNodeCaCertPath(legalName = invalidName)
saveTrustStoreWithRootCa(nodeCaCertPath.last()) saveNetworkTrustStore(nodeCaCertPath.last())
val registrationHelper = createRegistrationHelper(nodeCaCertPath) val registrationHelper = createRegistrationHelper(nodeCaCertPath)
assertThatExceptionOfType(CertificateRequestException::class.java) assertThatExceptionOfType(CertificateRequestException::class.java)
.isThrownBy { registrationHelper.buildKeystore() } .isThrownBy { registrationHelper.buildKeystore() }
@ -128,7 +133,7 @@ class NetworkRegistrationHelperTest {
val wrongRootCert = X509Utilities.createSelfSignedCACertificate( val wrongRootCert = X509Utilities.createSelfSignedCACertificate(
X500Principal("O=Foo,L=MU,C=GB"), X500Principal("O=Foo,L=MU,C=GB"),
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
saveTrustStoreWithRootCa(wrongRootCert) saveNetworkTrustStore(wrongRootCert)
val registrationHelper = createRegistrationHelper(createNodeCaCertPath()) val registrationHelper = createRegistrationHelper(createNodeCaCertPath())
assertThatThrownBy { assertThatThrownBy {
registrationHelper.buildKeystore() registrationHelper.buildKeystore()
@ -155,12 +160,13 @@ class NetworkRegistrationHelperTest {
doReturn(requestId).whenever(it).submitRequest(any()) doReturn(requestId).whenever(it).submitRequest(any())
doReturn(response).whenever(it).retrieveCertificates(eq(requestId)) doReturn(response).whenever(it).retrieveCertificates(eq(requestId))
} }
return NetworkRegistrationHelper(config, certService) return NetworkRegistrationHelper(config, certService, config.certificatesDirectory / networkRootTrustStoreFileName, networkRootTrustStorePassword)
} }
private fun saveTrustStoreWithRootCa(rootCert: X509Certificate) { private fun saveNetworkTrustStore(rootCert: X509Certificate) {
config.certificatesDirectory.createDirectories() config.certificatesDirectory.createDirectories()
config.loadTrustStore(createNew = true).update { val rootTruststorePath = config.certificatesDirectory / networkRootTrustStoreFileName
X509KeyStore.fromFile(rootTruststorePath, networkRootTrustStorePassword, createNew = true).update {
setCertificate(X509Utilities.CORDA_ROOT_CA, rootCert) setCertificate(X509Utilities.CORDA_ROOT_CA, rootCert)
} }
} }

View File

@ -33,6 +33,7 @@ import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.addShutdownHook
import net.corda.nodeapi.internal.config.parseAs import net.corda.nodeapi.internal.config.parseAs
import net.corda.nodeapi.internal.config.toConfig import net.corda.nodeapi.internal.config.toConfig
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NetworkParametersCopier
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
@ -237,17 +238,24 @@ class DriverDSLImpl(
)) ))
config.corda.certificatesDirectory.createDirectories() config.corda.certificatesDirectory.createDirectories()
config.corda.loadTrustStore(createNew = true).update { // Create network root truststore.
val rootTruststorePath = config.corda.certificatesDirectory / "network-root-truststore.jks"
// The network truststore will be provided by the network operator via out-of-band communication.
val rootTruststorePassword = "corda-root-password"
X509KeyStore.fromFile(rootTruststorePath, rootTruststorePassword, createNew = true).update {
setCertificate(X509Utilities.CORDA_ROOT_CA, rootCert) setCertificate(X509Utilities.CORDA_ROOT_CA, rootCert)
} }
return if (startNodesInProcess) { return if (startNodesInProcess) {
executorService.fork { executorService.fork {
NetworkRegistrationHelper(config.corda, HTTPNetworkRegistrationService(compatibilityZoneURL)).buildKeystore() NetworkRegistrationHelper(config.corda, HTTPNetworkRegistrationService(compatibilityZoneURL), rootTruststorePath, rootTruststorePassword).buildKeystore()
config config
} }
} else { } else {
startOutOfProcessMiniNode(config, "--initial-registration").map { config } startOutOfProcessMiniNode(config,
"--initial-registration",
"--network-root-truststore=${rootTruststorePath.toAbsolutePath()}",
"--network-root-truststore-password=$rootTruststorePassword").map { config }
} }
} }
@ -479,8 +487,8 @@ class DriverDSLImpl(
when (it.cluster) { when (it.cluster) {
null -> startSingleNotary(it, localNetworkMap) null -> startSingleNotary(it, localNetworkMap)
is ClusterSpec.Raft, is ClusterSpec.Raft,
// DummyCluster is used for testing the notary communication path, and it does not matter // DummyCluster is used for testing the notary communication path, and it does not matter
// which underlying consensus algorithm is used, so we just stick to Raft // which underlying consensus algorithm is used, so we just stick to Raft
is DummyClusterSpec -> startRaftNotaryCluster(it, localNetworkMap) is DummyClusterSpec -> startRaftNotaryCluster(it, localNetworkMap)
else -> throw IllegalArgumentException("BFT-SMaRt not supported") else -> throw IllegalArgumentException("BFT-SMaRt not supported")
} }
@ -596,7 +604,7 @@ class DriverDSLImpl(
* Start the node with the given flag which is expected to start the node for some function, which once complete will * Start the node with the given flag which is expected to start the node for some function, which once complete will
* terminate the node. * terminate the node.
*/ */
private fun startOutOfProcessMiniNode(config: NodeConfig, extraCmdLineFlag: String): CordaFuture<Unit> { private fun startOutOfProcessMiniNode(config: NodeConfig, vararg extraCmdLineFlag: String): CordaFuture<Unit> {
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
val monitorPort = if (jmxPolicy.startJmxHttpServer) jmxPolicy.jmxHttpServerPortAllocation?.nextPort() else null val monitorPort = if (jmxPolicy.startJmxHttpServer) jmxPolicy.jmxHttpServerPortAllocation?.nextPort() else null
val process = startOutOfProcessNode( val process = startOutOfProcessNode(
@ -608,7 +616,7 @@ class DriverDSLImpl(
systemProperties, systemProperties,
cordappPackages, cordappPackages,
"200m", "200m",
extraCmdLineFlag *extraCmdLineFlag
) )
return poll(executorService, "$extraCmdLineFlag (${config.corda.myLegalName})") { return poll(executorService, "$extraCmdLineFlag (${config.corda.myLegalName})") {
@ -652,7 +660,7 @@ class DriverDSLImpl(
} else { } else {
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
val monitorPort = if (jmxPolicy.startJmxHttpServer) jmxPolicy.jmxHttpServerPortAllocation?.nextPort() else null val monitorPort = if (jmxPolicy.startJmxHttpServer) jmxPolicy.jmxHttpServerPortAllocation?.nextPort() else null
val process = startOutOfProcessNode(config, quasarJarPath, debugPort, jolokiaJarPath, monitorPort, systemProperties, cordappPackages, maximumHeapSize, null) val process = startOutOfProcessNode(config, quasarJarPath, debugPort, jolokiaJarPath, monitorPort, systemProperties, cordappPackages, maximumHeapSize)
if (waitForNodesToFinish) { if (waitForNodesToFinish) {
state.locked { state.locked {
processes += process processes += process
@ -763,7 +771,7 @@ class DriverDSLImpl(
overriddenSystemProperties: Map<String, String>, overriddenSystemProperties: Map<String, String>,
cordappPackages: List<String>, cordappPackages: List<String>,
maximumHeapSize: String, maximumHeapSize: String,
extraCmdLineFlag: String? vararg extraCmdLineFlag: String
): Process { ): Process {
log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, " + log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, " +
"debug port is " + (debugPort ?: "not enabled") + ", " + "debug port is " + (debugPort ?: "not enabled") + ", " +
@ -801,9 +809,7 @@ class DriverDSLImpl(
"--base-directory=${config.corda.baseDirectory}", "--base-directory=${config.corda.baseDirectory}",
"--logging-level=$loggingLevel", "--logging-level=$loggingLevel",
"--no-local-shell").also { "--no-local-shell").also {
if (extraCmdLineFlag != null) { it += extraCmdLineFlag
it += extraCmdLineFlag
}
}.toList() }.toList()
return ProcessUtilities.startCordaProcess( return ProcessUtilities.startCordaProcess(