带有ssl-conf的Akka HTTPS(SSL)服务器 [英] Akka HTTPS (SSL) Server with ssl-conf

查看:175
本文介绍了带有ssl-conf的Akka HTTPS(SSL)服务器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Akka Version

  • Akka 2.4.7

Akka功能

Typesafe的ssl-config

语言:Scala

我正在使用Akka 2.4.7的Http Server功能来在不同的端口上提供多个 HTTPS 服务连接。
在此阶段,要求actor系统的这个组件在一个JVM中托管几个HTTPS服务 - 它是一个连接和集成其他服务的后端。

I am using the Http Server feature of Akka 2.4.7 to provide several HTTPS service connections on different ports. At this stage the requirement is for this component of the actor system to host several of the HTTPS services in the one JVM - it is a backend that connects and integrates other services.

我想使用 Typesafe的ssl-config 库,用于配置每个HTTPS服务器。我该怎么做(我的尝试不成功)

I want to use the Typesafe's ssl-config library to configure each HTTPS server. How do I do this (I am unsuccessful in my attempts)?

对于每个服务,我在application.conf中定义了ssl-config配置块。 nexted配置的一个示例是:

For each service I have defined ssl-config configuration blocks in application.conf. An example of a nexted 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"}
        ]
      }
    }
  }
}

我从使用中获取配置的这部分 application.conf 中定义的 my-service 的HOCON路径,并将其与参考默认配置合并以创建SSLConfigSettings。

I grab this portion of the config from using the HOCON path for my-service defined in application.conf and merge it with the reference default configuration to create a 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()
  }

现在有一个SSLConfigSettings我现在可以创建一个 AkkaSSLConfig 对象,而 Akka 2.4.7 可以用于创建 HttpsConnectionContext

Now having an SSLConfigSettings I now can create an AkkaSSLConfig object which in turn, in Akka 2.4.7, can be used to create a HttpsConnectionContext using the function prototype:

//#https-context-creation
// ConnectionContext
def https (
sslContext:SSLContext,
sslConfig:Option [AkkaSSLConfig] = None,
enabledCipherSuites:Option [immutable.Seq [String]] = None,
enabledProtocols:Option [immutable。 Seq [String]] = None,
clientAuth:Option [TLSClientAuth] = None,
sslParameters:Option [SSLParameters] = None)=
new HttpsConnectionContext(sslContext,sslConfig,enabledCipherSuites,enabledProtocols, clientAuth,sslParameters)
//#https-context-creation

//#https-context-creation // ConnectionContext def https( sslContext: SSLContext, sslConfig: Option[AkkaSSLConfig] = None, enabledCipherSuites: Option[immutable.Seq[String]] = None, enabledProtocols: Option[immutable.Seq[String]] = None, clientAuth: Option[TLSClientAuth] = None, sslParameters: Option[SSLParameters] = None) = new HttpsConnectionContext(sslContext, sslConfig, enabledCipherSuites, enabledProtocols, clientAuth, sslParameters) //#https-context-creation

因此,我可以使用以下之类的代码启动HTTPS服务器(注意:请求handeler在其他地方定义,提供 Future [HttpResponse]

So simply I can fire up a HTTPS server with code like the following (note: the request handeler is defined elsewhere providing the 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()

服务器无异常或错误启动,并在定义的端口 8991 上绑定到 127.0.0.1

The server starts up without exception or error and binds to 127.0.0.1 on the defined 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]

我使用浏览器或卷曲访问服务器,结果不佳。它要求我知道错误的客户端证书,因为我已经在ssl-conf中明确配置它们不需要它们并且JDK8中的ssl-conf设置为默认情况下不需要。

I access the server using a browser or curl and the result is not good. It's asking for a client certificate which I know is wrong, as I have explicitly configured in ssl-conf that they are not needed and ssl-conf in JDK8 sets to this to not needed 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

使用带有_s_client_选项的 openssl 进一步调查显示,没有发生SSL握手且没有返回证书,尽管知道密钥库是好的并且在其他地方工作。

Further investigation with openssl with _s_client_ option shows that there is no SSL handshake occurring and there are no certificates being returned, despite know 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
---

Akka登录调试模式显示没有异常,并且已建立TCP连接,TLS actor开始然后立即停止。

The Akka log in debug mode shows not exceptions and that a TCP connection has been made, a TLS actor starts and then stop 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 picked up:

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)"

