This is an automated email from the ASF dual-hosted git repository.

pjfanning pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-grpc.git


The following commit(s) were added to refs/heads/main by this push:
     new af8aa19e add verify-hostname support (#820)
af8aa19e is described below

commit af8aa19e73ca07995b6b06dce2c85895dff6f726
Author: PJ Fanning <[email protected]>
AuthorDate: Wed Aug 5 11:04:51 2026 +0100

    add verify-hostname support (#820)
---
 runtime/src/main/resources/reference.conf          |  6 ++++
 .../org/apache/pekko/grpc/GrpcClientSettings.scala | 22 ++++++++++---
 .../pekko/grpc/internal/PekkoHttpClientUtils.scala | 36 ++++++++++++++++------
 3 files changed, 50 insertions(+), 14 deletions(-)

diff --git a/runtime/src/main/resources/reference.conf 
b/runtime/src/main/resources/reference.conf
index 99fcc62a..89a84f85 100644
--- a/runtime/src/main/resources/reference.conf
+++ b/runtime/src/main/resources/reference.conf
@@ -38,6 +38,12 @@ pekko.grpc.client."*" {
   # leave empty to auto-detect, or configure 'jdk' or 'openssl'.
   ssl-provider = ""
 
+  # Whether to verify the server's hostname against its TLS certificate (RFC 
2818).
+  # When false (the default), the client accepts any valid certificate 
regardless
+  # of hostname. This is insecure for production and should only be used for 
testing.
+  # Only effective for the pekko-http backend; the netty backend always 
verifies.
+  verify-hostname = false
+
   # TODO: Enforce HTTP/2 TLS restrictions: 
https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-9.2
 
   # The number of times to try connecting before giving up.
diff --git 
a/runtime/src/main/scala/org/apache/pekko/grpc/GrpcClientSettings.scala 
b/runtime/src/main/scala/org/apache/pekko/grpc/GrpcClientSettings.scala
index 8b933314..16eb934b 100644
--- a/runtime/src/main/scala/org/apache/pekko/grpc/GrpcClientSettings.scala
+++ b/runtime/src/main/scala/org/apache/pekko/grpc/GrpcClientSettings.scala
@@ -158,7 +158,8 @@ object GrpcClientSettings {
       getOptionalString(clientConfiguration, "user-agent"),
       clientConfiguration.getBoolean("use-tls"),
       getOptionalString(clientConfiguration, "load-balancing-policy"),
-      clientConfiguration.getString("backend"))
+      clientConfiguration.getString("backend"),
+      verifyHostname = clientConfiguration.getBoolean("verify-hostname"))
 
   private def getOptionalString(config: Config, path: String): Option[String] =
     config.getString(path) match {
@@ -206,7 +207,8 @@ final class GrpcClientSettings private (
     val useTls: Boolean,
     val loadBalancingPolicy: Option[String],
     val backend: String,
-    val channelBuilderOverrides: NettyChannelBuilder => NettyChannelBuilder = 
identity) {
+    val channelBuilderOverrides: NettyChannelBuilder => NettyChannelBuilder = 
identity,
+    val verifyHostname: Boolean) {
   require(
     sslContext.isEmpty || trustManager.isEmpty,
     "Configuring the sslContext or the trustManager is mutually exclusive")
@@ -289,6 +291,16 @@ final class GrpcClientSettings private (
   def withBackend(value: String): GrpcClientSettings =
     copy(backend = value)
 
+  /**
+   * Whether to verify the server's hostname against its TLS certificate (RFC 
2818).
+   * When false (the default), the client accepts any valid certificate 
regardless
+   * of hostname. This is insecure for production and should only be used for 
testing.
+   * Only effective for the pekko-http backend; the netty backend always 
verifies.
+   * @since 2.0.0
+   */
+  def withVerifyHostname(value: Boolean): GrpcClientSettings =
+    copy(verifyHostname = value)
+
   private def copy(
       serviceName: String = serviceName,
       servicePortName: Option[String] = servicePortName,
@@ -306,7 +318,8 @@ final class GrpcClientSettings private (
       connectionAttempts: Option[Int] = connectionAttempts,
       loadBalancingPolicy: Option[String] = loadBalancingPolicy,
       backend: String = backend,
-      channelBuilderOverrides: NettyChannelBuilder => NettyChannelBuilder = 
channelBuilderOverrides)
+      channelBuilderOverrides: NettyChannelBuilder => NettyChannelBuilder = 
channelBuilderOverrides,
+      verifyHostname: Boolean = verifyHostname)
       : GrpcClientSettings =
     new GrpcClientSettings(
       callCredentials = callCredentials,
@@ -326,5 +339,6 @@ final class GrpcClientSettings private (
       connectionAttempts = connectionAttempts,
       loadBalancingPolicy = loadBalancingPolicy,
       backend = backend,
-      channelBuilderOverrides = channelBuilderOverrides)
+      channelBuilderOverrides = channelBuilderOverrides,
+      verifyHostname = verifyHostname)
 }
diff --git 
a/runtime/src/main/scala/org/apache/pekko/grpc/internal/PekkoHttpClientUtils.scala
 
b/runtime/src/main/scala/org/apache/pekko/grpc/internal/PekkoHttpClientUtils.scala
index c9797961..f4fe196e 100644
--- 
a/runtime/src/main/scala/org/apache/pekko/grpc/internal/PekkoHttpClientUtils.scala
+++ 
b/runtime/src/main/scala/org/apache/pekko/grpc/internal/PekkoHttpClientUtils.scala
@@ -96,18 +96,34 @@ object PekkoHttpClientUtils {
 
     val http2client =
       if (settings.useTls) {
-        val connectionContext =
-          ConnectionContext.httpsClient {
-            settings.sslContext.getOrElse {
-              settings.trustManager match {
-                case None               => SSLContext.getDefault
-                case Some(trustManager) =>
-                  val sslContext: SSLContext = SSLContext.getInstance("TLS")
-                  sslContext.init(Array[KeyManager](), 
Array[TrustManager](trustManager), new SecureRandom)
-                  sslContext
-              }
+        if (!settings.verifyHostname) {
+          log.warning(
+            "TLS hostname verification is disabled for pekko-http client '{}'. 
" +
+            "This is insecure and should only be used for testing. " +
+            "Enable it with verify-hostname = true in your configuration. " +
+            "Note: the netty backend always verifies hostnames.",
+            settings.serviceName)
+        }
+        val sslContext =
+          settings.sslContext.getOrElse {
+            settings.trustManager match {
+              case None               => SSLContext.getDefault
+              case Some(trustManager) =>
+                val ctx: SSLContext = SSLContext.getInstance("TLS")
+                ctx.init(Array[KeyManager](), 
Array[TrustManager](trustManager), new SecureRandom)
+                ctx
             }
           }
+        val connectionContext =
+          ConnectionContext.httpsClient((hostname, port) => {
+            val engine = sslContext.createSSLEngine(hostname, port)
+            if (settings.verifyHostname) {
+              val sslParams = engine.getSSLParameters
+              sslParams.setEndpointIdentificationAlgorithm("HTTPS")
+              engine.setSSLParameters(sslParams)
+            }
+            engine
+          })
 
         
builder.withCustomHttpsConnectionContext(connectionContext).managedPersistentHttp2()
       } else {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to