Akka Version:
Akka Features:
HTTPS Server Support
Typesafe\'s ssl-config
When your HTTPS Server hangs for 1 minute and then produces the "Aborting tcp connection because of upstream failure: No elements passed in the last 1 minute." error, it might be your random number generator does not have sufficient entropy to produce enough random numbers in a reasonable time.
In my case I 'fixed' this by using a new SecureRandom
instead of SecureRandom.getInstanceString
when initializing the SSLContext
. That seems sufficiently random/secure to me, but of course you should decide for yourself.
As posted in the other comment, there is a git hub issue that states that "automatically" using the configuration isn't supported yet. However, this issue is closed now; not completed just moved. I went through the release notes for future versions but I didn't see anything relating to this. With so much emphasis on security now, I'm surprised the setup for SSL/TSL isn't something that works out of the box.
I'm using v2.4.4 (current is 2.4.16) and similar to questioner, I found out the hard way that although the akk-http documentation tells you to use the config, and indeed from debugging you can see that the config gets read in, the implementation to actually use it, isn't completed. I got this message in my logs:
akka.actor.ActorSystemImpl(OtisRestActorSystem)] Automatic server-side configuration is not supported yet, will attempt to use client-side settings. Instead it is recommended to construct the Servers HttpsConnectionContext manually (via SSLContext)
I tried to "construct the Servers HttpsConnectionContext manually" using the their ssl config, but I couldn't get it to work.
There were other messages as well, when I was initially troubleshooting that showed it read in the configured key store (which doesn't use the class path to look for it so it couldn't find it at first). So I'm not sure which parts are working and which are missing. So I ended up abandoning the akka-http ssl config completely and set it up myself as my use case is pretty simple. I just want to enable server side SSL/TSL.
In my configuration I have:
ssl {
keyStoreFileName = "myKeyFile.p12"
keyStorePassword = "myPassword"
}
For reading my settings I have:
class Settings(config: Config) extends Extension {
object Ssl {
var KeyStoreFileName = config.getString("ssl.keyStoreFileName")
var KeyStorePassword = config.getString("ssl.keyStorePassword")
}
}
And for the "App" I have:
object RestWebServiceApp extends App with RouteConcatenation {
import akka.event.{Logging, LoggingAdapter}
import akka.http.scaladsl.{ ConnectionContext, HttpsConnectionContext, Http }
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.model.MediaTypes._
import akka.stream.{ActorMaterializer, ActorMaterializerSettings}
import java.io.InputStream
import java.security.{ SecureRandom, KeyStore }
import javax.net.ssl.{ SSLContext, TrustManagerFactory, KeyManagerFactory }
import JsonSupport._
implicit val system = ActorSystem("OtisRestActorSystem")
implicit val materializer: ActorMaterializer = ActorMaterializer(ActorMaterializerSettings(system))
implicit val ec = system.dispatcher
... //setting up all the routes, etc.
val settings = Settings(system)
val fileName = settings.Ssl.KeyStoreFileName
val keyFile: InputStream = getClass.getClassLoader.getResourceAsStream(fileName)
require(keyFile != null, s"Failed to load key file: ${settings.Ssl.KeyStoreFileName}")
val extension = if(fileName.lastIndexOf('.')>0) fileName.substring(fileName.lastIndexOf('.')+1) else ""
val keyStore: KeyStore = extension.toLowerCase match {
case "jks" => KeyStore.getInstance("jks") //Java Key Store; Java default and only works with Java; tested
case "jcek" => KeyStore.getInstance("JCEKS") //Java Cryptography Extension KeyStore; Java 1.4+; not tested
case "pfx" | "p12" => KeyStore.getInstance("PKCS12") // PKCS #12, Common and supported by many languages/frameworks; tested
case _ => throw new IllegalArgumentException(s"Key has an unknown type extension $extension. Support types are jks, jcek, pfx, p12.")
}
val password: Array[Char] = (if(settings.Ssl.KeyStorePassword==null) "" else settings.Ssl.KeyStorePassword).toCharArray
keyStore.load(keyFile, password)
//TODO: looks like the "SunX509", "TLS", are defined in the keystore, should we pull them out rather than hard coding?
val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509")
keyManagerFactory.init(keyStore, password)
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509")
tmf.init(keyStore)
val sslContext: SSLContext = SSLContext.getInstance("TLS")
sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, new SecureRandom)
val https: HttpsConnectionContext = ConnectionContext.https(sslContext)
Http().setDefaultServerHttpContext(https)
Http().bindAndHandle(routes, "localhost", 433, connectionContext = https)
}
Trying to answer the question: Because it is not yet implemented :) There is an open issue at github.