如果我手动创建HttpsConnectionContext并且不使用ssl-conf或AkkaSSLConfig,那么有效是什么 - 但这不是目标。如何使用AkkaSSLconf对象和Typesafe ssl-config库配置和创建HTTPS配置连接?

What works is if I create a HttpsConnectionContext manually and don't use ssl-conf or AkkaSSLConfig - but that's not the goal. How do I configure and create a HTTPS config connection using an AkkaSSLconf object and the Typesafe ssl-config library?

如果我特别要求这样的TLS上下文的 TLS 实例:

If I specifically ask for a TLS instance of the TLS context like this:

val sslCtx = SSLContext.getInstance("TLS")

我得到一个例外,即sslContext不是初始化。但是对于 init SSLContext,我需要创建密钥库,信任库,这一切都很好,但感觉我无视ssl-的所有优点 - 已经定义了所有这些内容的conf库。

I get an exception that the sslContext is not initialised. But to init the SSLContext, I need to create the keystore, truststore, which is all well and fine, but it feels like I am ignoring all of the goodness of the ssl-conf library that has all of this stuff already defined.

我发现你可以创建使用以下方法的HTTPS连接上下文:

I found that you can create the HTTPS Connection Context with the following method:

Http().createServerHttpsContext(akkaSSLConfig)

您可以使用AkkaSSLConfig创建HTTPS服务器上下文,这是我追求的好东西。问题在于测试HTTPS服务器不起作用,它只挂起1分钟,但例外情况是:

You can create the HTTPS Server context using the AkkaSSLConfig which is the good stuff I am after. Problem is in testing the HTTPS Server doesn't work, it just hangs 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)

我在GitHub上的Akka回购中查看了createServerHttpsContext的来源这里并找到:

I looked at the source for createServerHttpsContext on the Akka repo 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)
  }

为什么HTTPS服务器不能与 createServerHttpsContext(..)一起工作?特别是手动给你基本上设置了一个TLS SSLContext,KeyManagerFactory (带有密钥库),一个SecureRandom的实例,然后关闭你。

Why doesn't the HTTPS server work with the createServerHttpsContext(..)? Especially given that manually you basically set a TLS SSLContext, KeyManagerFactory (with key stores), an instance of SecureRandom and off you go.

推荐答案

正如其他评论中所述,有一个git hub问题,表明尚未支持自动使用配置。但是,此问题现已关闭;未完成已移动。我查看了未来版本的发行说明,但我没有看到任何与此相关的内容。现在如此强调安全性,我很惊讶SSL / TSL的设置不是那么有效电子邮箱。

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.

我正在使用v2.4.4(当前是2.4.16)和提问者类似,我发现虽然akk-http文档很难告诉你使用配置,实际上从调试你可以看到配置被读入,实际使用它的实现,没有完成。我在日志中收到此消息:

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)

我试图构建服务器 HttpsConnectionContext手动使用他们的ssl配置,但我无法让它工作。

I tried to "construct the Servers HttpsConnectionContext manually" using the their ssl config, but I couldn't get it to work.

还有其他消息,当我最初的故障排除显示它读取在配置的密钥库中(它不使用类路径查找它,因此它最初无法找到它)。所以我不确定哪些部件正在工作,哪些部件缺失。所以我最终完全放弃了akka-http ssl配置并自己设置,因为我的用例非常简单。我只想启用服务器端SSL / TSL。

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.

在我的配置中,我有:

  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")
  }
}

对于App我有:

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)
}

这篇关于带有ssl-conf的Akka HTTPS(SSL)服务器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