Skip to content

Commit

Permalink
CORDA-2113 - Include PNM ID in CSR (#4086)
Browse files Browse the repository at this point in the history
* CORDA-2113 - Include PNM ID in CSR

If Compatibility Zone operator is using private networks and the node
should be joining one, optionally the ID (a UUID) of that network can be
included as part of the node's CSR to to the Doorman.

* fix broken test
  • Loading branch information
Katelyn Baker committed Oct 18, 2018
1 parent 8af4044 commit 7cfd44e
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/source/corda-configuration-file.rst
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ absolute path to the node's base directory.

:doormanURL: Root address of the network registration service.
:networkMapURL: Root address of the network map service.
:pnm: Optional UUID of the private network operating within the compatibility zone this node should be joinging.

.. note:: Only one of ``compatibilityZoneURL`` or ``networkServices`` should be used.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,22 @@ class NetworkMapTest(var initFunc: (URL, NetworkMapServer) -> CompatibilityZoneP
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun runParams() = listOf(
{ addr: URL, nms: NetworkMapServer ->
SharedCompatibilityZoneParams(
{
addr: URL,
nms: NetworkMapServer -> SharedCompatibilityZoneParams(
addr,
pnm = null,
publishNotaries = {
nms.networkParameters = testNetworkParameters(it, modifiedTime = Instant.ofEpochMilli(random63BitValue()), epoch = 2)
}
)
},
{ addr: URL, nms: NetworkMapServer ->
SplitCompatibilityZoneParams(
{
addr: URL,
nms: NetworkMapServer -> SplitCompatibilityZoneParams (
doormanURL = URL("http://I/Don't/Exist"),
networkMapURL = addr,
pnm = null,
publishNotaries = {
nms.networkParameters = testNetworkParameters(it, modifiedTime = Instant.ofEpochMilli(random63BitValue()), epoch = 2)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class NodeRegistrationTest {
fun `node registration correct root cert`() {
val compatibilityZone = SharedCompatibilityZoneParams(
URL("http://$serverHostAndPort"),
null,
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
rootCert = DEV_ROOT_CA.certificate)
internalDriver(
Expand Down
30 changes: 18 additions & 12 deletions node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ open class NodeStartup : CordaCliWrapper("corda", "Runs a Corda Node") {

private val handleRegistrationError = { error: Exception ->
when (error) {
is NodeRegistrationException -> error.logAsExpected("Node registration service is unavailable. Perhaps try to perform the initial registration again after a while.")
is NodeRegistrationException -> error.logAsExpected("Issue with Node registration: ${error.message}")
else -> error.logAsUnexpected("Exception during node registration")
}
}
Expand Down Expand Up @@ -385,17 +385,23 @@ open class NodeStartup : CordaCliWrapper("corda", "Runs a Corda Node") {
logger.info(nodeStartedMessage)
}

protected open fun registerWithNetwork(conf: NodeConfiguration, versionInfo: VersionInfo, nodeRegistrationConfig: NodeRegistrationOption) {
val compatibilityZoneURL = conf.networkServices?.doormanURL ?: throw RuntimeException(
"compatibilityZoneURL or networkServices must be configured!")

println()
println("******************************************************************")
println("* *")
println("* Registering as a new participant with Corda network *")
println("* *")
println("******************************************************************")
NodeRegistrationHelper(conf, HTTPNetworkRegistrationService(compatibilityZoneURL, versionInfo), nodeRegistrationConfig).buildKeystore()
protected open fun registerWithNetwork(
conf: NodeConfiguration,
versionInfo: VersionInfo,
nodeRegistrationConfig: NodeRegistrationOption
) {
println("\n" +
"******************************************************************\n" +
"* *\n" +
"* Registering as a new participant with a Corda network *\n" +
"* *\n" +
"******************************************************************\n")

NodeRegistrationHelper(conf,
HTTPNetworkRegistrationService(
requireNotNull(conf.networkServices),
versionInfo),
nodeRegistrationConfig).buildKeystore()

// Minimal changes to make registration tool create node identity.
// TODO: Move node identity generation logic from node to registration helper.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ data class BFTSMaRtConfiguration(
*
* @property doormanURL The URL of the tls certificate signing service.
* @property networkMapURL The URL of the Network Map service.
* @property pnm If the compatibility zone operator supports the private network map option, have the node
* at registration automatically join that private network.
* @property inferred Non user setting that indicates weather the Network Services configuration was
* set explicitly ([inferred] == false) or weather they have been inferred via the compatibilityZoneURL parameter
* ([inferred] == true) where both the network map and doorman are running on the same endpoint. Only one,
Expand All @@ -164,6 +166,7 @@ data class BFTSMaRtConfiguration(
data class NetworkServicesConfig(
val doormanURL: URL,
val networkMapURL: URL,
val pnm: UUID? = null,
val inferred : Boolean = false
)

Expand Down Expand Up @@ -371,8 +374,9 @@ data class NodeConfigurationImpl(
""".trimMargin())
}

// Support the deprecated method of configuring network services with a single compatibilityZoneURL option
if (compatibilityZoneURL != null && networkServices == null) {
networkServices = NetworkServicesConfig(compatibilityZoneURL, compatibilityZoneURL, true)
networkServices = NetworkServicesConfig(compatibilityZoneURL, compatibilityZoneURL, inferred = true)
}
require(h2port == null || h2Settings == null) { "Cannot specify both 'h2port' and 'h2Settings' in configuration" }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import net.corda.core.internal.post
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.seconds
import net.corda.node.VersionInfo
import net.corda.node.services.config.NetworkServicesConfig
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import okhttp3.CacheControl
import okhttp3.Headers
Expand All @@ -19,8 +20,11 @@ import java.util.*
import java.util.zip.ZipInputStream
import javax.naming.ServiceUnavailableException

class HTTPNetworkRegistrationService(compatibilityZoneURL: URL, val versionInfo: VersionInfo) : NetworkRegistrationService {
private val registrationURL = URL("$compatibilityZoneURL/certificate")
class HTTPNetworkRegistrationService(
val config : NetworkServicesConfig,
val versionInfo: VersionInfo
) : NetworkRegistrationService {
private val registrationURL = URL("${config.doormanURL}/certificate")

companion object {
private val TRANSIENT_ERROR_STATUS_CODES = setOf(HTTP_BAD_GATEWAY, HTTP_UNAVAILABLE, HTTP_GATEWAY_TIMEOUT)
Expand Down Expand Up @@ -54,7 +58,8 @@ class HTTPNetworkRegistrationService(compatibilityZoneURL: URL, val versionInfo:
override fun submitRequest(request: PKCS10CertificationRequest): String {
return String(registrationURL.post(OpaqueBytes(request.encoded),
"Platform-Version" to "${versionInfo.platformVersion}",
"Client-Version" to versionInfo.releaseVersion))
"Client-Version" to versionInfo.releaseVersion,
"Private-Network-Map" to (config.pnm?.toString() ?: "")))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,9 @@ open class NetworkRegistrationHelper(private val certificatesDirectory: Path,
val requestId = try {
submitOrResumeCertificateSigningRequest(keyPair)
} catch (e: Exception) {
if (e is ConnectException || e is ServiceUnavailableException || e is IOException) {
throw NodeRegistrationException(e)
}
throw e
throw if (e is ConnectException || e is ServiceUnavailableException || e is IOException) {
NodeRegistrationException(e.message, e)
} else e
}

val certificates = try {
Expand Down Expand Up @@ -200,7 +199,8 @@ open class NetworkRegistrationHelper(private val certificatesDirectory: Path,
if (idlePeriodDuration != null) {
Thread.sleep(idlePeriodDuration.toMillis())
} else {
throw NodeRegistrationException(e)
throw NodeRegistrationException("Compatibility Zone registration service is currently unavailable, "
+ "try again later!.", e)
}
}
}
Expand Down Expand Up @@ -249,10 +249,17 @@ open class NetworkRegistrationHelper(private val certificatesDirectory: Path,
protected open fun isTlsCrlIssuerCertRequired(): Boolean = false
}

class NodeRegistrationException(cause: Throwable?) : IOException("Unable to contact node registration service", cause)

class NodeRegistrationHelper(private val config: NodeConfiguration, certService: NetworkRegistrationService, regConfig: NodeRegistrationOption, computeNextIdleDoormanConnectionPollInterval: (Duration?) -> Duration? = FixedPeriodLimitedRetrialStrategy(10, Duration.ofMinutes(1))) :
NetworkRegistrationHelper(
class NodeRegistrationException(
message: String?,
cause: Throwable?
) : IOException(message ?: "Unable to contact node registration service", cause)

class NodeRegistrationHelper(
private val config: NodeConfiguration,
certService: NetworkRegistrationService,
regConfig: NodeRegistrationOption,
computeNextIdleDoormanConnectionPollInterval: (Duration?) -> Duration? = FixedPeriodLimitedRetrialStrategy(10, Duration.ofMinutes(1))
) : NetworkRegistrationHelper(
config.certificatesDirectory,
config.signingCertificateStore,
config.myLegalName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ class DriverDSLImpl(

val registrationFuture = if (compatibilityZone?.rootCert != null) {
// We don't need the network map to be available to be able to register the node
startNodeRegistration(name, compatibilityZone.rootCert, compatibilityZone.doormanURL())
startNodeRegistration(name, compatibilityZone.rootCert, compatibilityZone.config())
} else {
doneFuture(Unit)
}
Expand Down Expand Up @@ -275,14 +275,18 @@ class DriverDSLImpl(
return startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize, localNetworkMap, additionalCordapps, regenerateCordappsOnStart)
}

private fun startNodeRegistration(providedName: CordaX500Name, rootCert: X509Certificate, compatibilityZoneURL: URL): CordaFuture<NodeConfig> {
private fun startNodeRegistration(
providedName: CordaX500Name,
rootCert: X509Certificate,
networkServicesConfig: NetworkServicesConfig
): CordaFuture<NodeConfig> {
val baseDirectory = baseDirectory(providedName).createDirectories()
val config = NodeConfig(ConfigHelper.loadConfig(
baseDirectory = baseDirectory,
allowMissingConfig = true,
configOverrides = configOf(
"p2pAddress" to portAllocation.nextHostAndPort().toString(),
"compatibilityZoneURL" to compatibilityZoneURL.toString(),
"compatibilityZoneURL" to networkServicesConfig.doormanURL.toString(),
"myLegalName" to providedName.toString(),
"rpcSettings" to mapOf(
"address" to portAllocation.nextHostAndPort().toString(),
Expand All @@ -305,7 +309,7 @@ class DriverDSLImpl(
executorService.fork {
NodeRegistrationHelper(
config.corda,
HTTPNetworkRegistrationService(compatibilityZoneURL, versionInfo),
HTTPNetworkRegistrationService(networkServicesConfig, versionInfo),
NodeRegistrationOption(rootTruststorePath, rootTruststorePassword)
).buildKeystore()
config
Expand Down Expand Up @@ -371,7 +375,7 @@ class DriverDSLImpl(
startNotaryIdentityGeneration()
} else {
// With a root cert specified we delegate generation of the notary identities to the CZ.
startAllNotaryRegistrations(compatibilityZone.rootCert, compatibilityZone.doormanURL())
startAllNotaryRegistrations(compatibilityZone.rootCert, compatibilityZone)
}
notaryInfosFuture.map { notaryInfos ->
compatibilityZone.publishNotaries(notaryInfos)
Expand Down Expand Up @@ -422,16 +426,22 @@ class DriverDSLImpl(
}
}

private fun startAllNotaryRegistrations(rootCert: X509Certificate, compatibilityZoneURL: URL): CordaFuture<List<NotaryInfo>> {
private fun startAllNotaryRegistrations(
rootCert: X509Certificate,
compatibilityZone: CompatibilityZoneParams): CordaFuture<List<NotaryInfo>> {
// Start the registration process for all the notaries together then wait for their responses.
return notarySpecs.map { spec ->
require(spec.cluster == null) { "Registering distributed notaries not supported" }
startNotaryRegistration(spec, rootCert, compatibilityZoneURL)
startNotaryRegistration(spec, rootCert, compatibilityZone)
}.transpose()
}

private fun startNotaryRegistration(spec: NotarySpec, rootCert: X509Certificate, compatibilityZoneURL: URL): CordaFuture<NotaryInfo> {
return startNodeRegistration(spec.name, rootCert, compatibilityZoneURL).flatMap { config ->
private fun startNotaryRegistration(
spec: NotarySpec,
rootCert: X509Certificate,
compatibilityZone: CompatibilityZoneParams
): CordaFuture<NotaryInfo> {
return startNodeRegistration(spec.name, rootCert, compatibilityZone.config()).flatMap { config ->
// Node registration only gives us the node CA cert, not the identity cert. That is only created on first
// startup or when the node is told to just generate its node info file. We do that here.
if (startNodesInProcess) {
Expand Down Expand Up @@ -1067,18 +1077,26 @@ sealed class CompatibilityZoneParams(
) {
abstract fun networkMapURL(): URL
abstract fun doormanURL(): URL
abstract fun config() : NetworkServicesConfig
}

/**
* Represent network management services, network map and doorman, running on the same URL
*/
class SharedCompatibilityZoneParams(
private val url: URL,
private val pnm : UUID?,
publishNotaries: (List<NotaryInfo>) -> Unit,
rootCert: X509Certificate? = null
) : CompatibilityZoneParams(publishNotaries, rootCert) {

val config : NetworkServicesConfig by lazy {
NetworkServicesConfig(url, url, pnm, false)
}

override fun doormanURL() = url
override fun networkMapURL() = url
override fun config() : NetworkServicesConfig = config
}

/**
Expand All @@ -1087,11 +1105,17 @@ class SharedCompatibilityZoneParams(
class SplitCompatibilityZoneParams(
private val doormanURL: URL,
private val networkMapURL: URL,
private val pnm : UUID?,
publishNotaries: (List<NotaryInfo>) -> Unit,
rootCert: X509Certificate? = null
) : CompatibilityZoneParams(publishNotaries, rootCert) {
val config : NetworkServicesConfig by lazy {
NetworkServicesConfig(doormanURL, networkMapURL, pnm, false)
}

override fun doormanURL() = doormanURL
override fun networkMapURL() = networkMapURL
override fun config() : NetworkServicesConfig = config
}

fun <A> internalDriver(
Expand Down

0 comments on commit 7cfd44e

Please sign in to comment.