У нас есть приложение, которое решает TCP-соединения, а также использует Akka Actors. Нам нужно было добавить поддержку TLS для Tcp-соединений, поэтому мы пошли с поддержкой Akka TLS, используя Steam и BidiFlow.
Несмотря на то, что он работает, мы сталкиваемся с проблемами диагностики проблем, таких как исключения рукопожатия. Используя -Djavax.net.debug=ssl,handshake,failure
, мы можем видеть информацию журнала в консоли, но она практически не читается, и клиент никак не может определить проблему с этим журналом. Попытка try / catches в коде не работает, и запись Akka в debug мы просто получаем:
2018-11-07 16: 31: 13,607 DEBUG akka.stream.impl.io.TLSActor
Система / StreamSupervisor-1 / поток-0-1
закрытие выхода
При включенном флаге javax мы видим, как генерируются некоторые исключения, но нет способа записать их в код, не только для целей отладки, но и для закрытия соединений при сбое рукопожатия (что, очевидно, не выполняется автоматически при сбой рукопожатия).
Кто-нибудь знает, как регистрировать или ловить эти исключения ??
Вот код, который управляет созданными TLSGraphs:
object Tls {
def getGraph(tlsConfig: TlsConfig, role: TLSRole):
BidiFlow[ByteString, ByteString, ByteString, ByteString, NotUsed] = {
val sslContext = createSslContext(tlsConfig)
val firstSession = createFirstSession(sslContext, tlsConfig.clientAuth)
createGraph(sslContext, firstSession, role)
}
private def createFirstSession(sslContext: SSLContext, clientAuth: Boolean): NegotiateNewSession = {
val negotiatedSession = TLSProtocol.NegotiateNewSession
.withProtocols("TLSv1.2")
.withCipherSuites(
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"
)
.withParameters(sslContext.getDefaultSSLParameters)
//.withClientAuth(TLSClientAuth.none)
negotiatedSession
}
private def createSslContext(tlsConfig: TlsConfig): SSLContext = {
val ksTrust = createKeyStore(tlsConfig.trustStoreType, tlsConfig.trustStoreFile, tlsConfig.trustStorePass)
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
trustManagerFactory.init(ksTrust)
val ksKeys = createKeyStore(tlsConfig.keyStoreType, tlsConfig.keyStoreFile, tlsConfig.keyStorePass)
val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
keyManagerFactory.init(ksKeys, tlsConfig.keyStorePass.toCharArray)
val sslContext = SSLContext.getInstance("TLSv1.2")
sslContext.init(keyManagerFactory.getKeyManagers, trustManagerFactory.getTrustManagers, new SecureRandom)
sslContext
}
private def createKeyStore(storeType: String, file: String, password: String): KeyStore = {
val keyStore: KeyStore = KeyStore.getInstance(storeType)
keyStore.load(new FileInputStream(file), password.toCharArray)
keyStore
}
private def createGraph(
sslContext: SSLContext,
firstSession: NegotiateNewSession,
role: TLSRole)
: BidiFlow[ByteString, ByteString, ByteString, ByteString, NotUsed] = {
val tls: BidiFlow[TLSProtocol.SslTlsOutbound, ByteString, ByteString, TLSProtocol.SslTlsInbound, NotUsed] =
TLS(sslContext, firstSession, role)
val tlsSupport: BidiFlow[ByteString, TLSProtocol.SslTlsOutbound, TLSProtocol.SslTlsInbound, ByteString, NotUsed] =
BidiFlow.fromFlows(
Flow[ByteString].map(TLSProtocol.SendBytes),
Flow[TLSProtocol.SslTlsInbound].map {
case TLSProtocol.SessionBytes(_, bytes) => bytes
case TLSProtocol.SessionTruncated =>
throw ProcessingException("TLS session truncated")
case _ =>
throw ProcessingException("Unexpected type received from TLS pipeline")
})
tlsSupport.atop(tls)
}
}