mirror of
https://github.com/corda/corda.git
synced 2024-12-26 16:11:12 +00:00
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:
parent
4851d9ca6a
commit
93054a9590
@ -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")
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
Loading…
Reference in New Issue
Block a user