ENT-6286: OS: Upgrade version of Artemis to 2.19.1 (#6975)

Fixes DDoS attack mentioned on the Jira ticket.

PR upgrades Artemis library to version 2.19.1.
This is our own release of Apache Artemis library which has vulnerability fix for v2.20 applied.

**_Breaking changes discovered during Artemis upgrade:_**
1. When the queue is created as temporary, it needs to explicitly be specified as non-durable.
2. By default, Artemis Client performs Host DNS name check against the certificate presented by the server. Our TLS certificates fail this check and this verification has to be explicitly disabled, see use of: `TransportConstants.VERIFY_HOST_PROP_NAME`.
3. Artemis Server now caches login attempts, even unsuccessful ones. When we add RPC users dynamically via DB insert this may have an unexpected outcome if the user with the same `userName` and `password` was not available previously.
To workaround permissions changing dynamically, authorization and authentication caches had to be disabled.
4. When computing `maxMessageSize`, the size of the headers content is now taken into account as well.
5. Artemis handling of start-up errors has changed. E.g. when the port is already bound.
6. A number of deprecated APIs like: `createTemporaryQueue`, `failoverOnInitialAttempt`, `NullOutputStream`, `CoreQueueConfiguration`.
7. Log warning message is produced like: `AMQ212080: Using legacy SSL store provider value: JKS. Please use either 'keyStoreType' or 'trustStoreType' instead as appropriate.`
8. As reported by QA, Artemis now produces more audit logging more details [here](https://r3-cev.atlassian.net/browse/ENT-6540). Log configuration been adjusted to reduce such output.
This commit is contained in:
Viktor Kolomeyko
2022-01-21 09:18:14 +00:00
committed by GitHub
parent b17e4571bf
commit 835321bb70
30 changed files with 271 additions and 162 deletions

View File

@ -66,7 +66,6 @@ class ArtemisMessagingClient(private val config: MutualSslConfiguration,
retryInterval = messagingServerConnectionConfig.retryInterval().toMillis()
retryIntervalMultiplier = messagingServerConnectionConfig.retryIntervalMultiplier()
maxRetryInterval = messagingServerConnectionConfig.maxRetryInterval(isHA).toMillis()
isFailoverOnInitialConnection = messagingServerConnectionConfig.failoverOnInitialAttempt(isHA)
initialConnectAttempts = messagingServerConnectionConfig.initialConnectAttempts(isHA)
}
addIncomingInterceptor(ArtemisMessageSizeChecksInterceptor(maxMessageSize))

View File

@ -73,27 +73,27 @@ class ArtemisTcpTransport {
private fun CertificateStore.toKeyStoreTransportOptions(path: Path) = mapOf(
TransportConstants.SSL_ENABLED_PROP_NAME to true,
TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS",
TransportConstants.KEYSTORE_TYPE_PROP_NAME to "JKS",
TransportConstants.KEYSTORE_PATH_PROP_NAME to path,
TransportConstants.KEYSTORE_PASSWORD_PROP_NAME to password,
TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to true)
private fun CertificateStore.toTrustStoreTransportOptions(path: Path) = mapOf(
TransportConstants.SSL_ENABLED_PROP_NAME to true,
TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME to "JKS",
TransportConstants.TRUSTSTORE_TYPE_PROP_NAME to "JKS",
TransportConstants.TRUSTSTORE_PATH_PROP_NAME to path,
TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to password,
TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to true)
private fun ClientRpcSslOptions.toTransportOptions() = mapOf(
TransportConstants.SSL_ENABLED_PROP_NAME to true,
TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME to trustStoreProvider,
TransportConstants.TRUSTSTORE_TYPE_PROP_NAME to trustStoreProvider,
TransportConstants.TRUSTSTORE_PATH_PROP_NAME to trustStorePath,
TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to trustStorePassword)
private fun BrokerRpcSslOptions.toTransportOptions() = mapOf(
TransportConstants.SSL_ENABLED_PROP_NAME to true,
TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS",
TransportConstants.KEYSTORE_TYPE_PROP_NAME to "JKS",
TransportConstants.KEYSTORE_PATH_PROP_NAME to keyStorePath,
TransportConstants.KEYSTORE_PASSWORD_PROP_NAME to keyStorePassword,
TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to false)
@ -106,9 +106,9 @@ class ArtemisTcpTransport {
return p2pAcceptorTcpTransport(hostAndPort, config?.keyStore, config?.trustStore, enableSSL = enableSSL, useOpenSsl = config?.useOpenSsl ?: false)
}
fun p2pConnectorTcpTransport(hostAndPort: NetworkHostAndPort, config: MutualSslConfiguration?, enableSSL: Boolean = true, keyStoreProvider: String? = null): TransportConfiguration {
fun p2pConnectorTcpTransport(hostAndPort: NetworkHostAndPort, config: MutualSslConfiguration?, enableSSL: Boolean = true, keyStoreType: String? = null): TransportConfiguration {
return p2pConnectorTcpTransport(hostAndPort, config?.keyStore, config?.trustStore, enableSSL = enableSSL, useOpenSsl = config?.useOpenSsl ?: false, keyStoreProvider = keyStoreProvider)
return p2pConnectorTcpTransport(hostAndPort, config?.keyStore, config?.trustStore, enableSSL = enableSSL, useOpenSsl = config?.useOpenSsl ?: false, keyStoreType = keyStoreType)
}
fun p2pAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, keyStore: FileBasedCertificateStoreSupplier?, trustStore: FileBasedCertificateStoreSupplier?, enableSSL: Boolean = true, useOpenSsl: Boolean = false): TransportConfiguration {
@ -124,20 +124,22 @@ class ArtemisTcpTransport {
}
@Suppress("LongParameterList")
fun p2pConnectorTcpTransport(hostAndPort: NetworkHostAndPort, keyStore: FileBasedCertificateStoreSupplier?, trustStore: FileBasedCertificateStoreSupplier?, enableSSL: Boolean = true, useOpenSsl: Boolean = false, keyStoreProvider: String? = null): TransportConfiguration {
fun p2pConnectorTcpTransport(hostAndPort: NetworkHostAndPort, keyStore: FileBasedCertificateStoreSupplier?, trustStore: FileBasedCertificateStoreSupplier?, enableSSL: Boolean = true, useOpenSsl: Boolean = false, keyStoreType: String? = null): TransportConfiguration {
val options = defaultArtemisOptions(hostAndPort, P2P_PROTOCOLS).toMutableMap()
if (enableSSL) {
options.putAll(defaultSSLOptions)
(keyStore to trustStore).addToTransportOptions(options)
options[TransportConstants.SSL_PROVIDER] = if (useOpenSsl) TransportConstants.OPENSSL_PROVIDER else TransportConstants.DEFAULT_SSL_PROVIDER
keyStoreProvider?.let { options.put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, keyStoreProvider) }
keyStoreType?.let { options.put(TransportConstants.KEYSTORE_TYPE_PROP_NAME, keyStoreType) }
// This is required to stop Client checking URL address vs. Server provided certificate
options[TransportConstants.VERIFY_HOST_PROP_NAME] = false
}
return TransportConfiguration(connectorFactoryClassName, options)
}
fun p2pConnectorTcpTransportFromList(hostAndPortList: List<NetworkHostAndPort>, config: MutualSslConfiguration?, enableSSL: Boolean = true, keyStoreProvider: String? = null): List<TransportConfiguration> = hostAndPortList.map {
p2pConnectorTcpTransport(it, config, enableSSL, keyStoreProvider)
fun p2pConnectorTcpTransportFromList(hostAndPortList: List<NetworkHostAndPort>, config: MutualSslConfiguration?, enableSSL: Boolean = true, keyStoreType: String? = null): List<TransportConfiguration> = hostAndPortList.map {
p2pConnectorTcpTransport(it, config, enableSSL, keyStoreType)
}
fun rpcAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: BrokerRpcSslOptions?, enableSSL: Boolean = true): TransportConfiguration {
@ -159,6 +161,8 @@ class ArtemisTcpTransport {
config.trustStorePath.requireOnDefaultFileSystem()
options.putAll(config.toTransportOptions())
options.putAll(defaultSSLOptions)
// This is required to stop Client checking URL address vs. Server provided certificate
options[TransportConstants.VERIFY_HOST_PROP_NAME] = false
}
return TransportConfiguration(connectorFactoryClassName, options)
}
@ -167,17 +171,23 @@ class ArtemisTcpTransport {
rpcConnectorTcpTransport(it, config, enableSSL)
}
fun rpcInternalClientTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration, keyStoreProvider: String? = null): TransportConfiguration {
return TransportConfiguration(connectorFactoryClassName, defaultArtemisOptions(hostAndPort, RPC_PROTOCOLS) + defaultSSLOptions + config.toTransportOptions() + asMap(keyStoreProvider))
fun rpcInternalClientTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration, keyStoreType: String? = null): TransportConfiguration {
val options = defaultArtemisOptions(hostAndPort, RPC_PROTOCOLS).toMutableMap()
options.putAll(defaultSSLOptions)
options.putAll(config.toTransportOptions())
options.putAll(asMap(keyStoreType))
// This is required to stop Client checking URL address vs. Server provided certificate
options[TransportConstants.VERIFY_HOST_PROP_NAME] = false
return TransportConfiguration(connectorFactoryClassName, options)
}
fun rpcInternalAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration, keyStoreProvider: String? = null): TransportConfiguration {
fun rpcInternalAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration, keyStoreType: String? = null): TransportConfiguration {
return TransportConfiguration(acceptorFactoryClassName, defaultArtemisOptions(hostAndPort, RPC_PROTOCOLS) + defaultSSLOptions +
config.toTransportOptions() + (TransportConstants.HANDSHAKE_TIMEOUT to 0) + asMap(keyStoreProvider))
config.toTransportOptions() + (TransportConstants.HANDSHAKE_TIMEOUT to 0) + asMap(keyStoreType))
}
private fun asMap(keyStoreProvider: String?): Map<String, String> {
return keyStoreProvider?.let {mutableMapOf(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to it)} ?: emptyMap()
private fun asMap(keyStoreType: String?): Map<String, String> {
return keyStoreType?.let { mutableMapOf(TransportConstants.KEYSTORE_TYPE_PROP_NAME to it) } ?: emptyMap()
}
}
}

