From 6e1b32e6ea8f175a234b4795b72abc7341c37253 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Wed, 6 Apr 2016 22:54:58 +0200 Subject: [PATCH] htp #20214 more docs on https context configuring --- .../http/javadsl/HttpsExamplesDocTest.java | 49 +++++++++++++ .../java/http/client-side/https-support.rst | 69 +++++++++++++++++-- .../http/scaladsl/HttpsExamplesSpec.scala | 30 ++++++++ .../scala/http/client-side/https-support.rst | 66 ++++++++++++++++-- .../akka/http/javadsl/ConnectionContext.scala | 1 + .../main/scala/akka/http/javadsl/Http.scala | 7 ++ .../http/scaladsl/ConnectionContext.scala | 1 + .../main/scala/akka/http/scaladsl/Http.scala | 31 +++++---- .../http/scaladsl/SslConfigWarningsSpec.scala | 12 ++-- project/Dependencies.scala | 2 +- project/MiMa.scala | 4 ++ 11 files changed, 240 insertions(+), 32 deletions(-) create mode 100644 akka-docs/rst/java/code/docs/http/javadsl/HttpsExamplesDocTest.java create mode 100644 akka-docs/rst/scala/code/docs/http/scaladsl/HttpsExamplesSpec.scala diff --git a/akka-docs/rst/java/code/docs/http/javadsl/HttpsExamplesDocTest.java b/akka-docs/rst/java/code/docs/http/javadsl/HttpsExamplesDocTest.java new file mode 100644 index 0000000000..8883594b1d --- /dev/null +++ b/akka-docs/rst/java/code/docs/http/javadsl/HttpsExamplesDocTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009-2016 Lightbend Inc. + */ + +package docs.http.javadsl; + +import akka.actor.AbstractActor; +import akka.actor.ActorSystem; +import akka.http.javadsl.*; +import akka.http.javadsl.model.HttpRequest; +import akka.http.javadsl.model.HttpResponse; +import akka.japi.Pair; +import akka.japi.pf.ReceiveBuilder; +import akka.stream.ActorMaterializer; +import akka.stream.Materializer; +import akka.stream.javadsl.Flow; +import akka.stream.javadsl.Sink; +import akka.stream.javadsl.Source; +import com.typesafe.sslconfig.akka.AkkaSSLConfig; +import scala.concurrent.ExecutionContextExecutor; +import scala.util.Try; + +import java.util.concurrent.CompletionStage; + +import static akka.http.javadsl.ConnectHttp.toHost; +import static akka.pattern.PatternsCS.pipe; + +@SuppressWarnings("unused") +public class HttpsExamplesDocTest { + + // compile only test + public void testConstructRequest() { + String unsafeHost = "example.com"; + //#disable-sni-connection + final ActorSystem system = ActorSystem.create(); + final ActorMaterializer mat = ActorMaterializer.create(system); + final Http http = Http.get(system); + + // WARNING: disabling SNI is a very bad idea, please don't unless you have a very good reason to. + final AkkaSSLConfig defaultSSLConfig = AkkaSSLConfig.get(system); + final AkkaSSLConfig badSslConfig = defaultSSLConfig + .convertSettings(s -> s.withLoose(s.loose().withDisableSNI(true))); + final HttpsConnectionContext badCtx = http.createClientHttpsContext(badSslConfig); + + http.outgoingConnection(ConnectHttp.toHostHttps(unsafeHost).withCustomHttpsContext(badCtx)); + //#disable-sni-connection + } + +} diff --git a/akka-docs/rst/java/http/client-side/https-support.rst b/akka-docs/rst/java/http/client-side/https-support.rst index f9a9e02776..ad7082e760 100644 --- a/akka-docs/rst/java/http/client-side/https-support.rst +++ b/akka-docs/rst/java/http/client-side/https-support.rst @@ -45,8 +45,33 @@ to rely on the configured default client-side ``HttpsContext``. If no custom ``HttpsContext`` is defined the default context uses Java's default TLS settings. Customizing the ``HttpsContext`` can make the Https client less secure. Understand what you are doing! +Detailed configuration and workarounds +-------------------------------------- + +Akka HTTP relies on `Typesafe SSL-Config`_ which is a library maintained by Lightbend that makes configuring +things related to SSL/TLS much simpler than using the raw SSL APIs provided by the JDK. Please refer to its +documentation to learn more about it. + +All configuration options available to this library may be set under the ``akka.ssl-context`` configuration for Akka HTTP applications. + +.. note:: + When encountering problems connecting to HTTPS hosts we highly encourage to reading up on the excellent ssl-config + configuration. Especially the quick start sections about `adding certificates to the trust store`_ should prove + very useful, for example to easily trust a self-signed certificate that applications might use in development mode. + +.. warning:: + While it is possible to disable certain checks using the so called "loose" settings in SSL Config, we **strongly recommend** + to instead attempt to solve these issues by properly configuring TLS–for example by adding trusted keys to the keystore. + + If however certain checks really need to be disabled because of misconfigured (or legacy) servers that your + application has to speak to, instead of disabling the checks globally (i.e. in ``application.conf``) we suggest + configuring the loose settings for *specific connections* that are known to need them disabled (and trusted for some other reason). + The pattern of doing so is documented in the folowing sub-sections. + +.. _adding certificates to the trust store: http://typesafehub.github.io/ssl-config/WSQuickStart.html#connecting-to-a-remote-server-over-https + Hostname verification ---------------------- +^^^^^^^^^^^^^^^^^^^^^ Hostname verification proves that the Akka HTTP client is actually communicating with the server it intended to communicate with. Without this check a man-in-the-middle attack is possible. In the attack scenario, an alternative @@ -57,9 +82,43 @@ The default ``HttpsContext`` enables hostname verification. Akka HTTP relies on to implement this and security options for SSL/TLS. Hostname verification is provided by the JDK and used by Akka HTTP since Java 7, and on Java 6 the verification is implemented by ssl-config manually. -.. note:: - We highly recommend updating your Java runtime to the latest available release, - preferably JDK 8, as it includes this and many more security features related to TLS. +For further recommended reading we would like to highlight the `fixing hostname verification blog post`_ by blog post by Will Sargent. -.. _Typesafe SSL-Config: https://github.com/typesafehub/ssl-config +.. _Typesafe SSL-Config: http://typesafehub.github.io/ssl-config +.. _fixing hostname verification blog post: https://tersesystems.com/2014/03/23/fixing-hostname-verification/ .. _akka.http.javadsl.Http: @github@/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala + + +Server Name Indication (SNI) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +SNI is an TLS extension which aims to guard against man-in-the-middle attacks. It does so by having the client send the +name of the virtual domain it is expecting to talk to as part of the TLS handshake. + +It is specified as part of `RFC 6066`_. + +Disabling TLS security features, at your own risk +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. warning:: + It is highly discouraged to disable any of the security features of TLS, however do acknowlage that workarounds may sometimes be needed. + + Before disabling any of the features one should consider if they may be solvable *within* the TLS world, + for example by `trusting a certificate`_, or `configuring the trusted cipher suites`_. + There's also a very important section in the ssl-config docs titled `LooseSSL - Please read this before turning anything off!`_. + + If disabling features is indeed desired, we recommend doing so for *specific connections*, + instead of globally configuring it via ``application.conf``. + +The following shows an example of disabling SNI for a given connection: + +.. includecode:: ../../code/docs/http/scaladsl/HttpsExamplesSpec.scala + :include: disable-sni-connection + +The ``badSslConfig`` is a copy of the default ``AkkaSSLConfig`` with with the slightly changed configuration to disable SNI. +This value can be cached and used for connections which should indeed not use this feature. + +.. _RFC 6066: https://tools.ietf.org/html/rfc6066#page-6 +.. _LooseSSL - Please read this before turning anything off!: http://typesafehub.github.io/ssl-config/LooseSSL.html#please-read-this-before-turning-anything-off +.. _trusting a certificate: http://typesafehub.github.io/ssl-config/WSQuickStart.html +.. _configuring the trusted cipher suites: http://typesafehub.github.io/ssl-config/CipherSuites.html diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/HttpsExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/HttpsExamplesSpec.scala new file mode 100644 index 0000000000..ff30a2479c --- /dev/null +++ b/akka-docs/rst/scala/code/docs/http/scaladsl/HttpsExamplesSpec.scala @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2009-2016 Lightbend Inc. + */ + +package docs.http.scaladsl + +import akka.actor.{ ActorLogging, ActorSystem } +import akka.http.scaladsl.Http +import akka.stream.ActorMaterializer +import akka.util.ByteString +import com.typesafe.sslconfig.akka.AkkaSSLConfig +import org.scalatest.{ Matchers, WordSpec } + +class HttpsExamplesSpec extends WordSpec with Matchers { + + "disable SNI for connection" in { + pending // compile-time only test + + val unsafeHost = "example.com" + //#disable-sni-connection + implicit val system = ActorSystem() + implicit val mat = ActorMaterializer() + + // WARNING: disabling SNI is a very bad idea, please don't unless you have a very good reason to. + val badSslConfig = AkkaSSLConfig().mapSettings(s => s.withLoose(s.loose.withDisableSNI(true))) + val badCtx = Http().createClientHttpsContext(badSslConfig) + Http().outgoingConnectionHttps(unsafeHost, connectionContext = badCtx) + //#disable-sni-connection + } +} \ No newline at end of file diff --git a/akka-docs/rst/scala/http/client-side/https-support.rst b/akka-docs/rst/scala/http/client-side/https-support.rst index 4c480db335..c3b421425b 100644 --- a/akka-docs/rst/scala/http/client-side/https-support.rst +++ b/akka-docs/rst/scala/http/client-side/https-support.rst @@ -45,8 +45,33 @@ to rely on the configured default client-side ``HttpsContext``. If no custom ``HttpsContext`` is defined the default context uses Java's default TLS settings. Customizing the ``HttpsContext`` can make the Https client less secure. Understand what you are doing! +Detailed configuration and workarounds +-------------------------------------- + +Akka HTTP relies on `Typesafe SSL-Config`_ which is a library maintained by Lightbend that makes configuring +things related to SSL/TLS much simpler than using the raw SSL APIs provided by the JDK. Please refer to its +documentation to learn more about it. + +All configuration options available to this library may be set under the ``akka.ssl-context`` configuration for Akka HTTP applications. + +.. note:: + When encountering problems connecting to HTTPS hosts we highly encourage to reading up on the excellent ssl-config + configuration. Especially the quick start sections about `adding certificates to the trust store`_ should prove + very useful, for example to easily trust a self-signed certificate that applications might use in development mode. + +.. warning:: + While it is possible to disable certain checks using the so called "loose" settings in SSL Config, we **strongly recommend** + to instead attempt to solve these issues by properly configuring TLS–for example by adding trusted keys to the keystore. + + If however certain checks really need to be disabled because of misconfigured (or legacy) servers that your + application has to speak to, instead of disabling the checks globally (i.e. in ``application.conf``) we suggest + configuring the loose settings for *specific connections* that are known to need them disabled (and trusted for some other reason). + The pattern of doing so is documented in the folowing sub-sections. + +.. _adding certificates to the trust store: http://typesafehub.github.io/ssl-config/WSQuickStart.html#connecting-to-a-remote-server-over-https + Hostname verification ---------------------- +^^^^^^^^^^^^^^^^^^^^^ Hostname verification proves that the Akka HTTP client is actually communicating with the server it intended to communicate with. Without this check a man-in-the-middle attack is possible. In the attack scenario, an alternative @@ -57,9 +82,40 @@ The default ``HttpsContext`` enables hostname verification. Akka HTTP relies on to implement this and security options for SSL/TLS. Hostname verification is provided by the JDK and used by Akka HTTP since Java 7, and on Java 6 the verification is implemented by ssl-config manually. -.. note:: - We highly recommend updating your Java runtime to the latest available release, - preferably JDK 8, as it includes this and many more security features related to TLS. +For further recommended reading we would like to highlight the `fixing hostname verification blog post`_ by blog post by Will Sargent. -.. _Typesafe SSL-Config: https://github.com/typesafehub/ssl-config +.. _Typesafe SSL-Config: http://typesafehub.github.io/ssl-config +.. _fixing hostname verification blog post: https://tersesystems.com/2014/03/23/fixing-hostname-verification/ .. _akka.http.scaladsl.Http: @github@/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala + +Server Name Indication (SNI) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +SNI is an TLS extension which aims to guard against man-in-the-middle attacks. It does so by having the client send the +name of the virtual domain it is expecting to talk to as part of the TLS handshake. + +It is specified as part of `RFC 6066`_. + +Disabling TLS security features, at your own risk +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. warning:: + It is highly discouraged to disable any of the security features of TLS, however do acknowlage that workarounds may sometimes be needed. + + Before disabling any of the features one should consider if they may be solvable *within* the TLS world, + for example by `trusting a certificate`_, or `configuring the trusted cipher suites`_ etc. + + If disabling features is indeed desired, we recommend doing so for *specific connections*, + instead of globally configuring it via ``application.conf``. + +The following shows an example of disabling SNI for a given connection: + +.. includecode:: ../../code/docs/http/scaladsl/HttpsExamplesSpec.scala + :include: disable-sni-connection + +The ``badSslConfig`` is a copy of the default ``AkkaSSLConfig`` with with the slightly changed configuration to disable SNI. +This value can be cached and used for connections which should indeed not use this feature. + +.. _RFC 6066: https://tools.ietf.org/html/rfc6066#page-6 +.. _trusting a certificate: http://typesafehub.github.io/ssl-config/WSQuickStart.html +.. _configuring the trusted cipher suites: http://typesafehub.github.io/ssl-config/CipherSuites.html \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/ConnectionContext.scala b/akka-http-core/src/main/scala/akka/http/javadsl/ConnectionContext.scala index 817b763050..8cc72354ec 100644 --- a/akka-http-core/src/main/scala/akka/http/javadsl/ConnectionContext.scala +++ b/akka-http-core/src/main/scala/akka/http/javadsl/ConnectionContext.scala @@ -12,6 +12,7 @@ import scala.compat.java8.OptionConverters object ConnectionContext { //#https-context-creation + // ConnectionContext /** Used to serve HTTPS traffic. */ def https(sslContext: SSLContext): HttpsConnectionContext = scaladsl.ConnectionContext.https(sslContext) diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala b/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala index 985dcb45d6..e2ee99f9a1 100644 --- a/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala +++ b/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala @@ -12,6 +12,7 @@ import akka.http.javadsl.model.ws._ import akka.http.javadsl.settings.{ ConnectionPoolSettings, ClientConnectionSettings, ServerSettings } import akka.{ NotUsed, stream } import akka.stream.TLSProtocol._ +import com.typesafe.sslconfig.akka.AkkaSSLConfig import scala.concurrent.Future import scala.util.Try import akka.stream.scaladsl.Keep @@ -657,6 +658,12 @@ class Http(system: ExtendedActorSystem) extends akka.actor.Extension { def setDefaultClientHttpsContext(context: HttpsConnectionContext): Unit = delegate.setDefaultClientHttpsContext(context.asInstanceOf[akka.http.scaladsl.HttpsConnectionContext]) + def createClientHttpsContext(sslConfig: AkkaSSLConfig): HttpsConnectionContext = + delegate.createClientHttpsContext(sslConfig) + + def createDefaultClientHttpsContext(): HttpsConnectionContext = + delegate.createDefaultClientHttpsContext() + private def adaptTupleFlow[T, Mat](scalaFlow: stream.scaladsl.Flow[(scaladsl.model.HttpRequest, T), (Try[scaladsl.model.HttpResponse], T), Mat]): Flow[Pair[HttpRequest, T], Pair[Try[HttpResponse], T], Mat] = { implicit val _ = JavaMapping.identity[T] JavaMapping.toJava(scalaFlow)(JavaMapping.flowMapping[Pair[HttpRequest, T], (scaladsl.model.HttpRequest, T), Pair[Try[HttpResponse], T], (Try[scaladsl.model.HttpResponse], T), Mat]) diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/ConnectionContext.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/ConnectionContext.scala index 4bfd7eca7a..888a88956b 100644 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/ConnectionContext.scala +++ b/akka-http-core/src/main/scala/akka/http/scaladsl/ConnectionContext.scala @@ -20,6 +20,7 @@ trait ConnectionContext extends akka.http.javadsl.ConnectionContext { object ConnectionContext { //#https-context-creation + // ConnectionContext def https(sslContext: SSLContext, enabledCipherSuites: Option[immutable.Seq[String]] = None, enabledProtocols: Option[immutable.Seq[String]] = None, diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala index c299def5a3..f3471e6879 100644 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala +++ b/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala @@ -744,26 +744,27 @@ trait DefaultSSLContextCreation { private[this] def log = system.log def validateAndWarnAboutLooseSettings() = { - if (sslConfig.config.loose.disableHostnameVerification) - log.warning("Detected that Hostname Verification (via ssl-config) is disabled globally for the Http extension! " + - "This is very dangerous and may expose you to man-in-the-middle attacks. " + - "If you are forced to interact with a server that is behaving such that you must disable this setting, " + - "please disable it for a given connection instead, by configuring a specific HttpsConnectionContext " + - "for use only for the trusted target that hostname verification would have blocked.") + val WarningAboutGlobalLoose = "This is very dangerous and may expose you to man-in-the-middle attacks. " + + "If you are forced to interact with a server that is behaving such that you must disable this setting, " + + "please disable it for a given connection instead, by configuring a specific HttpsConnectionContext " + + "for use only for the trusted target that hostname verification would have blocked." - if (sslConfig.config.loose.disableSNI) - log.warning("Detected that Server Name Indication (SNI) is disabled globally (via ssl-config) for the Http extension! " + - "This is very dangerous and may expose you to man-in-the-middle attacks. " + - "If you are forced to interact with a server that is behaving such that you must disable this setting, " + - "please disable it for a given connection instead, by configuring a specific HttpsConnectionContext " + - "for use only for the trusted target that hostname verification would have blocked.") + if (sslConfig.config.loose.disableHostnameVerification) + log.warning("Detected that Hostname Verification is disabled globally (via ssl-config's akka.ssl-config.loose.disableHostnameVerification) for the Http extension! " + + WarningAboutGlobalLoose) + + if (sslConfig.config.loose.disableSNI) { + log.warning("Detected that Server Name Indication (SNI) is disabled globally (via ssl-config's akka.ssl-config.loose.disableSNI) for the Http extension! " + + WarningAboutGlobalLoose) + + } } // --- end of log warnings --- - final def createDefaultClientHttpsContext(): HttpsConnectionContext = - createDefaultClientHttpsContext(sslConfig) + def createDefaultClientHttpsContext(): HttpsConnectionContext = + createClientHttpsContext(sslConfig) - def createDefaultClientHttpsContext(sslConfig: AkkaSSLConfig): HttpsConnectionContext = { + def createClientHttpsContext(sslConfig: AkkaSSLConfig): HttpsConnectionContext = { val config = sslConfig.config val log = Logging(system, getClass) diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/SslConfigWarningsSpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/SslConfigWarningsSpec.scala index ae3933961c..d5fbc25395 100644 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/SslConfigWarningsSpec.scala +++ b/akka-http-core/src/test/scala/akka/http/scaladsl/SslConfigWarningsSpec.scala @@ -9,9 +9,9 @@ import akka.event.Logging import akka.http.scaladsl.model.HttpMethods._ import akka.http.scaladsl.model._ import akka.stream.ActorMaterializer -import akka.testkit.{EventFilter, TestKit, TestProbe} -import com.typesafe.config.{Config, ConfigFactory} -import org.scalatest.{Matchers, WordSpec} +import akka.testkit.{ EventFilter, TestKit, TestProbe } +import com.typesafe.config.{ Config, ConfigFactory } +import org.scalatest.{ Matchers, WordSpec } import scala.concurrent.Await import scala.concurrent.duration._ @@ -40,12 +40,12 @@ class SslConfigWarningsSpec extends WordSpec with Matchers { val p = TestProbe() system.eventStream.subscribe(p.ref, classOf[Logging.LogEvent]) - EventFilter.warning(start = "Detected that Server Name Indication (SNI) is disabled globally", occurrences = 1) intercept { + EventFilter.warning(start = "Detected that Server Name Indication (SNI) is disabled globally ", occurrences = 1) intercept { Http()(system) } // the very big warning shall be logged only once per actor system (extension) - EventFilter.warning(start = "Detected that Server Name Indication (SNI) is disabled globally", occurrences = 0) intercept { + EventFilter.warning(start = "Detected that Server Name Indication (SNI) is disabled globally ", occurrences = 0) intercept { Http()(system) } @@ -59,7 +59,7 @@ class SslConfigWarningsSpec extends WordSpec with Matchers { val p = TestProbe() system.eventStream.subscribe(p.ref, classOf[Logging.LogEvent]) - val msgStart = "Detected that Hostname Verification (via ssl-config) is disabled globally for the Http extension" + val msgStart = "Detected that Hostname Verification is disabled globally " EventFilter.warning(start = msgStart, occurrences = 1) intercept { Http()(system) } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 0452b9f4e6..365c90ae24 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -53,7 +53,7 @@ object Dependencies { val reactiveStreams = "org.reactivestreams" % "reactive-streams" % "1.0.0" // CC0 // ssl-config - val sslConfigAkka = "com.typesafe" %% "ssl-config-akka" % "0.2.0" // ApacheV2 + val sslConfigAkka = "com.typesafe" %% "ssl-config-akka" % "0.2.1" // ApacheV2 // For akka-http spray-json support val sprayJson = "io.spray" %% "spray-json" % "1.3.2" // ApacheV2 diff --git a/project/MiMa.scala b/project/MiMa.scala index 1ddd05a8a3..ed915dd970 100644 --- a/project/MiMa.scala +++ b/project/MiMa.scala @@ -716,6 +716,10 @@ object MiMa extends AutoPlugin { // #19390 Add flow monitor ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.stream.scaladsl.FlowOpsMat.monitor"), + // #20214 + ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.http.scaladsl.DefaultSSLContextCreation.createClientHttpsContext"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.http.scaladsl.DefaultSSLContextCreation.validateAndWarnAboutLooseSettings"), + // #20080, #20081 remove race condition on HTTP client ProblemFilters.exclude[DirectMissingMethodProblem]("akka.http.scaladsl.Http#HostConnectionPool.gatewayFuture"), ProblemFilters.exclude[IncompatibleMethTypeProblem]("akka.http.scaladsl.Http#HostConnectionPool.copy"),