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

weichiu pushed a commit to branch branch-3.4
in repository https://gitbox.apache.org/repos/asf/hadoop.git


The following commit(s) were added to refs/heads/branch-3.4 by this push:
     new 5173c0e934d HADOOP-19546. Include cipher feature for HttpServer2 and 
SSLFactory (#7629)
5173c0e934d is described below

commit 5173c0e934d4895d7824c4f44a28374c9bf6f66d
Author: K0K0V0K <109747532+k0k0...@users.noreply.github.com>
AuthorDate: Sat Apr 19 01:17:51 2025 +0200

    HADOOP-19546. Include cipher feature for HttpServer2 and SSLFactory (#7629)
    
    (cherry picked from commit 7d474d37fe353a5dc05a6bfc4f1a80c5094eda99)
---
 .../src/main/conf/ssl-server.xml.example           | 20 ++++++++++-
 .../java/org/apache/hadoop/http/HttpServer2.java   | 18 ++++++++--
 .../org/apache/hadoop/security/ssl/SSLFactory.java | 41 +++++++++++-----------
 .../java/org/apache/hadoop/util/StringUtils.java   | 10 ++++++
 .../org/apache/hadoop/http/TestHttpCookieFlag.java |  4 +--
 .../org/apache/hadoop/http/TestSSLHttpServer.java  |  4 ++-
 .../hadoop/http/TestSSLHttpServerConfigs.java      |  4 ++-
 .../main/java/org/apache/hadoop/hdfs/DFSUtil.java  |  4 ++-
 .../hadoop/yarn/webapp/util/WebAppUtils.java       |  4 ++-
 .../yarn/webapp/util/TestWebServiceClient.java     |  4 ++-
 10 files changed, 83 insertions(+), 30 deletions(-)

diff --git 
a/hadoop-common-project/hadoop-common/src/main/conf/ssl-server.xml.example 
b/hadoop-common-project/hadoop-common/src/main/conf/ssl-server.xml.example
index a6820e952d5..cf1aac04d06 100644
--- a/hadoop-common-project/hadoop-common/src/main/conf/ssl-server.xml.example
+++ b/hadoop-common-project/hadoop-common/src/main/conf/ssl-server.xml.example
@@ -82,7 +82,25 @@
   SSL_RSA_EXPORT_WITH_RC4_40_MD5,SSL_RSA_EXPORT_WITH_DES40_CBC_SHA,
   SSL_RSA_WITH_RC4_128_MD5</value>
   <description>Optional. The weak security cipher suites that you want excluded
-  from SSL communication.</description>
+  from SSL communication.
+  Both ssl.server.include.cipher.list and ssl.server.exclude.cipher.list can 
be used simultaneously
+  to fine-tune the cipher suites utilized by Hadoop services.
+  If a cipher suite is present in both the inclusion and exclusion lists, it 
will be denied.
+  </description>
+</property>
+
+<property>
+  <name>ssl.server.include.cipher.list</name>
+  
<value>TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+  
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+  TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256,
+  TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256</value>
+  <description>Optional. If the inclusion list is populated,
+  any cipher not present in the list will not be allowed.
+  Both ssl.server.include.cipher.list and ssl.server.exclude.cipher.list can 
be used simultaneously
+  to fine-tune the cipher suites utilized by Hadoop services.
+  If a cipher suite is present in both the inclusion and exclusion lists, it 
will be denied.
+  </description>
 </property>
 
 </configuration>
diff --git 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/http/HttpServer2.java
 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/http/HttpServer2.java
index d4bfa41b21c..161c13e2ef3 100644
--- 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/http/HttpServer2.java
+++ 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/http/HttpServer2.java
@@ -252,6 +252,7 @@ public static class Builder {
         new ArrayList<>(Collections.singletonList(
             "hadoop.http.authentication."));
     private String excludeCiphers;
+    private String includeCiphers;
 
     private boolean xFrameEnabled;
     private XFrameOption xFrameOption = XFrameOption.SAMEORIGIN;
@@ -399,6 +400,11 @@ public Builder excludeCiphers(String pExcludeCiphers) {
       return this;
     }
 
+    public Builder includeCiphers(String pIncludeCiphers) {
+      this.includeCiphers = pIncludeCiphers;
+      return this;
+    }
+
     /**
      * Adds the ability to control X_FRAME_OPTIONS on HttpServer2.
      * @param xFrameEnabled - True enables X_FRAME_OPTIONS false disables it.
@@ -480,6 +486,7 @@ private void loadSSLConfiguration() throws IOException {
       trustStoreType = sslConf.get(SSLFactory.SSL_SERVER_TRUSTSTORE_TYPE,
           SSLFactory.SSL_SERVER_TRUSTSTORE_TYPE_DEFAULT);
       excludeCiphers = sslConf.get(SSLFactory.SSL_SERVER_EXCLUDE_CIPHER_LIST);
+      includeCiphers = sslConf.get(SSLFactory.SSL_SERVER_INCLUDE_CIPHER_LIST);
     }
 
     public HttpServer2 build() throws IOException {
@@ -607,10 +614,17 @@ private ServerConnector createHttpsChannelConnector(
           sslContextFactory.setTrustStorePassword(trustStorePassword);
         }
       }
-      if(null != excludeCiphers && !excludeCiphers.isEmpty()) {
+
+      if (StringUtils.hasLength(excludeCiphers)) {
         sslContextFactory.setExcludeCipherSuites(
             StringUtils.getTrimmedStrings(excludeCiphers));
-        LOG.info("Excluded Cipher List:" + excludeCiphers);
+        LOG.info("Excluded Cipher List:{}", excludeCiphers);
+      }
+
+      if (StringUtils.hasLength(includeCiphers)) {
+        sslContextFactory.setIncludeCipherSuites(
+          StringUtils.getTrimmedStrings(includeCiphers));
+        LOG.info("Included Cipher List:{}", includeCiphers);
       }
 
       setEnabledProtocols(sslContextFactory);
diff --git 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/ssl/SSLFactory.java
 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/ssl/SSLFactory.java
index 5ab38aa7420..eb8d8f6c37a 100644
--- 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/ssl/SSLFactory.java
+++ 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/ssl/SSLFactory.java
@@ -17,6 +17,7 @@
 */
 package org.apache.hadoop.security.ssl;
 
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
 import org.apache.hadoop.conf.Configuration;
@@ -38,10 +39,9 @@
 import java.io.IOException;
 import java.net.HttpURLConnection;
 import java.security.GeneralSecurityException;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Iterator;
 import java.util.List;
+import java.util.stream.Stream;
 
 /**
  * Factory that creates SSLEngine and SSLSocketFactory instances using
@@ -101,6 +101,9 @@ public enum Mode { CLIENT, SERVER }
   public static final String SSL_SERVER_EXCLUDE_CIPHER_LIST =
       "ssl.server.exclude.cipher.list";
 
+  public static final String SSL_SERVER_INCLUDE_CIPHER_LIST =
+      "ssl.server.include.cipher.list";
+
   public static final String KEY_MANAGER_SSLCERTIFICATE =
       IBM_JAVA ? "ibmX509" :
           KeyManagerFactory.getDefaultAlgorithm();
@@ -125,6 +128,7 @@ public enum Mode { CLIENT, SERVER }
 
   private String[] enabledProtocols = null;
   private List<String> excludeCiphers;
+  private List<String> includeCiphers;
 
   /**
    * Creates an SSLFactory.
@@ -153,9 +157,13 @@ public SSLFactory(Mode mode, Configuration conf) {
         SSL_ENABLED_PROTOCOLS_DEFAULT);
     excludeCiphers = Arrays.asList(
         sslConf.getTrimmedStrings(SSL_SERVER_EXCLUDE_CIPHER_LIST));
+    includeCiphers = Arrays.asList(
+      sslConf.getTrimmedStrings(SSL_SERVER_INCLUDE_CIPHER_LIST));
     if (LOG.isDebugEnabled()) {
       LOG.debug("will exclude cipher suites: {}",
           StringUtils.join(",", excludeCiphers));
+      LOG.debug("will include cipher suites: {}",
+          StringUtils.join(",", includeCiphers));
     }
   }
 
@@ -261,30 +269,23 @@ public SSLEngine createSSLEngine()
     } else {
       sslEngine.setUseClientMode(false);
       sslEngine.setNeedClientAuth(requireClientCert);
-      disableExcludedCiphers(sslEngine);
+      callSetEnabledCipherSuites(sslEngine);
     }
     sslEngine.setEnabledProtocols(enabledProtocols);
     return sslEngine;
   }
 
-  private void disableExcludedCiphers(SSLEngine sslEngine) {
-    String[] cipherSuites = sslEngine.getEnabledCipherSuites();
-
-    ArrayList<String> defaultEnabledCipherSuites =
-        new ArrayList<String>(Arrays.asList(cipherSuites));
-    Iterator iterator = excludeCiphers.iterator();
-
-    while(iterator.hasNext()) {
-      String cipherName = (String)iterator.next();
-      if(defaultEnabledCipherSuites.contains(cipherName)) {
-        defaultEnabledCipherSuites.remove(cipherName);
-        LOG.debug("Disabling cipher suite {}.", cipherName);
-      }
+  private void callSetEnabledCipherSuites(SSLEngine sslEngine) {
+    Stream<String> cipherSuites = 
Arrays.stream(sslEngine.getSupportedCipherSuites());
+    if (CollectionUtils.isNotEmpty(includeCiphers)) {
+      cipherSuites = cipherSuites.filter(s -> includeCiphers.contains(s));
     }
-
-    cipherSuites = defaultEnabledCipherSuites.toArray(
-        new String[defaultEnabledCipherSuites.size()]);
-    sslEngine.setEnabledCipherSuites(cipherSuites);
+    if (CollectionUtils.isNotEmpty(excludeCiphers)) {
+      cipherSuites = cipherSuites.filter(s -> !excludeCiphers.contains(s));
+    }
+    String[] enabledCipherSuites = cipherSuites.toArray(String[]::new);
+    LOG.debug("Enabled cipher suites: {}", StringUtils.join(",", 
enabledCipherSuites));
+    sslEngine.setEnabledCipherSuites(enabledCipherSuites);
   }
 
   /**
diff --git 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/StringUtils.java
 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/StringUtils.java
index 2585729950b..9057871894f 100644
--- 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/StringUtils.java
+++ 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/StringUtils.java
@@ -1354,4 +1354,14 @@ public static String wrap(String str, int wrapLength, 
String newLineStr,
       return wrappedLine.toString();
     }
   }
+
+  /**
+   * Checks whether the given string is not {@code null} and has a length 
greater than zero.
+   *
+   * @param str the string to check
+   * @return {@code true} if the string is not {@code null} and not empty; 
{@code false} otherwise
+   */
+  public static boolean hasLength(String str) {
+    return str != null && !str.isEmpty();
+  }
 }
diff --git 
a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestHttpCookieFlag.java
 
b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestHttpCookieFlag.java
index 8a722dff88d..e2d41e67115 100644
--- 
a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestHttpCookieFlag.java
+++ 
b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestHttpCookieFlag.java
@@ -104,8 +104,8 @@ public static void setUp() throws Exception {
             .trustStore(sslConf.get("ssl.server.truststore.location"),
                     sslConf.get("ssl.server.truststore.password"),
                     sslConf.get("ssl.server.truststore.type", "jks"))
-            .excludeCiphers(
-                    sslConf.get("ssl.server.exclude.cipher.list"))
+            .excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list"))
+            .includeCiphers(sslConf.get("ssl.server.include.cipher.list"))
             .build();
     server.addServlet("echo", "/echo", TestHttpServer.EchoServlet.class);
     server.start();
diff --git 
a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestSSLHttpServer.java
 
b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestSSLHttpServer.java
index cc76b4ad6d9..5ffed17b38c 100644
--- 
a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestSSLHttpServer.java
+++ 
b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestSSLHttpServer.java
@@ -149,7 +149,9 @@ private static void setupServer(Configuration conf, 
Configuration sslConf)
             sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".location"),
             sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".password"),
             sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".type", "jks"))