View File

@ -8,6 +8,7 @@ import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.MessagePac
import org.apache.activemq.artemis.protocol.amqp.broker.AMQPMessage
import org.apache.activemq.artemis.protocol.amqp.broker.AmqpInterceptor
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection
import org.apache.qpid.proton.amqp.messaging.Data
class ArtemisMessageSizeChecksInterceptor(maxMessageSize: Int) : MessageSizeChecksInterceptor<Packet>(maxMessageSize), Interceptor {
override fun getMessageSize(packet: Packet?): Int? {
@ -22,7 +23,7 @@ class ArtemisMessageSizeChecksInterceptor(maxMessageSize: Int) : MessageSizeChec
}
class AmqpMessageSizeChecksInterceptor(maxMessageSize: Int) : MessageSizeChecksInterceptor<AMQPMessage>(maxMessageSize), AmqpInterceptor {
override fun getMessageSize(packet: AMQPMessage?): Int? = packet?.encodeSize
override fun getMessageSize(packet: AMQPMessage?): Int? = (packet?.protonMessage?.body as? Data)?.value?.length
}
/**

View File

@ -20,6 +20,7 @@ import net.corda.nodeapi.internal.protonwrapper.netty.ProxyConfig
import net.corda.nodeapi.internal.protonwrapper.netty.RevocationConfig
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException
import org.apache.activemq.artemis.api.core.ActiveMQQueueExistsException
import org.apache.activemq.artemis.api.core.QueueConfiguration
import org.apache.activemq.artemis.api.core.RoutingType
import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.client.ClientConsumer
@ -114,7 +115,9 @@ class BridgeControlListener(private val keyStore: CertificateStore,
private fun registerBridgeControlListener(artemisSession: ClientSession) {
try {
artemisSession.createTemporaryQueue(BRIDGE_CONTROL, RoutingType.MULTICAST, bridgeControlQueue)
artemisSession.createQueue(
QueueConfiguration(bridgeControlQueue).setAddress(BRIDGE_CONTROL).setRoutingType(RoutingType.MULTICAST)
.setTemporary(true).setDurable(false))
} catch (ex: ActiveMQQueueExistsException) {
// Ignore if there is a queue still not cleaned up
}
@ -134,7 +137,9 @@ class BridgeControlListener(private val keyStore: CertificateStore,
private fun registerBridgeDuplicateChecker(artemisSession: ClientSession) {
try {
artemisSession.createTemporaryQueue(BRIDGE_NOTIFY, RoutingType.MULTICAST, bridgeNotifyQueue)
artemisSession.createQueue(
QueueConfiguration(bridgeNotifyQueue).setAddress(BRIDGE_NOTIFY).setRoutingType(RoutingType.MULTICAST)
.setTemporary(true).setDurable(false))
} catch (ex: ActiveMQQueueExistsException) {
// Ignore if there is a queue still not cleaned up
}

View File

@ -15,7 +15,6 @@ import java.time.Duration
*
* totalFailoverDuration = 5 + 5 * 1.5 + 5 * (1.5)^2 + 5 * (1.5)^3 + 5 * (1.5)^4 = ~66 seconds
*
* @param failoverOnInitialAttempt Determines whether failover is triggered if initial connection fails.
* @param initialConnectAttempts The number of reconnect attempts if failover is enabled for initial connection. A value
* of -1 represents infinite attempts.
* @param reconnectAttempts The number of reconnect attempts for failover after initial connection is done. A value
@ -27,7 +26,6 @@ import java.time.Duration
enum class MessagingServerConnectionConfiguration {
DEFAULT {
override fun failoverOnInitialAttempt(isHa: Boolean) = true
override fun initialConnectAttempts(isHa: Boolean) = 5
override fun reconnectAttempts(isHa: Boolean) = 5
override fun retryInterval() = 5.seconds
@ -36,7 +34,6 @@ enum class MessagingServerConnectionConfiguration {
},
FAIL_FAST {
override fun failoverOnInitialAttempt(isHa: Boolean) = isHa
override fun initialConnectAttempts(isHa: Boolean) = 0
// Client die too fast during failover/failback, need a few reconnect attempts to allow new master to become active
override fun reconnectAttempts(isHa: Boolean) = if (isHa) 3 else 0
@ -46,7 +43,6 @@ enum class MessagingServerConnectionConfiguration {
},
CONTINUOUS_RETRY {
override fun failoverOnInitialAttempt(isHa: Boolean) = true
override fun initialConnectAttempts(isHa: Boolean) = if (isHa) 0 else -1
override fun reconnectAttempts(isHa: Boolean) = -1
override fun retryInterval() = 5.seconds
@ -54,7 +50,6 @@ enum class MessagingServerConnectionConfiguration {
override fun maxRetryInterval(isHa: Boolean) = if (isHa) 3.minutes else 5.minutes
};
abstract fun failoverOnInitialAttempt(isHa: Boolean): Boolean
abstract fun initialConnectAttempts(isHa: Boolean): Int
abstract fun reconnectAttempts(isHa: Boolean): Int
abstract fun retryInterval(): Duration