-
Notifications
You must be signed in to change notification settings - Fork 285
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(corda-connector): scp jars to nodes #621
Implements transferring the cordapp jar files onto Corda nodes via SSH+SCP. It supports deploying to multiple nodes in a single request via an array of deployment configuration parameters that the caller can specify. Credentials are travelling on the wire in plain text right now which is of course unacceptable and a follow-up issue has been created to rectify this by adding keychain support or some other type of solution that does not make it necessary for the caller to send up SSH and RPC credentials with their deployment request. Another thing that we'll have to fix prior to GA is that right now there is no SSH host key verification. For development mode Corda nodes: There's possibility to declare which cordapp jar contains database migrations for H2 and the deployment endpoint can run these as well. The graceful shutdown method is implemented on our RPC connection class because this is not yet supported on the version of Corda that we are targeting with the connector (v4.5). The new test that verifies that all is working well is called deploy-cordapp-jars-to-nodes.test.ts and what it does is very similar to the older test called jvm-kotlin-spring-server.test.ts but this one does its invocations with a contract that has been built and deployed from scratch not like the old test which relied on the jars already being present in the AIO Corda container by default. The current commit is also tagged in the container registry as: hyperledger/cactus-connector-corda-server:2021-03-16-feat-621 Fixes #621 Signed-off-by: Peter Somogyvari <peter.somogyvari@accenture.com>
- Loading branch information
Showing
18 changed files
with
1,103 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
295 changes: 236 additions & 59 deletions
295
...tus/plugin/ledger/connector/corda/server/impl/ApiPluginLedgerConnectorCordaServiceImpl.kt
Large diffs are not rendered by default.
Oops, something went wrong.
74 changes: 74 additions & 0 deletions
74
...g/hyperledger/cactus/plugin/ledger/connector/corda/server/impl/InMemoryHostKeyVerifier.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package org.hyperledger.cactus.plugin.ledger.connector.corda.server.impl | ||
|
||
import net.corda.core.utilities.loggerFor | ||
import net.schmizz.sshj.common.KeyType | ||
import net.schmizz.sshj.transport.verification.HostKeyVerifier | ||
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts | ||
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts.KnownHostEntry | ||
import java.io.* | ||
import java.lang.RuntimeException | ||
import java.nio.charset.Charset | ||
import java.security.PublicKey | ||
import java.util.* | ||
|
||
// TODO: Once we are able to support host key verification this can be either | ||
// deleted or just left alone if it actually ends up being used by the | ||
// fix that makes it so that we can do host key verification. | ||
class InMemoryHostKeyVerifier(inputStream: InputStream?, charset: Charset?) : | ||
HostKeyVerifier { | ||
private val entries: MutableList<KnownHostEntry> = ArrayList() | ||
|
||
companion object { | ||
val logger = loggerFor<InMemoryHostKeyVerifier>() | ||
} | ||
|
||
init { | ||
|
||
// we construct the OpenSSHKnownHosts instance with a dummy file that does not exist because | ||
// that's the only way to trick it into doing nothing on the file system which is what we want | ||
// since this implementation is about providing an in-memory host key verification process... | ||
val nonExistentFilePath = UUID.randomUUID().toString() | ||
val hostsFile = File(nonExistentFilePath) | ||
val openSSHKnownHosts = OpenSSHKnownHosts(hostsFile) | ||
|
||
// we just wanted an EntryFactory which we could not instantiate without instantiating the OpenSSHKnownHosts | ||
// class as well (which is a limitation of Kotlin compared to Java it seems). | ||
val entryFactory: OpenSSHKnownHosts.EntryFactory = openSSHKnownHosts.EntryFactory() | ||
val reader = BufferedReader(InputStreamReader(inputStream, charset)) | ||
while (reader.ready()) { | ||
val line = reader.readLine() | ||
try { | ||
logger.debug("Parsing line {}", line) | ||
val entry = entryFactory.parseEntry(line) | ||
if (entry != null) { | ||
entries.add(entry) | ||
logger.debug("Added entry {}", entry) | ||
} | ||
} catch (e: Exception) { | ||
throw RuntimeException("Failed to init InMemoryHostKeyVerifier", e) | ||
} | ||
} | ||
logger.info("Parsing of host key entries successful.") | ||
} | ||
|
||
override fun verify(hostname: String, port: Int, key: PublicKey): Boolean { | ||
logger.debug("Verifying {}:{} {}", hostname, port, key) | ||
val type = KeyType.fromKey(key) | ||
if (type === KeyType.UNKNOWN) { | ||
logger.debug("Rejecting key due to unknown key type {}", type) | ||
return false | ||
} | ||
for (e in entries) { | ||
try { | ||
if (e.appliesTo(type, hostname) && e.verify(key)) { | ||
logger.debug("Accepting key type {} for host {} with key of {}", type, hostname, key) | ||
return true | ||
} | ||
} catch (ioe: IOException) { | ||
throw RuntimeException("Crashed while attempting to verify key type $type for host $hostname ", ioe) | ||
} | ||
} | ||
logger.debug("Rejecting due to none of the {} entries being acceptable.", entries.size) | ||
return false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
.../hyperledger/cactus/plugin/ledger/connector/corda/server/model/CordaNodeSshCredentials.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package org.hyperledger.cactus.plugin.ledger.connector.corda.server.model | ||
|
||
import java.util.Objects | ||
import com.fasterxml.jackson.annotation.JsonProperty | ||
import javax.validation.constraints.DecimalMax | ||
import javax.validation.constraints.DecimalMin | ||
import javax.validation.constraints.Max | ||
import javax.validation.constraints.Min | ||
import javax.validation.constraints.NotNull | ||
import javax.validation.constraints.Pattern | ||
import javax.validation.constraints.Size | ||
import javax.validation.Valid | ||
|
||
/** | ||
* | ||
* @param hostKeyEntry | ||
* @param username | ||
* @param password | ||
* @param hostname | ||
* @param port | ||
*/ | ||
data class CordaNodeSshCredentials( | ||
|
||
@get:NotNull | ||
@get:Size(min=1,max=65535) | ||
@field:JsonProperty("hostKeyEntry") val hostKeyEntry: kotlin.String, | ||
|
||
@get:NotNull | ||
@get:Size(min=1,max=32) | ||
@field:JsonProperty("username") val username: kotlin.String, | ||
|
||
@get:NotNull | ||
@get:Size(min=1,max=4096) | ||
@field:JsonProperty("password") val password: kotlin.String, | ||
|
||
@get:NotNull | ||
@get:Size(min=1,max=4096) | ||
@field:JsonProperty("hostname") val hostname: kotlin.String, | ||
|
||
@get:NotNull | ||
@get:Min(1) | ||
@get:Max(65535) | ||
@field:JsonProperty("port") val port: kotlin.Int | ||
) { | ||
|
||
} | ||
|
42 changes: 42 additions & 0 deletions
42
.../org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/CordaRpcCredentials.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package org.hyperledger.cactus.plugin.ledger.connector.corda.server.model | ||
|
||
import java.util.Objects | ||
import com.fasterxml.jackson.annotation.JsonProperty | ||
import javax.validation.constraints.DecimalMax | ||
import javax.validation.constraints.DecimalMin | ||
import javax.validation.constraints.Max | ||
import javax.validation.constraints.Min | ||
import javax.validation.constraints.NotNull | ||
import javax.validation.constraints.Pattern | ||
import javax.validation.constraints.Size | ||
import javax.validation.Valid | ||
|
||
/** | ||
* | ||
* @param hostname | ||
* @param port | ||
* @param username | ||
* @param password | ||
*/ | ||
data class CordaRpcCredentials( | ||
|
||
@get:NotNull | ||
@get:Size(min=1,max=65535) | ||
@field:JsonProperty("hostname") val hostname: kotlin.String, | ||
|
||
@get:NotNull | ||
@get:Min(1) | ||
@get:Max(65535) | ||
@field:JsonProperty("port") val port: kotlin.Int, | ||
|
||
@get:NotNull | ||
@get:Size(min=1,max=1024) | ||
@field:JsonProperty("username") val username: kotlin.String, | ||
|
||
@get:NotNull | ||
@get:Size(min=1,max=65535) | ||
@field:JsonProperty("password") val password: kotlin.String | ||
) { | ||
|
||
} | ||
|
53 changes: 53 additions & 0 deletions
53
.../hyperledger/cactus/plugin/ledger/connector/corda/server/model/CordappDeploymentConfig.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package org.hyperledger.cactus.plugin.ledger.connector.corda.server.model | ||
|
||
import java.util.Objects | ||
import com.fasterxml.jackson.annotation.JsonProperty | ||
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.CordaNodeSshCredentials | ||
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.CordaRpcCredentials | ||
import javax.validation.constraints.DecimalMax | ||
import javax.validation.constraints.DecimalMin | ||
import javax.validation.constraints.Max | ||
import javax.validation.constraints.Min | ||
import javax.validation.constraints.NotNull | ||
import javax.validation.constraints.Pattern | ||
import javax.validation.constraints.Size | ||
import javax.validation.Valid | ||
|
||
/** | ||
* | ||
* @param sshCredentials | ||
* @param rpcCredentials | ||
* @param cordaNodeStartCmd The shell command to execute in order to start back up a Corda node after having placed new jars in the cordapp directory of said node. | ||
* @param cordappDir The absolute file system path where the Corda Node is expecting deployed Cordapp jar files to be placed. | ||
* @param cordaJarPath The absolute file system path where the corda.jar file of the node can be found. This is used to execute database schema migrations where applicable (H2 database in use in development environments). | ||
* @param nodeBaseDirPath The absolute file system path where the base directory of the Corda node can be found. This is used to pass in to corda.jar when being invoked for certain tasks such as executing database schema migrations for a deployed contract. | ||
*/ | ||
data class CordappDeploymentConfig( | ||
|
||
@get:NotNull | ||
@field:Valid | ||
@field:JsonProperty("sshCredentials") val sshCredentials: CordaNodeSshCredentials, | ||
|
||
@get:NotNull | ||
@field:Valid | ||
@field:JsonProperty("rpcCredentials") val rpcCredentials: CordaRpcCredentials, | ||
|
||
@get:NotNull | ||
@get:Size(min=1,max=65535) | ||
@field:JsonProperty("cordaNodeStartCmd") val cordaNodeStartCmd: kotlin.String, | ||
|
||
@get:NotNull | ||
@get:Size(min=1,max=2048) | ||
@field:JsonProperty("cordappDir") val cordappDir: kotlin.String, | ||
|
||
@get:NotNull | ||
@get:Size(min=1,max=2048) | ||
@field:JsonProperty("cordaJarPath") val cordaJarPath: kotlin.String, | ||
|
||
@get:NotNull | ||
@get:Size(min=1,max=2048) | ||
@field:JsonProperty("nodeBaseDirPath") val nodeBaseDirPath: kotlin.String | ||
) { | ||
|
||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.