=rem #13874 cleanup SSLSettings (#21746)

This commit is contained in:
Johannes Rudolph 2016-10-28 11:45:48 +02:00 committed by Konrad Malawski
parent 3e55aa5555
commit ff78b84233
4 changed files with 50 additions and 75 deletions

View file

@ -20,34 +20,20 @@ import scala.util.Try
* INTERNAL API * INTERNAL API
*/ */
private[akka] class SSLSettings(config: Config) { private[akka] class SSLSettings(config: Config) {
import config.{ getString, getStringList }
import config._ val SSLKeyStore = getString("key-store")
val SSLTrustStore = getString("trust-store")
val SSLKeyStorePassword = getString("key-store-password")
val SSLKeyPassword = getString("key-password")
private def emptyIsNone(s: String): Option[String] = Option(s).filter(_.length > 0) val SSLTrustStorePassword = getString("trust-store-password")
val SSLKeyStore = emptyIsNone(getString("key-store"))
val SSLTrustStore = emptyIsNone(getString("trust-store"))
val SSLKeyStorePassword = emptyIsNone(getString("key-store-password"))
val SSLKeyPassword = emptyIsNone(getString("key-password"))
val SSLTrustStorePassword = emptyIsNone(getString("trust-store-password"))
val SSLEnabledAlgorithms = immutableSeq(getStringList("enabled-algorithms")).to[Set] val SSLEnabledAlgorithms = immutableSeq(getStringList("enabled-algorithms")).to[Set]
val SSLProtocol = emptyIsNone(getString("protocol")) val SSLProtocol = getString("protocol")
val SSLRandomNumberGenerator = emptyIsNone(getString("random-number-generator")) val SSLRandomNumberGenerator = getString("random-number-generator")
if (SSLProtocol.isEmpty) throw new ConfigurationException(
"Configuration option 'akka.remote.netty.ssl.enable-ssl is turned on but no protocol is defined in 'akka.remote.netty.ssl.security.protocol'.")
if (SSLKeyStore.isEmpty && SSLTrustStore.isEmpty) throw new ConfigurationException(
"Configuration option 'akka.remote.netty.ssl.enable-ssl is turned on but no key/trust store is defined in 'akka.remote.netty.ssl.security.key-store' / 'akka.remote.netty.ssl.security.trust-store'.")
if (SSLKeyStore.isDefined && SSLKeyStorePassword.isEmpty) throw new ConfigurationException(
"Configuration option 'akka.remote.netty.ssl.security.key-store' is defined but no key-store password is defined in 'akka.remote.netty.ssl.security.key-store-password'.")
if (SSLKeyStore.isDefined && SSLKeyPassword.isEmpty) throw new ConfigurationException(
"Configuration option 'akka.remote.netty.ssl.security.key-store' is defined but no key password is defined in 'akka.remote.netty.ssl.security.key-password'.")
if (SSLTrustStore.isDefined && SSLTrustStorePassword.isEmpty) throw new ConfigurationException(
"Configuration option 'akka.remote.netty.ssl.security.trust-store' is defined but no trust-store password is defined in 'akka.remote.netty.ssl.security.trust-store-password'.")
} }
/** /**
@ -65,26 +51,28 @@ private[akka] object NettySSLSupport {
def apply(settings: SSLSettings, log: LoggingAdapter, isClient: Boolean): SslHandler = def apply(settings: SSLSettings, log: LoggingAdapter, isClient: Boolean): SslHandler =
if (isClient) initializeClientSSL(settings, log) else initializeServerSSL(settings, log) if (isClient) initializeClientSSL(settings, log) else initializeServerSSL(settings, log)
def initializeCustomSecureRandom(rngName: Option[String], log: LoggingAdapter): SecureRandom = { def initializeCustomSecureRandom(rngName: String, log: LoggingAdapter): SecureRandom = {
val rng = rngName match { val rng = rngName match {
case Some(r @ ("AES128CounterSecureRNG" | "AES256CounterSecureRNG")) case r @ ("AES128CounterSecureRNG" | "AES256CounterSecureRNG")
log.debug("SSL random number generator set to: {}", r) log.debug("SSL random number generator set to: {}", r)
SecureRandom.getInstance(r, AkkaProvider) SecureRandom.getInstance(r, AkkaProvider)
case Some(r @ ("AES128CounterInetRNG" | "AES256CounterInetRNG")) case r @ ("AES128CounterInetRNG" | "AES256CounterInetRNG")
log.warning("SSL random number generator {} is deprecated, " + log.warning("SSL random number generator {} is deprecated, " +
"use AES128CounterSecureRNG or AES256CounterSecureRNG instead", r) "use AES128CounterSecureRNG or AES256CounterSecureRNG instead", r)
SecureRandom.getInstance(r, AkkaProvider) SecureRandom.getInstance(r, AkkaProvider)
case Some(s @ ("SHA1PRNG" | "NativePRNG")) case s @ ("SHA1PRNG" | "NativePRNG")
log.debug("SSL random number generator set to: " + s) log.debug("SSL random number generator set to: " + s)
// SHA1PRNG needs /dev/urandom to be the source on Linux to prevent problems with /dev/random blocking // SHA1PRNG needs /dev/urandom to be the source on Linux to prevent problems with /dev/random blocking
// However, this also makes the seed source insecure as the seed is reused to avoid blocking (not a problem on FreeBSD). // However, this also makes the seed source insecure as the seed is reused to avoid blocking (not a problem on FreeBSD).
SecureRandom.getInstance(s) SecureRandom.getInstance(s)
case Some(unknown)
log.warning("Unknown SSLRandomNumberGenerator [{}] falling back to SecureRandom", unknown) case ""
new SecureRandom
case None
log.debug("SSLRandomNumberGenerator not specified, falling back to SecureRandom") log.debug("SSLRandomNumberGenerator not specified, falling back to SecureRandom")
new SecureRandom new SecureRandom
case unknown
log.warning("Unknown SSLRandomNumberGenerator [{}] falling back to SecureRandom", unknown)
new SecureRandom
} }
rng.nextInt() // prevent stall on first access rng.nextInt() // prevent stall on first access
rng rng
@ -113,21 +101,14 @@ private[akka] object NettySSLSupport {
case e: GeneralSecurityException throw new RemoteTransportException("Client SSL connection could not be established because SSL context could not be constructed", e) case e: GeneralSecurityException throw new RemoteTransportException("Client SSL connection could not be established because SSL context could not be constructed", e)
} }
((settings.SSLTrustStore, settings.SSLTrustStorePassword, settings.SSLProtocol) match { constructClientContext(settings, log, settings.SSLTrustStore, settings.SSLTrustStorePassword, settings.SSLProtocol) match {
case (Some(trustStore), Some(password), Some(protocol)) constructClientContext(settings, log, trustStore, password, protocol)
case (trustStore, password, protocol) throw new GeneralSecurityException(
"One or several SSL trust store settings are missing: [trust-store: %s] [trust-store-password: %s] [protocol: %s]".format(
trustStore,
password,
protocol))
}) match {
case Some(context) case Some(context)
log.debug("Using client SSL context to create SSLEngine ...") log.debug("Using client SSL context to create SSLEngine ...")
new SslHandler({ new SslHandler({
val sslEngine = context.createSSLEngine val sslEngine = context.createSSLEngine
sslEngine.setUseClientMode(true) sslEngine.setUseClientMode(true)
sslEngine.setEnabledCipherSuites(settings.SSLEnabledAlgorithms.toArray) sslEngine.setEnabledCipherSuites(settings.SSLEnabledAlgorithms.toArray)
sslEngine.setEnabledProtocols(settings.SSLProtocol.toArray) sslEngine.setEnabledProtocols(Array(settings.SSLProtocol))
sslEngine sslEngine
}) })
case None case None
@ -154,30 +135,25 @@ private[akka] object NettySSLSupport {
keyStore keyStore
}, keyPassword.toCharArray) }, keyPassword.toCharArray)
val trustManagers: Option[Array[TrustManager]] = settings.SSLTrustStore map { val trustManagers: Array[TrustManager] = {
path val pwd = settings.SSLTrustStorePassword.toCharArray
val pwd = settings.SSLTrustStorePassword.map(_.toCharArray).orNull val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm) trustManagerFactory.init({
trustManagerFactory.init({ val trustStore = KeyStore.getInstance(KeyStore.getDefaultType)
val trustStore = KeyStore.getInstance(KeyStore.getDefaultType) val fin = new FileInputStream(settings.SSLTrustStore)
val fin = new FileInputStream(path) try trustStore.load(fin, pwd) finally Try(fin.close())
try trustStore.load(fin, pwd) finally Try(fin.close()) trustStore
trustStore })
}) trustManagerFactory.getTrustManagers
trustManagerFactory.getTrustManagers
} }
Option(SSLContext.getInstance(protocol)) map { ctx ctx.init(factory.getKeyManagers, trustManagers.orNull, rng); ctx } Option(SSLContext.getInstance(protocol)) map { ctx ctx.init(factory.getKeyManagers, trustManagers, rng); ctx }
} catch { } catch {
case e: FileNotFoundException throw new RemoteTransportException("Server SSL connection could not be established because key store could not be loaded", e) case e: FileNotFoundException throw new RemoteTransportException("Server SSL connection could not be established because key store could not be loaded", e)
case e: IOException throw new RemoteTransportException("Server SSL connection could not be established because: " + e.getMessage, e) case e: IOException throw new RemoteTransportException("Server SSL connection could not be established because: " + e.getMessage, e)
case e: GeneralSecurityException throw new RemoteTransportException("Server SSL connection could not be established because SSL context could not be constructed", e) case e: GeneralSecurityException throw new RemoteTransportException("Server SSL connection could not be established because SSL context could not be constructed", e)
} }
((settings.SSLKeyStore, settings.SSLKeyStorePassword, settings.SSLKeyPassword, settings.SSLProtocol) match { constructServerContext(settings, log, settings.SSLKeyStore, settings.SSLKeyStorePassword, settings.SSLKeyPassword, settings.SSLProtocol) match {
case (Some(keyStore), Some(storePassword), Some(keyPassword), Some(protocol)) constructServerContext(settings, log, keyStore, storePassword, keyPassword, protocol)
case (keyStore, storePassword, keyPassword, protocol) throw new GeneralSecurityException(
s"SSL key store settings went missing. [key-store: $keyStore] [key-store-password: $storePassword] [key-password: $keyPassword] [protocol: $protocol]")
}) match {
case Some(context) case Some(context)
log.debug("Using server SSL context to create SSLEngine ...") log.debug("Using server SSL context to create SSLEngine ...")
val sslEngine = context.createSSLEngine val sslEngine = context.createSSLEngine

View file

@ -122,14 +122,14 @@ class RemoteConfigSpec extends AkkaSpec(
"contain correct ssl configuration values in reference.conf" in { "contain correct ssl configuration values in reference.conf" in {
val sslSettings = new SSLSettings(system.settings.config.getConfig("akka.remote.netty.ssl.security")) val sslSettings = new SSLSettings(system.settings.config.getConfig("akka.remote.netty.ssl.security"))
sslSettings.SSLKeyStore should ===(Some("keystore")) sslSettings.SSLKeyStore should ===("keystore")
sslSettings.SSLKeyStorePassword should ===(Some("changeme")) sslSettings.SSLKeyStorePassword should ===("changeme")
sslSettings.SSLKeyPassword should ===(Some("changeme")) sslSettings.SSLKeyPassword should ===("changeme")
sslSettings.SSLTrustStore should ===(Some("truststore")) sslSettings.SSLTrustStore should ===("truststore")
sslSettings.SSLTrustStorePassword should ===(Some("changeme")) sslSettings.SSLTrustStorePassword should ===("changeme")
sslSettings.SSLProtocol should ===(Some("TLSv1.2")) sslSettings.SSLProtocol should ===("TLSv1.2")
sslSettings.SSLEnabledAlgorithms should ===(Set("TLS_RSA_WITH_AES_128_CBC_SHA")) sslSettings.SSLEnabledAlgorithms should ===(Set("TLS_RSA_WITH_AES_128_CBC_SHA"))
sslSettings.SSLRandomNumberGenerator should ===(None) sslSettings.SSLRandomNumberGenerator should ===("")
} }
"have debug logging of the failure injector turned off in reference.conf" in { "have debug logging of the failure injector turned off in reference.conf" in {

View file

@ -66,17 +66,16 @@ object Configuration {
val rng = NettySSLSupport.initializeCustomSecureRandom(settings.SSLRandomNumberGenerator, NoLogging) val rng = NettySSLSupport.initializeCustomSecureRandom(settings.SSLRandomNumberGenerator, NoLogging)
rng.nextInt() // Has to work rng.nextInt() // Has to work
settings.SSLRandomNumberGenerator foreach { val sRng = settings.SSLRandomNumberGenerator
sRng rng.getAlgorithm == sRng || (throw new NoSuchAlgorithmException(sRng)) rng.getAlgorithm == sRng || (throw new NoSuchAlgorithmException(sRng))
}
val engine = NettySSLSupport.initializeClientSSL(settings, NoLogging).getEngine val engine = NettySSLSupport.initializeClientSSL(settings, NoLogging).getEngine
val gotAllSupported = enabled.toSet diff engine.getSupportedCipherSuites.toSet val gotAllSupported = enabled.toSet diff engine.getSupportedCipherSuites.toSet
val gotAllEnabled = enabled.toSet diff engine.getEnabledCipherSuites.toSet val gotAllEnabled = enabled.toSet diff engine.getEnabledCipherSuites.toSet
gotAllSupported.isEmpty || (throw new IllegalArgumentException("Cipher Suite not supported: " + gotAllSupported)) gotAllSupported.isEmpty || (throw new IllegalArgumentException("Cipher Suite not supported: " + gotAllSupported))
gotAllEnabled.isEmpty || (throw new IllegalArgumentException("Cipher Suite not enabled: " + gotAllEnabled)) gotAllEnabled.isEmpty || (throw new IllegalArgumentException("Cipher Suite not enabled: " + gotAllEnabled))
engine.getSupportedProtocols.contains(settings.SSLProtocol.get) || engine.getSupportedProtocols.contains(settings.SSLProtocol) ||
(throw new IllegalArgumentException("Protocol not supported: " + settings.SSLProtocol.get)) (throw new IllegalArgumentException("Protocol not supported: " + settings.SSLProtocol))
CipherConfig(true, config, cipher, localPort, remotePort) CipherConfig(true, config, cipher, localPort, remotePort)
} catch { } catch {

View file

@ -13,14 +13,14 @@ class Ticket1978ConfigSpec extends AkkaSpec("""
"be able to parse these extra Netty config elements" in { "be able to parse these extra Netty config elements" in {
val settings = new SSLSettings(system.settings.config.getConfig("akka.remote.netty.ssl.security")) val settings = new SSLSettings(system.settings.config.getConfig("akka.remote.netty.ssl.security"))
settings.SSLKeyStore should ===(Some("keystore")) settings.SSLKeyStore should ===("keystore")
settings.SSLKeyStorePassword should ===(Some("changeme")) settings.SSLKeyStorePassword should ===("changeme")
settings.SSLKeyPassword should ===(Some("changeme")) settings.SSLKeyPassword should ===("changeme")
settings.SSLTrustStore should ===(Some("truststore")) settings.SSLTrustStore should ===("truststore")
settings.SSLTrustStorePassword should ===(Some("changeme")) settings.SSLTrustStorePassword should ===("changeme")
settings.SSLProtocol should ===(Some("TLSv1.2")) settings.SSLProtocol should ===("TLSv1.2")
settings.SSLEnabledAlgorithms should ===(Set("TLS_RSA_WITH_AES_128_CBC_SHA")) settings.SSLEnabledAlgorithms should ===(Set("TLS_RSA_WITH_AES_128_CBC_SHA"))
settings.SSLRandomNumberGenerator should ===(Some("AES128CounterSecureRNG")) settings.SSLRandomNumberGenerator should ===("AES128CounterSecureRNG")
} }
} }
} }