mirror of
https://github.com/corda/corda.git
synced 2025-01-13 08:20:01 +00:00
Split up the parsing of the command line arguments from the parsing of the config files. (#547)
Also, the "config-file" command line argument for the doorman jar is required - it no longer defaults to network-management.conf
This commit is contained in:
parent
4b1e0a6ffd
commit
34800ab527
@ -27,11 +27,7 @@ Allowed parameters are:
|
||||
|
||||
:rootPrivateKeyPassword: Password for the root private key
|
||||
|
||||
:host: host on which doorman runs
|
||||
|
||||
:port: port on which doorman runs
|
||||
|
||||
:mode: must be one of: DOORMAN (default), CA_KEYGEN, ROOT_KEYGEN.
|
||||
:address: The host and port on which doorman runs
|
||||
|
||||
:database: database properties. The same (including its default value) as for node configuration (see :doc:`corda-configuration-file`).
|
||||
|
||||
|
@ -71,8 +71,8 @@ Doorman service can be started with the following options :
|
||||
|
||||
The doorman service can use JIRA to manage the certificate signing request approval workflow. This can be turned on by providing JIRA connection configuration in the config file.
|
||||
```
|
||||
doormanConfig {
|
||||
jiraConfig {
|
||||
doorman {
|
||||
jira {
|
||||
address = "https://doorman-jira-host.com/"
|
||||
projectCode = "TD"
|
||||
username = "username"
|
||||
@ -93,7 +93,7 @@ The doorman service can use JIRA to manage the certificate signing request appro
|
||||
### Network map service
|
||||
Network map service can be enabled by providing the following config:
|
||||
```
|
||||
networkMapConfig {
|
||||
networkMap {
|
||||
cacheTimeout = 600000
|
||||
signInterval = 10000
|
||||
}
|
||||
@ -104,15 +104,13 @@ The doorman service can use JIRA to manage the certificate signing request appro
|
||||
##Example config file
|
||||
```
|
||||
basedir = "."
|
||||
host = localhost
|
||||
port = 0
|
||||
address = "localhost:0"
|
||||
rootStorePath = ${basedir}"/certificates/rootstore.jks"
|
||||
keystorePath = ${basedir}"/certificates/caKeystore.jks"
|
||||
#keystorePassword = "password" #Optional if not specified, user will be prompted on the console.
|
||||
#caPrivateKeyPassword = "password" #Optional if not specified, user will be prompted on the console.
|
||||
#rootPrivateKeyPassword = "password" #Optional if not specified, user will be prompted on the console.
|
||||
#rootKeystorePassword = "password" #Optional if not specified, user will be prompted on the console.
|
||||
#trustStorePassword = "password" #Optional if not specified, user will be prompted on the console. Applicable only if, in the ROOT_KEYGEN execution mode.
|
||||
|
||||
dataSourceProperties {
|
||||
dataSourceClassName = org.h2.jdbcx.JdbcDataSource
|
||||
@ -153,31 +151,35 @@ networkMap {
|
||||
If local signer is enabled, the server will look for key stores in the certificate folder on start up.
|
||||
The key stores can be created using `--mode` flag.
|
||||
```
|
||||
java -jar doorman-<version>.jar --mode ROOT_KEYGEN
|
||||
java -jar doorman-<version>.jar --config-file <config file> --mode ROOT_KEYGEN
|
||||
```
|
||||
and
|
||||
```
|
||||
java -jar doorman-<version>.jar --mode CA_KEYGEN
|
||||
java -jar doorman-<version>.jar --config-file <config file> --mode CA_KEYGEN
|
||||
```
|
||||
|
||||
A trust store file containing the root trust certificate will be produced in the location `distribute-nodes / truststore.jks`
|
||||
(relative to `rootStorePath`). `truststore.jks` must be copied to the `certificates` directory of each node before
|
||||
they attempt to register. The trust store password is `trustpass`.
|
||||
A trust store containing the root certificate will be created in the location `distribute-nodes / network-root-truststore.jks`
|
||||
(relative to `rootStorePath`). The trust store's password can be set using command line argument `--trust-store-password`,
|
||||
or the doorman's keygen utility will ask for password input if trust store password is not provided using this flag.
|
||||
Path to the network root trust store and password must be provided to corda node via command line arguments before
|
||||
they attempt to register.
|
||||
This trust store file is to be distributed to every node that wishes to register with the doorman. The node cannot
|
||||
register without it.
|
||||
|
||||
### 2. Start Doorman service for notary registration
|
||||
Start the network management server with the doorman service for initial bootstrapping. Network map service (`networkMapConfig`) should be **disabled** at this point.
|
||||
**Comment out** network map config in the config file and start the server by running :
|
||||
Start the network management server with the doorman service for initial bootstrapping. Network map service (`networkMap`)
|
||||
should be **disabled** at this point. **Comment out** network map config in the config file and start the server by running :
|
||||
```
|
||||
java -jar doorman-<version>.jar
|
||||
java -jar doorman-<version>.jar --config-file <config file>
|
||||
```
|
||||
|
||||
### 3. Create notary node and register with the doorman
|
||||
After the doorman service is started, start the notary node for the `initial-registration` process.
|
||||
After the doorman service is started, start the notary node for registration.
|
||||
```
|
||||
java -jar corda.jar --initial-registration --network-root-truststore-password <trust store password>
|
||||
```
|
||||
By default it will expect trust store file received from the doorman to be in the location ``certificates/network-root-truststore.jks``.
|
||||
This can be overridden with the additional `--network-root-truststore` flag.
|
||||
|
||||
NOTE: This step applies to all nodes that wish to register with the doorman.
|
||||
|
||||
### 4. Generate node info files for notary nodes
|
||||
Once notary nodes are registered, run the notary nodes with the `just-generate-node-info` flag.
|
||||
@ -187,16 +189,19 @@ networkMap {
|
||||
The network parameters should contain reference to the notaries node info files.
|
||||
Example network parameters file:
|
||||
|
||||
notaries : [{
|
||||
notaryNodeInfoFile: "/Path/To/NodeInfo/File1"
|
||||
validating: true
|
||||
}, {
|
||||
notaryNodeInfoFile: "/Path/To/NodeInfo/File2"
|
||||
validating: false
|
||||
}]
|
||||
notaries : [
|
||||
{
|
||||
notaryNodeInfoFile: "/Path/To/NodeInfo/File1"
|
||||
validating: true
|
||||
},
|
||||
{
|
||||
notaryNodeInfoFile: "/Path/To/NodeInfo/File2"
|
||||
validating: false
|
||||
}
|
||||
]
|
||||
minimumPlatformVersion = 1
|
||||
maxMessageSize = 10485760
|
||||
maxTransactionSize = 40000
|
||||
maxTransactionSize = 10485760
|
||||
|
||||
Save the parameters to `network-parameters.conf`
|
||||
|
||||
@ -204,7 +209,7 @@ networkMap {
|
||||
A network parameters file is required to start the network map service for the first time. The initial network parameters file can be loaded using the `--update-network-parameters` flag.
|
||||
We can now restart the network management server with both doorman and network map service.
|
||||
```
|
||||
java -jar doorman-<version>.jar --update-network-parameters network-parameters.conf
|
||||
java -jar doorman-<version>.jar --config-file <config file> --update-network-parameters network-parameters.conf
|
||||
```
|
||||
|
||||
### 7. Logs
|
||||
@ -212,5 +217,5 @@ In order to set the desired logging level the system properties need to be used.
|
||||
Appropriate system properties can be set at the execution time.
|
||||
Example:
|
||||
```
|
||||
java -DdefaultLogLevel=TRACE -DconsoleLogLevel=TRACE -jar doorman-<version>.jar
|
||||
java -DdefaultLogLevel=TRACE -DconsoleLogLevel=TRACE -jar doorman-<version>.jar --config-file <config file>
|
||||
```
|
@ -13,14 +13,14 @@ h2port = 0
|
||||
|
||||
# Doorman config
|
||||
# Comment out this section if running without doorman service
|
||||
doormanConfig{
|
||||
doorman {
|
||||
approveInterval = 10000
|
||||
approveAll = false
|
||||
}
|
||||
|
||||
# Network map config
|
||||
# Comment out this section if running without network map service
|
||||
networkMapConfig{
|
||||
networkMap {
|
||||
cacheTimeout = 600000
|
||||
signInterval = 10000
|
||||
}
|
||||
|
@ -13,10 +13,10 @@ h2port = 0
|
||||
|
||||
# Doorman config
|
||||
# Comment out this section if running without doorman service
|
||||
doormanConfig{
|
||||
doorman {
|
||||
approveInterval = 10000
|
||||
approveAll = false
|
||||
jiraConfig{
|
||||
jira {
|
||||
address = "https://doorman-jira-host.com/"
|
||||
projectCode = "TD"
|
||||
username = "username"
|
||||
@ -27,7 +27,7 @@ doormanConfig{
|
||||
|
||||
# Network map config
|
||||
# Comment out this section if running without network map service
|
||||
networkMapConfig{
|
||||
networkMap {
|
||||
cacheTimeout = 600000
|
||||
signInterval = 10000
|
||||
}
|
||||
|
@ -19,14 +19,14 @@ h2port = 0
|
||||
|
||||
# Doorman config
|
||||
# Comment out this section if running without doorman service
|
||||
doormanConfig{
|
||||
doorman {
|
||||
approveInterval = 10000
|
||||
approveAll = false
|
||||
}
|
||||
|
||||
# Network map config
|
||||
# Comment out this section if running without network map service
|
||||
networkMapConfig{
|
||||
networkMap {
|
||||
cacheTimeout = 600000
|
||||
signInterval = 10000
|
||||
}
|
||||
|
@ -1,13 +1,11 @@
|
||||
basedir = "."
|
||||
host = localhost
|
||||
port = 0
|
||||
address = "localhost:0"
|
||||
rootStorePath = ${basedir}"/certificates/rootstore.jks"
|
||||
keystorePath = ${basedir}"/certificates/caKeystore.jks"
|
||||
#keystorePassword = "password" #Optional if not specified, user will be prompted on the console.
|
||||
#caPrivateKeyPassword = "password" #Optional if not specified, user will be prompted on the console.
|
||||
#rootPrivateKeyPassword = "password" #Optional if not specified, user will be prompted on the console.
|
||||
#rootKeystorePassword = "password" #Optional if not specified, user will be prompted on the console.
|
||||
#trustStorePassword = "password" #Optional if not specified, user will be prompted on the console. Applicable only if, in the ROOT_KEYGEN execution mode.
|
||||
|
||||
dataSourceProperties {
|
||||
dataSourceClassName = org.h2.jdbcx.JdbcDataSource
|
||||
|
@ -15,17 +15,6 @@ doorman {
|
||||
}
|
||||
}
|
||||
|
||||
networkMap {
|
||||
username = "TEST_USERNAME",
|
||||
keyGroup = "DEV.CORDACONNECT.OPS.NETMAP"
|
||||
authParameters {
|
||||
mode = KEY_FILE
|
||||
password = "PASSWORD"
|
||||
keyFilePath = "./Administrator.KEY"
|
||||
threshold = 2
|
||||
}
|
||||
}
|
||||
|
||||
h2port = 0
|
||||
dataSourceProperties {
|
||||
"dataSourceClassName" = org.h2.jdbcx.JdbcDataSource
|
22
network-management/hsm-for-networkmap.conf
Normal file
22
network-management/hsm-for-networkmap.conf
Normal file
@ -0,0 +1,22 @@
|
||||
basedir = "."
|
||||
device = "3001@192.168.0.1"
|
||||
keySpecifier = -1
|
||||
|
||||
networkMap {
|
||||
username = "TEST_USERNAME",
|
||||
keyGroup = "DEV.CORDACONNECT.OPS.NETMAP"
|
||||
authParameters {
|
||||
mode = KEY_FILE
|
||||
password = "PASSWORD"
|
||||
keyFilePath = "./Administrator.KEY"
|
||||
threshold = 2
|
||||
}
|
||||
}
|
||||
|
||||
h2port = 0
|
||||
dataSourceProperties {
|
||||
"dataSourceClassName" = org.h2.jdbcx.JdbcDataSource
|
||||
"dataSource.url" = "jdbc:h2:file:"${basedir}"/persistence;DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=10000;WRITE_DELAY=0;AUTO_SERVER_PORT="${h2port}
|
||||
"dataSource.user" = sa
|
||||
"dataSource.password" = ""
|
||||
}
|
@ -15,10 +15,10 @@ import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import com.r3.corda.networkmanage.HsmSimulator
|
||||
import com.r3.corda.networkmanage.hsm.authentication.InputReader
|
||||
import com.r3.corda.networkmanage.hsm.configuration.AuthenticationParameters
|
||||
import com.r3.corda.networkmanage.hsm.configuration.DoormanCertificateParameters
|
||||
import com.r3.corda.networkmanage.hsm.configuration.NetworkMapCertificateParameters
|
||||
import com.r3.corda.networkmanage.hsm.configuration.Parameters
|
||||
import com.r3.corda.networkmanage.hsm.configuration.AuthParametersConfig
|
||||
import com.r3.corda.networkmanage.hsm.configuration.DoormanCertificateConfig
|
||||
import com.r3.corda.networkmanage.hsm.configuration.NetworkMapCertificateConfig
|
||||
import com.r3.corda.networkmanage.hsm.configuration.SigningServiceConfig
|
||||
import com.r3.corda.networkmanage.hsm.generator.CertificateConfiguration
|
||||
import com.r3.corda.networkmanage.hsm.generator.GeneratorParameters
|
||||
import com.r3.corda.networkmanage.hsm.generator.UserAuthenticationParameters
|
||||
@ -127,26 +127,26 @@ abstract class HsmBaseTest {
|
||||
), hsmUserConfigs)
|
||||
}
|
||||
|
||||
protected fun createHsmSigningServiceConfig(): Parameters {
|
||||
return Parameters(
|
||||
protected fun createHsmSigningServiceConfig(): SigningServiceConfig {
|
||||
return SigningServiceConfig(
|
||||
dataSourceProperties = mock(),
|
||||
device = "${hsmSimulator.port}@${hsmSimulator.host}",
|
||||
keySpecifier = 1,
|
||||
doorman = DoormanCertificateParameters(
|
||||
doorman = DoormanCertificateConfig(
|
||||
rootKeyStoreFile = rootKeyStoreFile,
|
||||
keyGroup = DOORMAN_CERT_KEY_GROUP,
|
||||
validDays = 3650,
|
||||
rootKeyStorePassword = TRUSTSTORE_PASSWORD,
|
||||
crlDistributionPoint = "http://test.com/revoked.crl",
|
||||
authParameters = AuthenticationParameters(
|
||||
authParameters = AuthParametersConfig(
|
||||
mode = SigningServiceAuthMode.PASSWORD,
|
||||
threshold = 2
|
||||
)
|
||||
),
|
||||
networkMap = NetworkMapCertificateParameters(
|
||||
networkMap = NetworkMapCertificateConfig(
|
||||
username = "INTEGRATION_TEST",
|
||||
keyGroup = NETWORK_MAP_CERT_KEY_GROUP,
|
||||
authParameters = AuthenticationParameters(
|
||||
authParameters = AuthParametersConfig(
|
||||
mode = SigningServiceAuthMode.PASSWORD,
|
||||
password = "INTEGRATION_TEST",
|
||||
threshold = 2
|
||||
|
@ -10,10 +10,9 @@
|
||||
|
||||
package com.r3.corda.networkmanage.common.utils
|
||||
|
||||
import com.google.common.base.CaseFormat
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import joptsimple.ArgumentAcceptingOptionSpec
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import com.typesafe.config.ConfigRenderOptions
|
||||
import joptsimple.OptionParser
|
||||
import net.corda.core.CordaOID
|
||||
import net.corda.core.crypto.sha256
|
||||
@ -22,6 +21,7 @@ import net.corda.core.internal.SignedDataWithCert
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.network.NetworkMap
|
||||
@ -32,6 +32,9 @@ import org.bouncycastle.asn1.ASN1Encodable
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.nio.file.Path
|
||||
import java.security.KeyPair
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
@ -40,6 +43,8 @@ import java.security.cert.X509Certificate
|
||||
|
||||
const val CORDA_NETWORK_MAP = "cordanetworkmap"
|
||||
|
||||
val logger: Logger = LoggerFactory.getLogger("com.r3.corda.networkmanage.common.utils")
|
||||
|
||||
// TODO These should be defined in node-api
|
||||
typealias SignedNetworkParameters = SignedDataWithCert<NetworkParameters>
|
||||
typealias SignedNetworkMap = SignedDataWithCert<NetworkMap>
|
||||
@ -54,20 +59,10 @@ data class CertPathAndKey(val certPath: List<X509Certificate>, val key: PrivateK
|
||||
*/
|
||||
fun PublicKey.hashString() = encoded.sha256().toString()
|
||||
|
||||
fun Array<out String>.toConfigWithOptions(registerOptions: OptionParser.() -> Unit): Config {
|
||||
val parser = OptionParser()
|
||||
val helpOption = parser.acceptsAll(listOf("h", "?", "help"), "show help").forHelp()
|
||||
registerOptions(parser)
|
||||
val optionSet = parser.parse(*this)
|
||||
// Print help and exit on help option.
|
||||
if (optionSet.has(helpOption)) {
|
||||
throw ShowHelpException(parser)
|
||||
}
|
||||
// Convert all command line options to Config.
|
||||
return ConfigFactory.parseMap(parser.recognizedOptions().mapValues {
|
||||
val optionSpec = it.value
|
||||
if (optionSpec is ArgumentAcceptingOptionSpec<*> && !optionSpec.requiresArgument() && optionSet.has(optionSpec)) true else optionSpec.value(optionSet)
|
||||
}.mapKeys { it.key.toCamelcase() }.filterValues { it != null })
|
||||
inline fun <reified T : Any> parseConfig(file: Path): T {
|
||||
val config = ConfigFactory.parseFile(file.toFile(), ConfigParseOptions.defaults().setAllowMissing(true)).resolve()
|
||||
logger.info(config.root().render(ConfigRenderOptions.defaults()))
|
||||
return config.parseAs(strict = false)
|
||||
}
|
||||
|
||||
class ShowHelpException(val parser: OptionParser, val errorMessage: String? = null) : Exception()
|
||||
@ -87,12 +82,6 @@ fun initialiseSerialization() {
|
||||
context)
|
||||
}
|
||||
|
||||
private fun String.toCamelcase(): String {
|
||||
return if (contains('_') || contains('-')) {
|
||||
CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, this.replace("-", "_"))
|
||||
} else this
|
||||
}
|
||||
|
||||
private fun PKCS10CertificationRequest.firstAttributeValue(identifier: ASN1ObjectIdentifier): ASN1Encodable? {
|
||||
return getAttributes(identifier).firstOrNull()?.attrValues?.firstOrNull()
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
package com.r3.corda.networkmanage.doorman
|
||||
|
||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import com.typesafe.config.ConfigRenderOptions
|
||||
import joptsimple.OptionParser
|
||||
import joptsimple.util.EnumConverter
|
||||
import joptsimple.util.PathConverter
|
||||
import joptsimple.util.PathProperties
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import java.nio.file.Path
|
||||
|
||||
class DoormanArgsParser {
|
||||
private val optionParser = OptionParser()
|
||||
private val helpOption = optionParser.acceptsAll(listOf("h", "help"), "show help").forHelp()
|
||||
private val configFileArg = optionParser
|
||||
.accepts("config-file", "The path to the config file")
|
||||
.withRequiredArg()
|
||||
.withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING))
|
||||
.required()
|
||||
private val modeArg = optionParser
|
||||
.accepts("mode", "Set the mode of this application")
|
||||
.withRequiredArg()
|
||||
.withValuesConvertedBy(object : EnumConverter<Mode>(Mode::class.java) {})
|
||||
.defaultsTo(Mode.DOORMAN)
|
||||
private val updateNetworkParametersArg = optionParser
|
||||
.accepts("update-network-parameters", "Update network parameters file. Currently only network parameters initialisation is supported.")
|
||||
.withRequiredArg()
|
||||
.withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING))
|
||||
private val trustStorePasswordArg = optionParser
|
||||
.accepts("trust-store-password", "Password for the generated network root trust store. Only required when operating in ${Mode.ROOT_KEYGEN} mode.")
|
||||
.withRequiredArg()
|
||||
|
||||
fun parse(vararg args: String): DoormanCmdLineOptions {
|
||||
val optionSet = optionParser.parse(*args)
|
||||
if (optionSet.has(helpOption)) {
|
||||
throw ShowHelpException(optionParser)
|
||||
}
|
||||
val configFile = optionSet.valueOf(configFileArg)
|
||||
val mode = optionSet.valueOf(modeArg)
|
||||
val networkParametersFile = optionSet.valueOf(updateNetworkParametersArg)
|
||||
val trustStorePassword = optionSet.valueOf(trustStorePasswordArg)
|
||||
return DoormanCmdLineOptions(configFile, mode, networkParametersFile, trustStorePassword)
|
||||
}
|
||||
}
|
||||
|
||||
data class DoormanCmdLineOptions(val configFile: Path, val mode: Mode, val networkParametersFile: Path?, val trustStorePassword: String?) {
|
||||
init {
|
||||
// Make sure trust store password is only specified in root keygen mode.
|
||||
if (mode != Mode.ROOT_KEYGEN) {
|
||||
require(trustStorePassword == null) { "Trust store password should not be specified in this mode." }
|
||||
}
|
||||
}
|
||||
}
|
@ -10,31 +10,21 @@
|
||||
|
||||
package com.r3.corda.networkmanage.doorman
|
||||
|
||||
import com.r3.corda.networkmanage.common.utils.toConfigWithOptions
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.isRegularFile
|
||||
import com.google.common.primitives.Booleans
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
|
||||
data class NetworkManagementServerConfig( // TODO: Move local signing to signing server.
|
||||
val host: String,
|
||||
val port: Int,
|
||||
val address: NetworkHostAndPort,
|
||||
val dataSourceProperties: Properties,
|
||||
val database: DatabaseConfig = DatabaseConfig(),
|
||||
val mode: Mode,
|
||||
|
||||
val doorman: DoormanConfig?,
|
||||
val networkMap: NetworkMapConfig?,
|
||||
|
||||
val updateNetworkParameters: Path?,
|
||||
val trustStorePassword: String?,
|
||||
|
||||
// TODO Should be part of a localSigning sub-config
|
||||
val keystorePath: Path? = null,
|
||||
// TODO Should be part of a localSigning sub-config
|
||||
@ -52,20 +42,20 @@ data class NetworkManagementServerConfig( // TODO: Move local signing to signing
|
||||
val DEFAULT_APPROVE_INTERVAL = 5.seconds
|
||||
val DEFAULT_SIGN_INTERVAL = 5.seconds
|
||||
}
|
||||
|
||||
init {
|
||||
if (updateNetworkParameters != null) {
|
||||
check(updateNetworkParameters.isRegularFile()) { "Update network parameters file $updateNetworkParameters does not exist" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class DoormanConfig(val approveAll: Boolean = false,
|
||||
val jira: JiraConfig? = null,
|
||||
val approveInterval: Long = NetworkManagementServerConfig.DEFAULT_APPROVE_INTERVAL.toMillis())
|
||||
val approveInterval: Long = NetworkManagementServerConfig.DEFAULT_APPROVE_INTERVAL.toMillis()) {
|
||||
init {
|
||||
require(Booleans.countTrue(approveAll, jira != null) == 1) {
|
||||
"Either 'approveAll' or 'jira' config settings need to be specified but not both"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class NetworkMapConfig(val cacheTimeout: Long,
|
||||
// TODO: Move signing to signing server.
|
||||
// TODO: Move signing to signing server.
|
||||
val signInterval: Long = NetworkManagementServerConfig.DEFAULT_SIGN_INTERVAL.toMillis())
|
||||
|
||||
enum class Mode {
|
||||
@ -81,43 +71,3 @@ data class JiraConfig(
|
||||
val username: String,
|
||||
val password: String
|
||||
)
|
||||
|
||||
/**
|
||||
* Parses the doorman command line options.
|
||||
*/
|
||||
fun parseParameters(vararg args: String): NetworkManagementServerConfig {
|
||||
val argConfig = args.toConfigWithOptions {
|
||||
accepts("config-file", "The path to the config file")
|
||||
.withRequiredArg()
|
||||
.describedAs("filepath")
|
||||
accepts("update-network-parameters", "Update network parameters filepath. Currently only network parameters initialisation is supported.")
|
||||
.withRequiredArg()
|
||||
.describedAs("filepath")
|
||||
accepts("mode", "Set the mode of this application")
|
||||
.withRequiredArg()
|
||||
.defaultsTo(Mode.DOORMAN.name)
|
||||
accepts("trust-store-password", "Password for generated network root trust store. Only required when operating in root keygen mode.")
|
||||
.withRequiredArg()
|
||||
.describedAs("password")
|
||||
}
|
||||
|
||||
// The config-file option is changed to configFile
|
||||
val configFile = if (argConfig.hasPath("configFile")) {
|
||||
Paths.get(argConfig.getString("configFile"))
|
||||
} else {
|
||||
// TODO Remove this default
|
||||
Paths.get(".") / "network-management.conf"
|
||||
}
|
||||
require(configFile.isRegularFile()) { "Config file $configFile does not exist" }
|
||||
|
||||
val config = argConfig.withFallback(ConfigFactory.parseFile(configFile.toFile(), ConfigParseOptions.defaults().setAllowMissing(true)))
|
||||
.resolve()
|
||||
.parseAs<NetworkManagementServerConfig>(false)
|
||||
|
||||
// Make sure trust store password is only specified in root keygen mode.
|
||||
if (config.mode != Mode.ROOT_KEYGEN) {
|
||||
require(config.trustStorePassword == null) { "Trust store password should not be specified in this mode." }
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
@ -15,32 +15,35 @@ import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||
import com.r3.corda.networkmanage.common.utils.*
|
||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.time.Instant
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
private val logger = LoggerFactory.getLogger("com.r3.corda.networkmanage.doorman")
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
if (Manifests.exists("Doorman-Version")) {
|
||||
println("Version: ${Manifests.read("Doorman-Version")}")
|
||||
}
|
||||
|
||||
val parameters = try {
|
||||
parseParameters(*args)
|
||||
val cmdLineOptions = try {
|
||||
DoormanArgsParser().parse(*args)
|
||||
} catch (e: ShowHelpException) {
|
||||
e.errorMessage?.let(::println)
|
||||
e.parser.printHelpOn(System.out)
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
// TODO Use the logger for this and elsewhere in this file.
|
||||
println("Running in ${parameters.mode} mode")
|
||||
when (parameters.mode) {
|
||||
Mode.ROOT_KEYGEN -> parameters.rootKeyGenMode()
|
||||
Mode.CA_KEYGEN -> parameters.caKeyGenMode()
|
||||
Mode.DOORMAN -> parameters.doormanMode()
|
||||
val config = parseConfig<NetworkManagementServerConfig>(cmdLineOptions.configFile)
|
||||
|
||||
logger.info("Running in ${cmdLineOptions.mode} mode")
|
||||
when (cmdLineOptions.mode) {
|
||||
Mode.ROOT_KEYGEN -> rootKeyGenMode(cmdLineOptions, config)
|
||||
Mode.CA_KEYGEN -> caKeyGenMode(config)
|
||||
Mode.DOORMAN -> doormanMode(cmdLineOptions, config)
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,48 +63,48 @@ private fun processKeyStore(config: NetworkManagementServerConfig): Pair<CertPat
|
||||
return Pair(csrCertPathAndKey, networkMapSigner)
|
||||
}
|
||||
|
||||
private fun NetworkManagementServerConfig.rootKeyGenMode() {
|
||||
private fun rootKeyGenMode(cmdLineOptions: DoormanCmdLineOptions, config: NetworkManagementServerConfig) {
|
||||
generateRootKeyPair(
|
||||
rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"),
|
||||
rootKeystorePassword,
|
||||
rootPrivateKeyPassword,
|
||||
trustStorePassword
|
||||
config.rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"),
|
||||
config.rootKeystorePassword,
|
||||
config.rootPrivateKeyPassword,
|
||||
cmdLineOptions.trustStorePassword
|
||||
)
|
||||
}
|
||||
|
||||
private fun NetworkManagementServerConfig.caKeyGenMode() {
|
||||
private fun caKeyGenMode(config: NetworkManagementServerConfig) {
|
||||
generateSigningKeyPairs(
|
||||
keystorePath ?: throw IllegalArgumentException("The 'keystorePath' parameter must be specified when generating keys!"),
|
||||
rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"),
|
||||
rootKeystorePassword,
|
||||
rootPrivateKeyPassword,
|
||||
keystorePassword,
|
||||
caPrivateKeyPassword
|
||||
config.keystorePath ?: throw IllegalArgumentException("The 'keystorePath' parameter must be specified when generating keys!"),
|
||||
config.rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"),
|
||||
config.rootKeystorePassword,
|
||||
config.rootPrivateKeyPassword,
|
||||
config.keystorePassword,
|
||||
config.caPrivateKeyPassword
|
||||
)
|
||||
}
|
||||
|
||||
private fun NetworkManagementServerConfig.doormanMode() {
|
||||
private fun doormanMode(cmdLineOptions: DoormanCmdLineOptions, config: NetworkManagementServerConfig) {
|
||||
initialiseSerialization()
|
||||
val persistence = configureDatabase(dataSourceProperties, database)
|
||||
val persistence = configureDatabase(config.dataSourceProperties, config.database)
|
||||
// TODO: move signing to signing server.
|
||||
val csrAndNetworkMap = processKeyStore(this)
|
||||
val csrAndNetworkMap = processKeyStore(config)
|
||||
|
||||
if (csrAndNetworkMap != null) {
|
||||
println("Starting network management services with local signing")
|
||||
logger.info("Starting network management services with local signing")
|
||||
}
|
||||
|
||||
val networkManagementServer = NetworkManagementServer()
|
||||
val networkParameters = updateNetworkParameters?.let {
|
||||
val networkParameters = cmdLineOptions.networkParametersFile?.let {
|
||||
// TODO This check shouldn't be needed. Fix up the config design.
|
||||
requireNotNull(networkMap) { "'networkMap' config is required for applying network parameters" }
|
||||
println("Parsing network parameters from '${it.toAbsolutePath()}'...")
|
||||
requireNotNull(config.networkMap) { "'networkMap' config is required for applying network parameters" }
|
||||
logger.info("Parsing network parameters from '${it.toAbsolutePath()}'...")
|
||||
parseNetworkParametersConfig(it).toNetworkParameters(modifiedTime = Instant.now(), epoch = 1)
|
||||
}
|
||||
val networkMapStartParams = networkMap?.let {
|
||||
val networkMapStartParams = config.networkMap?.let {
|
||||
NetworkMapStartParams(csrAndNetworkMap?.second, networkParameters, it)
|
||||
}
|
||||
|
||||
networkManagementServer.start(NetworkHostAndPort(host, port), persistence, csrAndNetworkMap?.first, doorman, networkMapStartParams)
|
||||
networkManagementServer.start(config.address, persistence, csrAndNetworkMap?.first, config.doorman, networkMapStartParams)
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(thread(start = false) {
|
||||
networkManagementServer.close()
|
||||
|
@ -43,6 +43,7 @@ class NetworkManagementServer : Closeable {
|
||||
lateinit var hostAndPort: NetworkHostAndPort
|
||||
|
||||
override fun close() {
|
||||
logger.info("Closing server...")
|
||||
for (closeAction in closeActions) {
|
||||
try {
|
||||
closeAction()
|
||||
@ -94,7 +95,6 @@ class NetworkManagementServer : Closeable {
|
||||
serverStatus: NetworkManagementServerStatus): RegistrationWebService {
|
||||
logger.info("Starting Doorman server.")
|
||||
val requestService = if (config.approveAll) {
|
||||
require(config.jira == null) { "Jira configuration cannot be specified when the approveAll parameter is set to true." }
|
||||
logger.warn("Doorman server is in 'Approve All' mode, this will approve all incoming certificate signing requests.")
|
||||
ApproveAllCertificateSigningRequestStorage(PersistentCertificateSigningRequestStorage(database))
|
||||
} else {
|
||||
|
@ -10,63 +10,56 @@
|
||||
|
||||
package com.r3.corda.networkmanage.hsm
|
||||
|
||||
import com.google.common.primitives.Booleans
|
||||
import com.jcabi.manifests.Manifests
|
||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||
import com.r3.corda.networkmanage.common.utils.initialiseSerialization
|
||||
import com.r3.corda.networkmanage.hsm.configuration.parseParameters
|
||||
import com.r3.corda.networkmanage.common.utils.parseConfig
|
||||
import com.r3.corda.networkmanage.hsm.configuration.SigningServiceArgsParser
|
||||
import com.r3.corda.networkmanage.hsm.configuration.SigningServiceConfig
|
||||
import com.r3.corda.networkmanage.hsm.processor.CsrProcessor
|
||||
import com.r3.corda.networkmanage.hsm.processor.NetworkMapProcessor
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import java.security.Security
|
||||
import javax.crypto.Cipher
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
private val logger = LogManager.getLogger("com.r3.corda.networkmanage.hsm.Main")
|
||||
|
||||
private fun logServiceVersion() {
|
||||
fun main(args: Array<String>) {
|
||||
if (Manifests.exists("Signing-Service-Version")) {
|
||||
println("Signing Service Version: ${Manifests.read("Signing-Service-Version")}")
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
logServiceVersion()
|
||||
parseParameters(*args).run {
|
||||
try {
|
||||
// Validate
|
||||
// Grabbed from https://stackoverflow.com/questions/7953567/checking-if-unlimited-cryptography-is-available
|
||||
require(Cipher.getMaxAllowedKeyLength("AES") >= 256) {
|
||||
"Unlimited Strength Jurisdiction Policy Files must be installed, see http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html"
|
||||
}
|
||||
require(Booleans.countTrue(doorman != null, networkMap != null) == 1) {
|
||||
"Exactly one networkMap or doorman configuration needs to be specified."
|
||||
}
|
||||
requireNotNull(dataSourceProperties)
|
||||
val cmdLineOptions = try {
|
||||
SigningServiceArgsParser().parse(*args)
|
||||
} catch (e: ShowHelpException) {
|
||||
e.errorMessage?.let(::println)
|
||||
e.parser.printHelpOn(System.out)
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
// Ensure the BouncyCastle provider is installed
|
||||
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
|
||||
Security.addProvider(BouncyCastleProvider())
|
||||
}
|
||||
val config = parseConfig<SigningServiceConfig>(cmdLineOptions.configFile)
|
||||
|
||||
initialiseSerialization()
|
||||
// Create DB connection.
|
||||
val persistence = configureDatabase(dataSourceProperties, database)
|
||||
if (networkMap != null) {
|
||||
NetworkMapProcessor(networkMap, device, keySpecifier, persistence).run()
|
||||
} else {
|
||||
try {
|
||||
CsrProcessor(doorman!!, device, keySpecifier, persistence).showMenu()
|
||||
} catch (e: ShowHelpException) {
|
||||
e.errorMessage?.let(::println)
|
||||
e.parser.printHelpOn(System.out)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.error("Error while starting the HSM Signing service.", e)
|
||||
}
|
||||
// Validate
|
||||
// Grabbed from https://stackoverflow.com/questions/7953567/checking-if-unlimited-cryptography-is-available
|
||||
require(Cipher.getMaxAllowedKeyLength("AES") >= 256) {
|
||||
"Unlimited Strength Jurisdiction Policy Files must be installed, see http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html"
|
||||
}
|
||||
|
||||
// Ensure the BouncyCastle provider is installed
|
||||
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
|
||||
Security.addProvider(BouncyCastleProvider())
|
||||
}
|
||||
|
||||
initialiseSerialization()
|
||||
|
||||
// Create DB connection.
|
||||
val persistence = configureDatabase(config.dataSourceProperties, config.database)
|
||||
if (config.networkMap != null) {
|
||||
NetworkMapProcessor(config.networkMap, config.device, config.keySpecifier, persistence).run()
|
||||
} else {
|
||||
CsrProcessor(config.doorman!!, config.device, config.keySpecifier, persistence).showMenu()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,87 +0,0 @@
|
||||
/*
|
||||
* R3 Proprietary and Confidential
|
||||
*
|
||||
* Copyright (c) 2018 R3 Limited. All rights reserved.
|
||||
*
|
||||
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
|
||||
*
|
||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||
*/
|
||||
|
||||
package com.r3.corda.networkmanage.hsm.configuration
|
||||
|
||||
import com.r3.corda.networkmanage.common.utils.toConfigWithOptions
|
||||
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.isRegularFile
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Configuration parameters. Those are general configuration parameters shared with both
|
||||
* network map and certificate signing requests processes.
|
||||
*/
|
||||
data class Parameters(val dataSourceProperties: Properties,
|
||||
val database: DatabaseConfig = DatabaseConfig(),
|
||||
val device: String,
|
||||
val keySpecifier: Int,
|
||||
val networkMap: NetworkMapCertificateParameters? = null,
|
||||
val doorman: DoormanCertificateParameters? = null)
|
||||
|
||||
/**
|
||||
* Network map signing process specific parameters.
|
||||
*/
|
||||
data class NetworkMapCertificateParameters(val username: String,
|
||||
val keyGroup: String,
|
||||
val authParameters: AuthenticationParameters)
|
||||
|
||||
/**
|
||||
* Certificate signing requests process specific parameters.
|
||||
*/
|
||||
data class DoormanCertificateParameters(val crlDistributionPoint: String,
|
||||
val keyGroup:String,
|
||||
val validDays: Int,
|
||||
val rootKeyStoreFile: Path,
|
||||
val rootKeyStorePassword: String,
|
||||
val authParameters: AuthenticationParameters) {
|
||||
fun loadRootKeyStore(createNew: Boolean = false): X509KeyStore {
|
||||
return X509KeyStore.fromFile(rootKeyStoreFile, rootKeyStorePassword, createNew)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication related parameters.
|
||||
*/
|
||||
data class AuthenticationParameters(val mode: AuthMode,
|
||||
val password: String? = null, // This is either HSM password or key file password, depending on the mode.
|
||||
val keyFilePath: Path? = null,
|
||||
val threshold: Int)
|
||||
|
||||
/**
|
||||
* Parses the list of arguments and produces an instance of [Parameters].
|
||||
* @param args list of strings corresponding to program arguments
|
||||
* @return instance of Parameters produced from [args]
|
||||
*/
|
||||
fun parseParameters(vararg args: String): Parameters {
|
||||
val argConfig = args.toConfigWithOptions {
|
||||
accepts("basedir", "Overriding configuration filepath, default to current directory.").withRequiredArg().defaultsTo(".").describedAs("filepath")
|
||||
accepts("config-file", "Overriding configuration file.").withRequiredArg().describedAs("filepath")
|
||||
}
|
||||
|
||||
// The config-file option is changed to configFile
|
||||
val configFile = if (argConfig.hasPath("configFile")) {
|
||||
Paths.get(argConfig.getString("configFile"))
|
||||
} else {
|
||||
Paths.get(argConfig.getString("basedir")) / "signing_service.conf"
|
||||
}
|
||||
require(configFile.isRegularFile()) { "Config file $configFile does not exist" }
|
||||
|
||||
val config = argConfig.withFallback(ConfigFactory.parseFile(configFile.toFile(), ConfigParseOptions.defaults().setAllowMissing(true))).resolve()
|
||||
return config.parseAs(false)
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* R3 Proprietary and Confidential
|
||||
*
|
||||
* Copyright (c) 2018 R3 Limited. All rights reserved.
|
||||
*
|
||||
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
|
||||
*
|
||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||
*/
|
||||
|
||||
package com.r3.corda.networkmanage.hsm.configuration
|
||||
|
||||
import com.google.common.primitives.Booleans
|
||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import com.typesafe.config.ConfigRenderOptions
|
||||
import joptsimple.OptionParser
|
||||
import joptsimple.util.PathConverter
|
||||
import joptsimple.util.PathProperties
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Configuration parameters. Those are general configuration parameters shared with both
|
||||
* network map and certificate signing requests processes.
|
||||
*/
|
||||
data class SigningServiceConfig(val dataSourceProperties: Properties,
|
||||
val database: DatabaseConfig = DatabaseConfig(),
|
||||
val device: String,
|
||||
val keySpecifier: Int,
|
||||
val networkMap: NetworkMapCertificateConfig? = null,
|
||||
val doorman: DoormanCertificateConfig? = null) {
|
||||
init {
|
||||
require(Booleans.countTrue(doorman != null, networkMap != null) == 1) {
|
||||
"Exactly one networkMap or doorman configuration needs to be specified."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network map signing process specific parameters.
|
||||
*/
|
||||
data class NetworkMapCertificateConfig(val username: String,
|
||||
val keyGroup: String,
|
||||
val authParameters: AuthParametersConfig)
|
||||
|
||||
/**
|
||||
* Certificate signing requests process specific parameters.
|
||||
*/
|
||||
data class DoormanCertificateConfig(val crlDistributionPoint: String,
|
||||
val keyGroup:String,
|
||||
val validDays: Int,
|
||||
val rootKeyStoreFile: Path,
|
||||
val rootKeyStorePassword: String,
|
||||
val authParameters: AuthParametersConfig) {
|
||||
fun loadRootKeyStore(createNew: Boolean = false): X509KeyStore {
|
||||
return X509KeyStore.fromFile(rootKeyStoreFile, rootKeyStorePassword, createNew)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication related parameters.
|
||||
*/
|
||||
data class AuthParametersConfig(val mode: AuthMode,
|
||||
val password: String? = null, // This is either HSM password or key file password, depending on the mode.
|
||||
val keyFilePath: Path? = null,
|
||||
val threshold: Int)
|
||||
|
||||
class SigningServiceArgsParser {
|
||||
private val optionParser = OptionParser()
|
||||
private val helpOption = optionParser.acceptsAll(listOf("h", "help"), "show help").forHelp()
|
||||
private val baseDirArg = optionParser
|
||||
.accepts("basedir", "Overriding configuration filepath, default to current directory.")
|
||||
.withRequiredArg()
|
||||
.withValuesConvertedBy(PathConverter(PathProperties.DIRECTORY_EXISTING))
|
||||
.defaultsTo(Paths.get("."))
|
||||
private val configFileArg = optionParser
|
||||
.accepts("config-file", "The path to the config file")
|
||||
.withRequiredArg()
|
||||
.withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING))
|
||||
|
||||
fun parse(vararg args: String): SigningServiceCmdLineOptions {
|
||||
val optionSet = optionParser.parse(*args)
|
||||
if (optionSet.has(helpOption)) {
|
||||
throw ShowHelpException(optionParser)
|
||||
}
|
||||
val baseDir = optionSet.valueOf(baseDirArg)
|
||||
val configFile = optionSet.valueOf(configFileArg) ?: baseDir / "signing_service.conf"
|
||||
return SigningServiceCmdLineOptions(baseDir, configFile)
|
||||
}
|
||||
}
|
||||
|
||||
data class SigningServiceCmdLineOptions(val baseDir: Path, val configFile: Path)
|
@ -12,7 +12,7 @@ package com.r3.corda.networkmanage.hsm.processor
|
||||
|
||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||
import com.r3.corda.networkmanage.hsm.configuration.DoormanCertificateParameters
|
||||
import com.r3.corda.networkmanage.hsm.configuration.DoormanCertificateConfig
|
||||
import com.r3.corda.networkmanage.hsm.menu.Menu
|
||||
import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
|
||||
import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStorage
|
||||
@ -21,7 +21,7 @@ import com.r3.corda.networkmanage.hsm.utils.mapCryptoServerException
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
|
||||
class CsrProcessor(private val parameters: DoormanCertificateParameters,
|
||||
class CsrProcessor(private val config: DoormanCertificateConfig,
|
||||
private val device: String,
|
||||
private val keySpecifier: Int,
|
||||
private val database: CordaPersistence) {
|
||||
@ -29,12 +29,12 @@ class CsrProcessor(private val parameters: DoormanCertificateParameters,
|
||||
val logger = contextLogger()
|
||||
}
|
||||
|
||||
private val auth = parameters.authParameters
|
||||
private val auth = config.authParameters
|
||||
|
||||
fun showMenu() {
|
||||
val csrStorage = DBSignedCertificateRequestStorage(database)
|
||||
val sign: (List<ApprovedCertificateRequestData>) -> Unit = {
|
||||
val signer = parameters.run {
|
||||
val signer = config.run {
|
||||
HsmCsrSigner(
|
||||
csrStorage,
|
||||
loadRootKeyStore(),
|
||||
@ -42,7 +42,7 @@ class CsrProcessor(private val parameters: DoormanCertificateParameters,
|
||||
null,
|
||||
validDays,
|
||||
Authenticator(
|
||||
provider = createProvider(parameters.keyGroup, keySpecifier, device),
|
||||
provider = createProvider(config.keyGroup, keySpecifier, device),
|
||||
mode = auth.mode,
|
||||
authStrengthThreshold = auth.threshold))
|
||||
}
|
||||
|
@ -15,12 +15,12 @@ import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
||||
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
|
||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||
import com.r3.corda.networkmanage.hsm.configuration.NetworkMapCertificateParameters
|
||||
import com.r3.corda.networkmanage.hsm.configuration.NetworkMapCertificateConfig
|
||||
import com.r3.corda.networkmanage.hsm.signer.HsmSigner
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
|
||||
class NetworkMapProcessor(private val parameters: NetworkMapCertificateParameters,
|
||||
class NetworkMapProcessor(private val config: NetworkMapCertificateConfig,
|
||||
private val device: String,
|
||||
private val keySpecifier: Int,
|
||||
private val database: CordaPersistence) {
|
||||
@ -29,7 +29,7 @@ class NetworkMapProcessor(private val parameters: NetworkMapCertificateParameter
|
||||
}
|
||||
|
||||
init {
|
||||
parameters.authParameters.run {
|
||||
config.authParameters.run {
|
||||
requireNotNull(password)
|
||||
require(mode != AuthMode.CARD_READER)
|
||||
if (mode == AuthMode.KEY_FILE) {
|
||||
@ -40,7 +40,7 @@ class NetworkMapProcessor(private val parameters: NetworkMapCertificateParameter
|
||||
|
||||
fun run() {
|
||||
logger.info("Starting network map processor.")
|
||||
parameters.run {
|
||||
config.run {
|
||||
val networkMapStorage = PersistentNetworkMapStorage(database)
|
||||
val signer = HsmSigner(
|
||||
Authenticator(
|
||||
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* R3 Proprietary and Confidential
|
||||
*
|
||||
* Copyright (c) 2018 R3 Limited. All rights reserved.
|
||||
*
|
||||
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
|
||||
*
|
||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||
*/
|
||||
|
||||
package com.r3.corda.networkmanage.doorman
|
||||
|
||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||
import joptsimple.OptionException
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class DoormanArgsParserTest {
|
||||
private val validConfigPath = File("doorman.conf").absolutePath
|
||||
private val argsParser = DoormanArgsParser()
|
||||
|
||||
@Test
|
||||
fun `should fail when network parameters file is missing`() {
|
||||
assertThatThrownBy {
|
||||
argsParser.parse("--config-file", validConfigPath, "--update-network-parameters", "not-here")
|
||||
}.hasMessageContaining("not-here")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail when config file is missing`() {
|
||||
assertThatThrownBy {
|
||||
argsParser.parse("--config-file", "not-existing-file")
|
||||
}.hasMessageContaining("not-existing-file")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should throw ShowHelpException when help option is passed on the command line`() {
|
||||
assertFailsWith<ShowHelpException> {
|
||||
argsParser.parse("-h")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse trust store password correctly`() {
|
||||
val parameter = argsParser.parse("--config-file", validConfigPath, "--mode", "ROOT_KEYGEN", "--trust-store-password", "testPassword")
|
||||
assertEquals("testPassword", parameter.trustStorePassword)
|
||||
|
||||
assertFailsWith<OptionException> {
|
||||
argsParser.parse("--trust-store-password")
|
||||
}
|
||||
|
||||
// Should fail if password is provided in mode other then root keygen.
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
argsParser.parse("--config-file", validConfigPath, "--trust-store-password", "testPassword")
|
||||
}
|
||||
|
||||
// trust store password is optional.
|
||||
assertNull(argsParser.parse("--config-file", validConfigPath).trustStorePassword)
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* R3 Proprietary and Confidential
|
||||
*
|
||||
* Copyright (c) 2018 R3 Limited. All rights reserved.
|
||||
*
|
||||
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
|
||||
*
|
||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||
*/
|
||||
|
||||
package com.r3.corda.networkmanage.doorman
|
||||
|
||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||
import com.typesafe.config.ConfigException
|
||||
import joptsimple.OptionException
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class DoormanParametersTest {
|
||||
private val validOverrideNetworkConfigPath = File("network-parameters.conf").absolutePath
|
||||
private val validConfigPath = File("doorman.conf").absolutePath
|
||||
private val invalidConfigPath = File(javaClass.getResource("/doorman_fail.conf").toURI()).absolutePath
|
||||
private val validArgs = arrayOf("--config-file", validConfigPath, "--update-network-parameters", validOverrideNetworkConfigPath)
|
||||
|
||||
@Test
|
||||
fun `should fail when initial network parameters file is missing`() {
|
||||
val message = assertFailsWith<InvocationTargetException> {
|
||||
parseParameters("--config-file", validConfigPath, "--update-network-parameters", "not-here")
|
||||
}.targetException.message
|
||||
assertThat(message).contains("Update network parameters file ")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail when config file is missing`() {
|
||||
val message = assertFailsWith<IllegalArgumentException> {
|
||||
parseParameters("--config-file", "not-existing-file")
|
||||
}.message
|
||||
assertThat(message).contains("Config file ")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should throw ShowHelpException when help option is passed on the command line`() {
|
||||
assertFailsWith<ShowHelpException> {
|
||||
parseParameters("-?")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail when config missing`() {
|
||||
assertFailsWith<ConfigException.Missing> {
|
||||
parseParameters("--config-file", invalidConfigPath)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse database config correctly`() {
|
||||
val parameter = parseParameters(*validArgs).database
|
||||
assertTrue(parameter.runMigration)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse trust store password correctly`() {
|
||||
val parameter = parseParameters("--config-file", validConfigPath, "--mode", "ROOT_KEYGEN", "--trust-store-password", "testPassword")
|
||||
assertEquals("testPassword", parameter.trustStorePassword)
|
||||
|
||||
assertFailsWith<OptionException> {
|
||||
parseParameters("--trust-store-password")
|
||||
}
|
||||
|
||||
// Should fail if password is provided in mode other then root keygen.
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
parseParameters("--config-file", validConfigPath, "--trust-store-password", "testPassword")
|
||||
}
|
||||
|
||||
// trust store password is optional.
|
||||
assertNull(parseParameters("--config-file", validConfigPath).trustStorePassword)
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/*
|
||||
* R3 Proprietary and Confidential
|
||||
*
|
||||
* Copyright (c) 2018 R3 Limited. All rights reserved.
|
||||
*
|
||||
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
|
||||
*
|
||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||
*/
|
||||
|
||||
package com.r3.corda.networkmanage.hsm.configuration
|
||||
|
||||
import com.r3.corda.networkmanage.TestBase
|
||||
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
|
||||
import com.typesafe.config.ConfigException
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class ConfigurationTest : TestBase() {
|
||||
private val validConfigPath = File("./hsm.conf").absolutePath
|
||||
private val invalidConfigPath = File(javaClass.getResource("/hsm_fail.conf").toURI()).absolutePath
|
||||
|
||||
@Test
|
||||
fun `config file is parsed correctly`() {
|
||||
val parameters = parseParameters("--config-file", validConfigPath)
|
||||
assertEquals("3001@192.168.0.1", parameters.device)
|
||||
val doormanCertParameters = parameters.doorman!!
|
||||
assertEquals(AuthMode.PASSWORD, doormanCertParameters.authParameters.mode)
|
||||
assertEquals(2, doormanCertParameters.authParameters.threshold)
|
||||
assertEquals(3650, doormanCertParameters.validDays)
|
||||
val nmParams = parameters.networkMap!!
|
||||
assertEquals(AuthMode.KEY_FILE, nmParams.authParameters.mode)
|
||||
assertEquals(Paths.get("./Administrator.KEY"), nmParams.authParameters.keyFilePath)
|
||||
assertEquals(2, nmParams.authParameters.threshold)
|
||||
assertEquals("PASSWORD", nmParams.authParameters.password)
|
||||
assertEquals("TEST_USERNAME", nmParams.username)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail when config missing database source properties`() {
|
||||
// dataSourceProperties is missing from node_fail.conf and it should fail during parsing, and shouldn't use default from reference.conf.
|
||||
assertFailsWith<ConfigException.Missing> {
|
||||
parseParameters("--config-file", invalidConfigPath)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail when config file is missing`() {
|
||||
val message = assertFailsWith<IllegalArgumentException> {
|
||||
com.r3.corda.networkmanage.doorman.parseParameters("--config-file", "not-existing-file")
|
||||
}.message
|
||||
Assertions.assertThat(message).contains("Config file ")
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* R3 Proprietary and Confidential
|
||||
*
|
||||
* Copyright (c) 2018 R3 Limited. All rights reserved.
|
||||
*
|
||||
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
|
||||
*
|
||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||
*/
|
||||
|
||||
package com.r3.corda.networkmanage.hsm.configuration
|
||||
|
||||
import com.r3.corda.networkmanage.TestBase
|
||||
import com.r3.corda.networkmanage.common.utils.parseConfig
|
||||
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class SigningServiceArgsParserTest : TestBase() {
|
||||
private val doormanConfigPath = File("./hsm-for-doorman.conf").absolutePath
|
||||
private val networkMapConfigPath = File("./hsm-for-networkmap.conf").absolutePath
|
||||
private val argsParser = SigningServiceArgsParser()
|
||||
|
||||
@Test
|
||||
fun `doorman-based config file is parsed correctly`() {
|
||||
val cmdLineOptions = argsParser.parse("--config-file", doormanConfigPath)
|
||||
val config = parseConfig<SigningServiceConfig>(cmdLineOptions.configFile)
|
||||
assertEquals("3001@192.168.0.1", config.device)
|
||||
val doormanCertParameters = config.doorman!!
|
||||
assertEquals(AuthMode.PASSWORD, doormanCertParameters.authParameters.mode)
|
||||
assertEquals(2, doormanCertParameters.authParameters.threshold)
|
||||
assertEquals(3650, doormanCertParameters.validDays)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `networkmap-based config file is parsed correctly`() {
|
||||
val cmdLineOptions = argsParser.parse("--config-file", networkMapConfigPath)
|
||||
val config = parseConfig<SigningServiceConfig>(cmdLineOptions.configFile)
|
||||
assertEquals("3001@192.168.0.1", config.device)
|
||||
val networkMapConfig = config.networkMap!!
|
||||
assertEquals(AuthMode.KEY_FILE, networkMapConfig.authParameters.mode)
|
||||
assertEquals(Paths.get("./Administrator.KEY"), networkMapConfig.authParameters.keyFilePath)
|
||||
assertEquals(2, networkMapConfig.authParameters.threshold)
|
||||
assertEquals("PASSWORD", networkMapConfig.authParameters.password)
|
||||
assertEquals("TEST_USERNAME", networkMapConfig.username)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail when config file is missing`() {
|
||||
assertThatThrownBy {
|
||||
argsParser.parse("--config-file", "not-existing-file")
|
||||
}.hasMessageContaining("not-existing-file")
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.lang.reflect.Modifier.isStatic
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.net.Proxy
|
||||
@ -74,19 +75,20 @@ fun <T : Any> Config.parseAs(clazz: KClass<T>, strict: Boolean = true): T {
|
||||
val path = defaultToOldPath(property)
|
||||
getValueInternal<Any>(path, param.type, strict)
|
||||
}
|
||||
return constructor.callBy(args)
|
||||
try {
|
||||
return constructor.callBy(args)
|
||||
} catch (e: InvocationTargetException) {
|
||||
throw e.cause!!
|
||||
}
|
||||
}
|
||||
|
||||
class UnknownConfigurationKeysException private constructor(val unknownKeys: Set<String>) : IllegalArgumentException(message(unknownKeys)) {
|
||||
|
||||
init {
|
||||
require(unknownKeys.isNotEmpty()) { "Absence of unknown keys should not raise UnknownConfigurationKeysException." }
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun of(offendingKeys: Set<String>): UnknownConfigurationKeysException = UnknownConfigurationKeysException(offendingKeys)
|
||||
|
||||
private fun message(offendingKeys: Set<String>) = "Unknown configuration keys: ${offendingKeys.joinToString(", ", "[", "]")}."
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user