Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/guides/implementing-mutual-tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ object ServerApp extends ZIOAppDefault {
),
includeClientCert = false,
clientAuth = Some(ClientAuth.Required),
protocols = Seq("TLSv1.3", "TLSv1.2"),
)

private val serverConfig =
Expand All @@ -201,6 +202,8 @@ Please note that we enabled the `ClientAuth.Required` option in the SSL configur

If we want to access the client certificate, we can enable the `includeClientCert` option in the SSL configuration. This allows us to access the client certificate via `req.remoteCertificate` in the request handler.

The `protocols` parameter in `SSLConfig` allows configuring supported TLS protocol versions. This is useful for disabling older protocol versions for security reasons.

### Client Implementation

Similarly, the client implementation for mTLS requires both a keystore (containing the client's certificate and private key) and a truststore (containing the CA certificate used to verify the server's certificate). The client will automatically send its certificate during the TLS handshake if configured correctly:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ private[netty] object SSLUtil {
clientAuthConfig.foreach(ca => self.clientAuth(getClientAuth(ca)))
self
.sslProvider(toNettyProvider(sslConfig.provider))
.protocols(sslConfig.protocols: _*)
.applicationProtocolConfig(
new ApplicationProtocolConfig(
Protocol.ALPN,
Expand Down
55 changes: 54 additions & 1 deletion zio-http/shared/src/main/scala/zio/http/SSLConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package zio.http

import scala.annotation.unroll

import zio.Config
import zio.Config.Secret

Expand All @@ -36,7 +38,35 @@ final case class SSLConfig(
provider: Provider,
clientAuth: Option[ClientAuth] = None,
includeClientCert: Boolean = false,
)
protocols: Seq[String] = Seq("TLSv1.3", "TLSv1.2"),
) {
// @unroll annotation does not work here, doing manual unroll for binary compatibility

def this(
behaviour: HttpBehaviour,
data: Data,
provider: Provider,
clientAuth: Option[ClientAuth],
includeClientCert: Boolean,
) = this(behaviour, data, provider, clientAuth, includeClientCert, Seq("TLSv1.3", "TLSv1.2"))

def copy(
behaviour: HttpBehaviour = this.behaviour,
data: Data = this.data,
provider: Provider = this.provider,
clientAuth: Option[ClientAuth] = this.clientAuth,
includeClientCert: Boolean = this.includeClientCert,
protocols: Seq[String] = this.protocols,
): SSLConfig = SSLConfig(behaviour, data, provider, clientAuth, includeClientCert, protocols)

def copy(
behaviour: HttpBehaviour,
data: Data,
provider: Provider,
clientAuth: Option[ClientAuth],
includeClientCert: Boolean,
): SSLConfig = SSLConfig(behaviour, data, provider, clientAuth, includeClientCert, this.protocols)
}

object SSLConfig {

Expand All @@ -46,6 +76,14 @@ object SSLConfig {
def apply(data: Data, clientAuth: ClientAuth): SSLConfig =
new SSLConfig(HttpBehaviour.Redirect, data, Provider.JDK, Some(clientAuth))

def apply(
behaviour: HttpBehaviour,
data: Data,
provider: Provider,
clientAuth: Option[ClientAuth],
includeClientCert: Boolean,
): SSLConfig = new SSLConfig(behaviour, data, provider, clientAuth, includeClientCert)

val config: Config[SSLConfig] =
(
HttpBehaviour.config.nested("behaviour") ++
Expand All @@ -68,13 +106,16 @@ object SSLConfig {
clientAuth: Option[ClientAuth] = None,
trustCertCollectionPath: Option[String] = None,
includeClientCert: Boolean = false,
@unroll
protocols: Seq[String] = Seq("TLSv1.3", "TLSv1.2"),
): SSLConfig =
new SSLConfig(
behaviour,
Data.FromFile(certPath, keyPath, trustCertCollectionPath),
Provider.JDK,
clientAuth,
includeClientCert,
protocols,
)

def fromResource(certPath: String, keyPath: String): SSLConfig =
Expand All @@ -90,13 +131,16 @@ object SSLConfig {
clientAuth: Option[ClientAuth] = None,
trustCertCollectionPath: Option[String] = None,
includeClientCert: Boolean = false,
@unroll
protocols: Seq[String] = Seq("TLSv1.3", "TLSv1.2"),
): SSLConfig =
new SSLConfig(
behaviour,
Data.FromResource(certPath, keyPath, trustCertCollectionPath),
Provider.JDK,
clientAuth,
includeClientCert,
protocols,
)

def fromJavaxNetSslKeyStoreFile(
Expand All @@ -107,6 +151,8 @@ object SSLConfig {
trustManagerKeyStore: Option[Data.TrustManagerKeyStore] = None,
clientAuth: Option[ClientAuth] = None,
includeClientCert: Boolean = false,
@unroll
protocols: Seq[String] = Seq("TLSv1.3", "TLSv1.2"),
): SSLConfig =
new SSLConfig(
behaviour,
Expand All @@ -119,6 +165,7 @@ object SSLConfig {
Provider.JDK,
clientAuth,
includeClientCert,
protocols,
)

def fromJavaxNetSslKeyStoreFile(keyManagerFile: String, keyManagerPassword: Secret): SSLConfig =
Expand All @@ -131,6 +178,8 @@ object SSLConfig {
trustManagerKeyStore: Option[Data.TrustManagerKeyStore] = None,
clientAuth: Option[ClientAuth] = None,
includeClientCert: Boolean = false,
@unroll
protocols: Seq[String] = Seq("TLSv1.3", "TLSv1.2"),
): SSLConfig = {
fromJavaxNetSsl(
Data.FromJavaxNetSsl(
Expand All @@ -142,6 +191,7 @@ object SSLConfig {
HttpBehaviour.Redirect,
clientAuth,
includeClientCert,
protocols,
)
}

Expand All @@ -150,13 +200,16 @@ object SSLConfig {
behaviour: HttpBehaviour = HttpBehaviour.Redirect,
clientAuth: Option[ClientAuth] = None,
includeClientCert: Boolean = false,
@unroll
protocols: Seq[String] = Seq("TLSv1.3", "TLSv1.2"),
): SSLConfig =
new SSLConfig(
behaviour,
data,
Provider.JDK,
clientAuth,
includeClientCert,
protocols,
)

def fromJavaxNetSslKeyStoreResource(keyManagerResource: String, keyManagerPassword: Secret): SSLConfig =
Expand Down
Loading