Akka HTTPS Server (SSL) with ssl-conf

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#-672917867] 

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 (#0) * SSL peer handshake failed, the server most likely requires a client certificate to connect * Closing connection 0 curl: (35) SSL peer handshake failed, the server most likely requires a client certificate to connect 

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#-745039521] 2016-06-11 14:09:26,381 INFO [autumn-backend-akka.actor.default-dispatcher-7] ActorSystemImpl - Accepted HTTP connection [Local: address=127.0.0.1, port=8991; Remote: address=127.0.0.1 port=58726]/127.0.0.1:58726 2016-06-11 14:09:26,384 DEBUG [autumn-backend-akka.actor.default-dispatcher-9] StreamSupervisor - now supervising Actor[akka://autumn-backend/user/StreamSupervisor-0/flow-9-0-unknown-operation#149184815] 2016-06-11 14:09:26,385 DEBUG [autumn-backend-akka.actor.default-dispatcher-7] TcpIncomingConnection - now watched by Actor[akka://autumn-backend/user/StreamSupervisor-0/$$j#-1999211380] 2016-06-11 14:09:26,385 DEBUG [autumn-backend-akka.actor.default-dispatcher-9] ActorGraphInterpreter - started ( akka.stream.impl.fusing.ActorGraphInterpreter@57451dc8 ) 2016-06-11 14:09:26,385 DEBUG [autumn-backend-akka.actor.default-dispatcher-5] StreamSupervisor - now supervising Actor[akka://autumn-backend/user/StreamSupervisor-0/flow-9-1-unknown-operation#1511230856] sslConfig.config.loose.disableSNI = false 2016-06-11 14:09:26,387 DEBUG [autumn-backend-akka.actor.default-dispatcher-5] TLSActor - started ( akka.stream.impl.io.TLSActor@50f220e8 ) 2016-06-11 14:09:26,389 DEBUG [autumn-backend-akka.actor.default-dispatcher-5] TLSActor - stopped 

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.

+5
source share
3 answers

As pointed out in another comment, there is a problem with the git hub, which says that "automatically" using the configuration is not yet supported. However, this issue is now closed; not completed just relocated . I looked through the release notes for future versions, but I did not see anything about this. With so much security in mind, I am surprised right now that the installation for SSL / TSL is not something that works out of the box.

I am using v2.4.4 (current 2.4.16), and it looks like a questionnaire, I found that, despite the fact that the akk-http documentation tells you about using the configuration, and indeed, from debugging you can see that the configuration is being read , the implementation for its actual use is not complete. I received 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 "manually constructing the HttpsConnectionContext servers" using their ssl configuration, but I could not get it to work.

There were other messages when I initially fixed errors that showed that it was being read in a configured keystore (which does not use the path to it to look for it so that it cannot find it first). So I'm not sure which parts work and which ones are missing. Therefore, I completely abandoned the akka-http ssl configuration and configured it myself, as my use case is pretty simple. I just want to enable SSL / TSL on the server side.

In my configuration, I have:

  ssl { keyStoreFileName = "myKeyFile.p12" keyStorePassword = "myPassword" } 

To read 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 "Application" 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) } 
+4
source

Trying to answer the question: because it has not yet been implemented :) There is an open problem on github .

+1
source

When your HTTPS server hangs for 1 minute, and then displays "Cancel tcp connection due to upstream failure: no items have passed in the last 1 minute". mistake, maybe your random number generator does not have enough entropy to create enough random numbers in a reasonable amount of time.

In my case, I “fixed” this using new SecureRandom instead of SecureRandom.getInstanceString when initializing SSLContext . This seems random / safe enough to me, but of course you have to decide for yourself.

0
source

All Articles