ENT-2653: Split Artemis and Tunnel keys generation onto two separate sub-commands (#1584)

* ENT-2653: Introduce generation modes in InternalKeystoreGenerator

So that it would be possible to produce Artemis and Tunnel keystores independently.

* ENT-2653: Split Artemis certificates generation and Tunnel certificate generation onto two separate sub-commands.

* ENT-2653: Minor documentation update
This commit is contained in:
Viktor Kolomeyko 2018-11-23 11:20:10 +00:00 committed by GitHub
parent df2c313f23
commit 109bed8def
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 95 additions and 37 deletions

View File

@ -23,7 +23,8 @@ Sub-commands
``node-registration``: Corda registration tool for registering 1 or more node with the corda network, using provided node configuration.
``import-ssl-key``: Key copying tool for creating bridge SSL keystore or add new node SSL identity to existing bridge SSL keystore.
``generate-internal-ssl-keystores``: Generate self-signed root and SSL certificates for bridge, external artemis broker and float, for internal communication between the services.
``generate-internal-artemis-ssl-keystores``: Generate self-signed root and SSL certificates for internal communication between the services and external Artemis broker.
``generate-internal-tunnel-ssl-keystores``: Generate self-signed root and SSL certificates for internal communication between Bridge and Float.
``install-shell-extensions``: Install alias and autocompletion for bash and zsh. See :doc:`cli-application-shell-extensions` for more info.
@ -70,22 +71,45 @@ Command-line options
* ``-V``, ``--version`` :Print version information and exit.
Self signed internal SSL keystore
---------------------------------
Self signed internal Artemis SSL keystore
-----------------------------------------
TLS is used to ensure communications between firewall components are secured. This tool can be used to generate the required keystores if TLS cert signing infrastructure is not available within your organisation.
TLS is used to ensure communications between various components connected to standalone Artemis. This tool can be used to generate the required keystores if TLS cert signing infrastructure is not available within your organisation.
Please note that for Artemis to work correctly with keystores, the password for the store and the password for the private will be set to the same value.
Command-line options
~~~~~~~~~~~~~~~~~~~~
.. code-block:: shell
ha-utilities generate-internal-ssl-keystores [-hvV] [--logging-level=<loggingLevel>] [-b=<baseDirectory>] [-c=<country>] [-l=<locality>] [-o=<organization>] [-p=<password>]
ha-utilities generate-internal-artemis-ssl-keystores [-hvV] [--logging-level=<loggingLevel>] [-b=<baseDirectory>] [-c=<country>] [-l=<locality>] [-o=<organization>] [-p=<keyStorePassword>] [-t=<trustStorePassword>]
* ``-v``, ``--verbose``, ``--log-to-console``: If set, prints logging to the console as well as to a file.
* ``--logging-level=<loggingLevel>``: Enable logging at this level and higher. Possible values: ERROR, WARN, INFO, DEBUG, TRACE. Default: INFO
* ``-p``, ``--keyStorePassword=<keyStorePassword>``: Password for all generated keystores. Default: changeit
* ``-t``, ``--trustStorePassword=<trustStorePassword>``: Password for the trust store. Default: changeit
* ``-o``, ``--organization=<organization>``: X500Name's organization attribute. Default: Corda
* ``-l``, ``--locality=<locality>``: X500Name's locality attribute. Default: London
* ``-c``, ``--country=<country>``: X500Name's country attribute. Default: GB
* ``-b``, ``--base-directory=<baseDirectory>``: The node working directory where all the files are kept.
* ``-h``, ``--help``: Show this help message and exit.
* ``-V``, ``--version``: Print version information and exit.
Self signed internal Tunnel SSL keystore
-----------------------------------------
TLS is used to for communications between Bridge and Float components. This tool can be used to generate the required keystores if TLS cert signing infrastructure is not available within your organisation.
Command-line options
~~~~~~~~~~~~~~~~~~~~
.. code-block:: shell
ha-utilities generate-internal-tunnel-ssl-keystores [-hvV] [--logging-level=<loggingLevel>] [-b=<baseDirectory>] [-c=<country>] [-l=<locality>] [-o=<organization>] [-p=<keyStorePassword>] [-e=<entryPassword>] [-t=<trustStorePassword>]
* ``-v``, ``--verbose``, ``--log-to-console``: If set, prints logging to the console as well as to a file.
* ``--logging-level=<loggingLevel>``: Enable logging at this level and higher. Possible values: ERROR, WARN, INFO, DEBUG, TRACE. Default: INFO
* ``-p``, ``--keyStorePassword=<keyStorePassword>``: Password for all generated keystores. Default: changeit
* ``-e``, ``--entryPassword=<entryPassword>``: Password for all the keystores private keys. Default: changeit
* ``-t``, ``--trustStorePassword=<trustStorePassword>``: Password for all the trust stores. Default: changeit
* ``-t``, ``--trustStorePassword=<trustStorePassword>``: Password for the trust store. Default: changeit
* ``-o``, ``--organization=<organization>``: X500Name's organization attribute. Default: Corda
* ``-l``, ``--locality=<locality>``: X500Name's locality attribute. Default: London
* ``-c``, ``--country=<country>``: X500Name's country attribute. Default: GB

View File

@ -15,20 +15,45 @@ import java.nio.file.Path
import java.nio.file.Paths
import javax.security.auth.x500.X500Principal
class InternalKeystoreGenerator : CliWrapperBase("generate-internal-ssl-keystores", "Generate self-signed root and SSL certificates for bridge, external artemis broker and float, for internal communication between the services.") {
companion object {
private const val DEFAULT_PASSWORD = "changeit"
private const val DEFAULT_PASSWORD = "changeit"
internal class InternalArtemisKeystoreGenerator : AbstractInternalKeystoreGenerator("generate-internal-artemis-ssl-keystores", "Generate self-signed root and SSL certificates for internal communication between the services and external Artemis broker.") {
override fun createKeyStores() {
println("Generating Artemis keystores")
val artemisCertDir = baseDirectory / "artemis"
val root = createRootKeystore("Internal Artemis Root", artemisCertDir / "artemis-root.jks", artemisCertDir / "artemis-truststore.jks",
keyStorePassword, keyStorePassword, trustStorePassword).getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, keyStorePassword)
createTLSKeystore("bridge", root, artemisCertDir / "bridge.jks", keyStorePassword, keyStorePassword)
createTLSKeystore("artemis", root, artemisCertDir / "artemis.jks", keyStorePassword, keyStorePassword)
createTLSKeystore("artemis-client", root, artemisCertDir / "artemis-client.jks", keyStorePassword, keyStorePassword)
}
}
internal class InternalTunnelKeystoreGenerator : AbstractInternalKeystoreGenerator("generate-internal-tunnel-ssl-keystores", "Generate self-signed root and SSL certificates for internal communication between Bridge and Float.") {
@Option(names = ["-e", "--entryPassword"], description = ["Password for all the keystores private keys."], defaultValue = DEFAULT_PASSWORD)
lateinit var entryPassword: String
override fun createKeyStores() {
println("Generating Tunnel keystores")
val tunnelCertDir = baseDirectory / "tunnel"
val tunnelRoot = createRootKeystore("Internal Tunnel Root", tunnelCertDir / "tunnel-root.jks", tunnelCertDir / "tunnel-truststore.jks",
keyStorePassword, entryPassword, trustStorePassword).getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, entryPassword)
createTLSKeystore("float", tunnelRoot, tunnelCertDir / "float.jks", keyStorePassword, entryPassword)
createTLSKeystore("bridge", tunnelRoot, tunnelCertDir / "bridge.jks", keyStorePassword, entryPassword)
warnOnDefaultPassword(entryPassword, "entryPassword")
}
}
internal abstract class AbstractInternalKeystoreGenerator(alias: String, description: String) : CliWrapperBase(alias, description) {
@Option(names = ["-b", BASE_DIR], description = ["The node working directory where all the files are kept."])
var baseDirectory: Path = Paths.get(".").toAbsolutePath().normalize()
// TODO: options to generate keys for different HA deployment mode?
@Option(names = ["-p", "--keyStorePassword"], description = ["Password for all generated keystores."], defaultValue = DEFAULT_PASSWORD)
lateinit var keyStorePassword: String
@Option(names = ["-e", "--entryPassword"], description = ["Password for all the keystores private keys."], defaultValue = DEFAULT_PASSWORD)
lateinit var entryPassword: String
@Option(names = ["-t", "--trustStorePassword"], description = ["Password for all the trust stores."], defaultValue = DEFAULT_PASSWORD)
@Option(names = ["-t", "--trustStorePassword"], description = ["Password for the trust store."], defaultValue = DEFAULT_PASSWORD)
lateinit var trustStorePassword: String
@Option(names = ["-o", "--organization"], description = ["X500Name's organization attribute."], defaultValue = "Corda")
lateinit var organization: String
@ -39,33 +64,26 @@ class InternalKeystoreGenerator : CliWrapperBase("generate-internal-ssl-keystore
@Option(names = ["-c", "--country"], description = ["X500Name's country attribute."], defaultValue = "GB")
lateinit var country: String
protected abstract fun createKeyStores()
override fun runProgram(): Int {
// Create tunnel certs
val tunnelCertDir = baseDirectory / "tunnel"
val tunnelRoot = createRootKeystore("Internal Tunnel Root", tunnelCertDir / "tunnel-root.jks", tunnelCertDir / "tunnel-truststore.jks",
keyStorePassword, entryPassword, trustStorePassword).getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, entryPassword)
createTLSKeystore("float", tunnelRoot, tunnelCertDir / "float.jks", keyStorePassword, entryPassword)
createTLSKeystore("bridge", tunnelRoot, tunnelCertDir / "bridge.jks", keyStorePassword, entryPassword)
// Create artemis certs
// NB: For Artemis store password must be the same as key password due to implementation restrictions.
val artemisCertDir = baseDirectory / "artemis"
val root = createRootKeystore("Internal Artemis Root", artemisCertDir / "artemis-root.jks", artemisCertDir / "artemis-truststore.jks",
keyStorePassword, keyStorePassword, trustStorePassword).getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, keyStorePassword)
createTLSKeystore("bridge", root, artemisCertDir / "bridge.jks", keyStorePassword, keyStorePassword)
createTLSKeystore("artemis", root, artemisCertDir / "artemis.jks", keyStorePassword, keyStorePassword)
createTLSKeystore("artemis-client", root, artemisCertDir / "artemis-client.jks", keyStorePassword, keyStorePassword)
createKeyStores()
listOf(keyStorePassword to "keyStorePassword", entryPassword to "entryPassword", trustStorePassword to "trustStorePassword").forEach {
if(it.first == DEFAULT_PASSWORD) {
println("WARN: Password for '${it.second}' is defaulted to '$DEFAULT_PASSWORD'. Please consider changing the password using java keytool.")
}
listOf(keyStorePassword to "keyStorePassword", trustStorePassword to "trustStorePassword").forEach {
warnOnDefaultPassword(it.first, it.second)
}
return ExitCodes.SUCCESS
}
private fun createRootKeystore(commonName: String, keystorePath: Path, trustStorePath: Path, storePassword: String, entryPassword: String, trustStorePassword: String): X509KeyStore {
protected fun warnOnDefaultPassword(password: String, paramName: String) {
if (password == DEFAULT_PASSWORD) {
println("WARN: Password for '$paramName' is defaulted to '$DEFAULT_PASSWORD'. Please consider changing the password using java keytool.")
}
}
protected fun createRootKeystore(commonName: String, keystorePath: Path, trustStorePath: Path, storePassword: String, entryPassword: String, trustStorePassword: String): X509KeyStore {
val key = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCert = X509Utilities.createSelfSignedCACertificate(getX500Principal(commonName), key)
val keystore = X509KeyStore.fromFile(keystorePath, storePassword, createNew = true)
@ -80,7 +98,7 @@ class InternalKeystoreGenerator : CliWrapperBase("generate-internal-ssl-keystore
return keystore
}
private fun createTLSKeystore(serviceName: String, root: CertificateAndKeyPair, keystorePath: Path, storePassword: String, entryPassword: String) {
protected fun createTLSKeystore(serviceName: String, root: CertificateAndKeyPair, keystorePath: Path, storePassword: String, entryPassword: String) {
val key = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val cert = X509Utilities.createCertificate(CertificateType.TLS, root.certificate, root.keyPair, getX500Principal(serviceName), key.public)
X509KeyStore.fromFile(keystorePath, storePassword, createNew = true).update {

View File

@ -9,7 +9,7 @@ fun main(args: Array<String>) {
}
class HAUtilities : CordaCliWrapper("ha-utilities", "HA utilities contains tools to help setting up corda firewall services.") {
override fun additionalSubCommands() = setOf(RegistrationTool(), BridgeSSLKeyTool(), InternalKeystoreGenerator())
override fun additionalSubCommands() = setOf(RegistrationTool(), BridgeSSLKeyTool(), InternalArtemisKeystoreGenerator(), InternalTunnelKeystoreGenerator())
override fun runProgram(): Int {
printHelp()

View File

@ -17,14 +17,14 @@ import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class InternalKeystoreGeneratorTest {
private val generator = InternalKeystoreGenerator()
@Rule
@JvmField
val tempFolder: TemporaryFolder = TemporaryFolder()
@Test
fun `generate keystores correctly`() {
fun `generate tunnel keystores correctly`() {
val generator = InternalTunnelKeystoreGenerator()
val workingDirectory = tempFolder.root.toPath()
val keyStorePassword = "keyStorePassword"
val entryPassword = "entryPassword"
@ -57,6 +57,22 @@ class InternalKeystoreGeneratorTest {
assertTrue(internal.isCertificateEntry(CORDA_ROOT_CA))
assertFalse(internal.isKeyEntry(CORDA_ROOT_CA))
}
}
@Test
fun `generate Artemis keystores correctly`() {
val generator = InternalArtemisKeystoreGenerator()
val workingDirectory = tempFolder.root.toPath()
val keyStorePassword = "keyStorePassword"
val trustStorePassword = "trustStorePassword"
CommandLine.populateCommand(generator,
BASE_DIR, workingDirectory.toString(),
"--keyStorePassword", keyStorePassword,
"--trustStorePassword", trustStorePassword)
assertEquals(workingDirectory, generator.baseDirectory)
assertEquals(keyStorePassword, generator.keyStorePassword)
assertEquals(trustStorePassword, generator.trustStorePassword)
generator.runProgram()
listOf("bridge.jks", "artemis.jks", "artemis-client.jks").map { workingDirectory / "artemis" / it }.forEach {
assertTrue(it.exists())