-        .excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list")).build();
+        .excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list"))
+        .includeCiphers(sslConf.get("ssl.server.include.cipher.list"))
+        .build();
     server.addServlet(SERVLET_NAME_ECHO, SERVLET_PATH_ECHO, EchoServlet.class);
     server.addServlet(SERVLET_NAME_LONGHEADER, SERVLET_PATH_LONGHEADER,
         LongHeaderServlet.class);
diff --git 
a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestSSLHttpServerConfigs.java
 
b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestSSLHttpServerConfigs.java
index 039fae01957..f5c4c59d861 100644
--- 
a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestSSLHttpServerConfigs.java
+++ 
b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestSSLHttpServerConfigs.java
@@ -109,7 +109,9 @@ private HttpServer2 setupServer(String keyStoreKeyPassword,
             sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".location"),
             trustStorePassword,
             sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".type", "jks"))
-        .excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list")).build();
+        .excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list"))
+        .includeCiphers(sslConf.get("ssl.server.include.cipher.list"))
+        .build();
 
     return server;
   }
diff --git 
a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSUtil.java
 
b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSUtil.java
index 3ecb20bc6a2..2c52b58d7cd 100644
--- 
a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSUtil.java
+++ 
b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSUtil.java
@@ -1630,7 +1630,9 @@ public static HttpServer2.Builder 
loadSslConfToHttpServerBuilder(HttpServer2.Bui
             getPassword(sslConf, DFS_SERVER_HTTPS_TRUSTSTORE_PASSWORD_KEY),
             sslConf.get("ssl.server.truststore.type", "jks"))
         .excludeCiphers(
-            sslConf.get("ssl.server.exclude.cipher.list"));
+            sslConf.get("ssl.server.exclude.cipher.list"))
+        .includeCiphers(
+            sslConf.get("ssl.server.include.cipher.list"));
   }
 
   /**
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/util/WebAppUtils.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/util/WebAppUtils.java
index ff04ffe47a2..c0d7021f302 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/util/WebAppUtils.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/util/WebAppUtils.java
@@ -492,7 +492,9 @@ public static HttpServer2.Builder loadSslConfiguration(
             getPassword(sslConf, WEB_APP_TRUSTSTORE_PASSWORD_KEY),
             sslConf.get("ssl.server.truststore.type", "jks"))
         .excludeCiphers(
-            sslConf.get("ssl.server.exclude.cipher.list"));
+            sslConf.get("ssl.server.exclude.cipher.list"))
+        .includeCiphers(
+            sslConf.get("ssl.server.include.cipher.list"));
   }
 
   /**
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/webapp/util/TestWebServiceClient.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/webapp/util/TestWebServiceClient.java
index b51dcf88bcb..a5a5e9024dd 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/webapp/util/TestWebServiceClient.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/webapp/util/TestWebServiceClient.java
@@ -83,7 +83,9 @@ void testCreateClient() throws Exception {
             sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".location"),
             sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".password"),
             sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".type", "jks"))
-        .excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list")).build();
+        .excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list"))
+        .includeCiphers(sslConf.get("ssl.server.include.cipher.list"))
+        .build();
     server.addServlet(SERVLET_NAME_ECHO, SERVLET_PATH_ECHO, EchoServlet.class);
     server.start();
 


---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscr...@hadoop.apache.org
For additional commands, e-mail: common-commits-h...@hadoop.apache.org

Reply via email to