Akka Version:
Features of Akka:
Language: Scala
I am using the Http Server feature for Akka 2.4.7 to provide multiple HTTPS service connections on different ports. At this point, the requirement is for this actor component to host multiple HTTPS services in the same JVM — a backend that connects and integrates other services.
Question
I want to use the Typesafe ssl-config library to configure each HTTPS server. How to do it (I was not successful in my attempts)?
What I tried:
For each service, I defined ssl-config configuration blocks in application.conf. An example of the following configuration is:
my-service { ssl-config = { debug { all = true } sslParameters { clientAuth : "none" } ssl = { keyManager = { stores = [ {path: tmp/certs/autumn/devhost.jks, password: "not-real-password", type: "JKS"} ] } } } }
I captured this part of the configuration using the HOCON path for the my service defined in application.conf, and combined it with the default default setting to create SSLConfigSettings.
def parseSslConfig(config: Config): SSLConfigSettings = { val cfg = config.withFallback(ConfigFactory.defaultReference().getConfig("ssl-config")) val parser = new SSLConfigParser(EnrichedConfig(cfg), getClass.getClassLoader) parser.parse() }
Now that I have SSLConfigSettings, now I can create an AkkaSSLConfig object, which, in turn, in Akka 2.4.7, can be used to create an HttpsConnectionContext using the function prototype:
// # HTTPS context creation // ConnectionContext def https (sslContext: SSLContext, sslConfig: Option [AkkaSSLConfig] = No, enabledCipherSuites: Option [immutable.Seq [String]] = None, enabledProtocols: Option [immutable.Seq [String] ] = None, clientAuth: Option [TLSClientAuth] = No, sslParameters: Option [SSLParameters] = No) = new HttpsConnectionContext (sslContext, sslConfig, enabledCipherSuites, enabledProtocols, clientAuth, sslParameters) // # HTTPS context
This way I can start the HTTPS server with code similar to the one below (note: the request handler is defined elsewhere, providing Future[HttpResponse] )
val akkaSSLConfig: AkkaSSLConfig = AkkaSSLConfig().withSettings(sslConfigSettings) val serverConnectionContext = ConnectionContext.https(SSLContext.getDefault, Some(akkaSSLConfig)) val httpServer = httpServerSystem.bind(interface = "127.0.0.1", port = 8991, connectionContext = serverConnectionContext) val bindingFuture: Future[Http.ServerBinding] = httpServer.to(Sink.foreach { connection => system.log.info(s"Accepted HTTP connection " + s"[Local: address=${connection.localAddress.getAddress.getHostAddress}, port=${connection.localAddress.getPort};" + s" Remote: address=${connection.remoteAddress.getAddress.getHostAddress} port=${connection.remoteAddress.getPort}]" + connection.remoteAddress) connection.handleWithAsyncHandler(httpRequest => requestHandler(httpRequest, connection.localAddress, connection.remoteAddress)) }).run()
The server starts without exception or error and binds to 127.0.0.1 on a specific port 8991.
2016-06-11 14:07:51,403 DEBUG [autumn-backend-akka.actor.default-dispatcher-7] TcpListener - Successfully bound to /127.0.0.1:8991 2016-06-11 14:07:51,404 DEBUG [autumn-backend-akka.actor.default-dispatcher-7] TcpListener - started ( akka.io.TcpListener@3d1d819f ) 2016-06-11 14:07:51,404 DEBUG [autumn-backend-akka.actor.default-dispatcher-7] TcpListener - now watched by Actor[akka://autumn-backend/system/IO-TCP/selectors/$a#-745039521] 2016-06-11 14:07:51,407 DEBUG [autumn-backend-akka.actor.default-dispatcher-5] TcpListener - now watched by Actor[akka://autumn-backend/user/StreamSupervisor-0/$$a
I access the server using a browser or curl, and the result does not fit. It requests a client certificate, which, as I know, is erroneous, since I explicitly configured in ssl-conf that they are not needed, and ssl-conf in JDK8 establishes that this is not necessary by default.
curl -v https://localhost:8991 * Rebuilt URL to: https://localhost:8991/ * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 8991 (
Further research using openssl with the _s_client_ option shows that there is no SSL connection confirmation and no certificates returned, although it is known that the keystore is good and works elsewhere.
openssl s_client -showcerts -connect localhost:8991 CONNECTED(00000003) 140735299473488:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:s23_clnt.c:769: --- no peer certificate available No client certificate CA names sent --- SSL handshake has read 7 bytes and written 317 bytes --- New, (NONE), Cipher is (NONE) Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE No ALPN negotiated ---
In Akka log debugging mode, no exceptions are displayed and that the TCP connection was made, the TLS actor starts, and then stops immediately.
2016-06-11 14:09:26,378 DEBUG [autumn-backend-akka.actor.default-dispatcher-6] TcpListener - New connection accepted 2016-06-11 14:09:26,378 DEBUG [autumn-backend-akka.actor.default-dispatcher-9] SelectionHandler - now supervising Actor[akka://autumn-backend/system/IO-TCP/selectors/$a/9#1252313265] 2016-06-11 14:09:26,378 DEBUG [autumn-backend-akka.actor.default-dispatcher-5] TcpIncomingConnection - started ( akka.io.TcpIncomingConnection@6f12f120 ) 2016-06-11 14:09:26,378 DEBUG [autumn-backend-akka.actor.default-dispatcher-5] TcpIncomingConnection - now watched by Actor[akka://autumn-backend/system/IO-TCP/selectors/$a
Debugging at runtime shows that the keystore is being matched:
akkaSSLConfig = { com.typesafe.sslconfig.akka.AkkaSSLConfig@7851 } system = { akka.actor.ActorSystemImpl@7850 } "akka://autumn-backend" config = { com.typesafe.sslconfig.ssl.SSLConfigSettings@7849 } "SSLConfig(None,SSLDebugConfig(false,false,false,None,false,false,false,false,None,false,false,false,false,false),false,Vector(RSA keySize < 2048, DSA keySize < 2048, EC keySize < 224),Vector(MD2, MD4, MD5),None,Some(Vector(TLSv1.2, TLSv1.1, TLSv1)),class com.typesafe.sslconfig.ssl.DefaultHostnameVerifier,KeyManagerConfig(SunX509,List(KeyStoreConfig(None,Some(config/certs/autumn/devhost.jks),Some(A8C7B78Ymb),JKS))),SSLLooseConfig(false,None,None,false,false,false,false),TLSv1.2,None,None,SSLParametersConfig(Default,Vector()),TrustManagerConfig(PKIX,List()))" default = false protocol = "TLSv1.2" checkRevocation = { scala.None$@7905 } "None" revocationLists = { scala.None$@7905 } "None" enabledCipherSuites = { scala.None$@7905 } "None" enabledProtocols = { scala.Some@7906 } "Some(Vector(TLSv1.2, TLSv1.1, TLSv1))" disabledSignatureAlgorithms = { scala.collection.immutable.Vector@7907 } "Vector" size = 3 disabledKeyAlgorithms = { scala.collection.immutable.Vector@7911 } "Vector" size = 3 sslParametersConfig = { com.typesafe.sslconfig.ssl.SSLParametersConfig@7917 } "SSLParametersConfig(Default,Vector())" keyManagerConfig = { com.typesafe.sslconfig.ssl.KeyManagerConfig@7918 } "KeyManagerConfig(SunX509,List(KeyStoreConfig(None,Some(config/certs/autumn/devhost.jks),Some(A8C7B78Ymb),JKS)))" algorithm = "SunX509" keyStoreConfigs = { scala.collection.immutable.$colon$colon@7942 } "::" size = 1 0 = { com.typesafe.sslconfig.ssl.KeyStoreConfig@9390 } "KeyStoreConfig(None,Some(config/certs/autumn/devhost.jks),Some(not-real-password),JKS)"
Which works if I create an HttpsConnectionContext manually and don't use ssl-conf or AkkaSSLConfig, but that is not the goal. How to configure and create an HTTPS configuration connection using the AkkaSSLconf object and the syspacesafe class library?
UPDATE 1:
If I specifically ask for a TLS instance of a TLS context, for example:
val sslCtx = SSLContext.getInstance("TLS")
I get an exception that sslContext is not initialized. But for init SSLContext, I need to create a keystore, truststore, which is fine and fine, but it seems to me that I ignore all the goodness of the ssl-conf library, which already has all of this.
Update 2:
I found that you can create an HTTPS connection context in the following way:
Http().createServerHttpsContext(akkaSSLConfig)
You can create an HTTPS server context using AkkaSSLConfig, which is a good material that I use. The problem is that the HTTPS server is down, it just freezes for 1 minute with the exception:
2016-06-12 11:14:53,222 DEBUG [autumn-backend-akka.actor.default-dispatcher-12] RepointableActorRef - Aborting tcp connection because of upstream failure: No elements passed in the last 1 minute. akka.stream.impl.Timers$IdleTimeoutBidi$$anon$7.onTimer(Timers.scala:160) akka.stream.stage.TimerGraphStageLogic.akka$stream$stage$TimerGraphStageLogic$$onInternalTimer(GraphStage.scala:1125) akka.stream.stage.TimerGraphStageLogic$$anonfun$akka$stream$stage$TimerGraphStageLogic$$getTimerAsyncCallback$1.apply(GraphStage.scala:1114) akka.stream.stage.TimerGraphStageLogic$$anonfun$akka$stream$stage$TimerGraphStageLogic$$getTimerAsyncCallback$1.apply(GraphStage.scala:1114) akka.stream.impl.fusing.GraphInterpreter.runAsyncInput(GraphInterpreter.scala:572) akka.stream.impl.fusing.GraphInterpreterShell.receive(ActorGraphInterpreter.scala:420) akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:604) akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:619) akka.actor.Actor$class.aroundReceive(Actor.scala:484)
I looked at the source for createServerHttpsContext on the Akka replica on GitHub here and found:
// currently the same configuration as client by default, however we should tune this for server-side apropriately (!) def createServerHttpsContext(sslConfig: AkkaSSLConfig): HttpsConnectionContext = { log.warning("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).") createClientHttpsContext(sslConfig) }
Why is the HTTPS server not working with createServerHttpsContext(..) ? Especially considering that manually you basically install TLS SSLContext, KeyManagerFactory (with key stores), a SecureRandom instance and exit.