parent
3e55aa5555
commit
ff78b84233
4 changed files with 50 additions and 75 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue