Skip to content

Commit 7d474d3

Browse files
authored
HADOOP-19546. Include cipher feature for HttpServer2 and SSLFactory (#7629)
1 parent f79f5bc commit 7d474d3

File tree

10 files changed

+83
-30
lines changed

10 files changed

+83
-30
lines changed

hadoop-common-project/hadoop-common/src/main/conf/ssl-server.xml.example

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,25 @@
8282
SSL_RSA_EXPORT_WITH_RC4_40_MD5,SSL_RSA_EXPORT_WITH_DES40_CBC_SHA,
8383
SSL_RSA_WITH_RC4_128_MD5</value>
8484
<description>Optional. The weak security cipher suites that you want excluded
85-
from SSL communication.</description>
85+
from SSL communication.
86+
Both ssl.server.include.cipher.list and ssl.server.exclude.cipher.list can be used simultaneously
87+
to fine-tune the cipher suites utilized by Hadoop services.
88+
If a cipher suite is present in both the inclusion and exclusion lists, it will be denied.
89+
</description>
90+
</property>
91+
92+
<property>
93+
<name>ssl.server.include.cipher.list</name>
94+
<value>TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
95+
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
96+
TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256,
97+
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256</value>
98+
<description>Optional. If the inclusion list is populated,
99+
any cipher not present in the list will not be allowed.
100+
Both ssl.server.include.cipher.list and ssl.server.exclude.cipher.list can be used simultaneously
101+
to fine-tune the cipher suites utilized by Hadoop services.
102+
If a cipher suite is present in both the inclusion and exclusion lists, it will be denied.
103+
</description>
86104
</property>
87105

88106
</configuration>

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/http/HttpServer2.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ public static class Builder {
253253
new ArrayList<>(Collections.singletonList(
254254
"hadoop.http.authentication."));
255255
private String excludeCiphers;
256+
private String includeCiphers;
256257

257258
private boolean xFrameEnabled;
258259
private XFrameOption xFrameOption = XFrameOption.SAMEORIGIN;
@@ -400,6 +401,11 @@ public Builder excludeCiphers(String pExcludeCiphers) {
400401
return this;
401402
}
402403

404+
public Builder includeCiphers(String pIncludeCiphers) {
405+
this.includeCiphers = pIncludeCiphers;
406+
return this;
407+
}
408+
403409
/**
404410
* Adds the ability to control X_FRAME_OPTIONS on HttpServer2.
405411
* @param xFrameEnabled - True enables X_FRAME_OPTIONS false disables it.
@@ -481,6 +487,7 @@ private void loadSSLConfiguration() throws IOException {
481487
trustStoreType = sslConf.get(SSLFactory.SSL_SERVER_TRUSTSTORE_TYPE,
482488
SSLFactory.SSL_SERVER_TRUSTSTORE_TYPE_DEFAULT);
483489
excludeCiphers = sslConf.get(SSLFactory.SSL_SERVER_EXCLUDE_CIPHER_LIST);
490+
includeCiphers = sslConf.get(SSLFactory.SSL_SERVER_INCLUDE_CIPHER_LIST);
484491
}
485492

486493
public HttpServer2 build() throws IOException {
@@ -608,10 +615,17 @@ private ServerConnector createHttpsChannelConnector(
608615
sslContextFactory.setTrustStorePassword(trustStorePassword);
609616
}
610617
}
611-
if(null != excludeCiphers && !excludeCiphers.isEmpty()) {
618+
619+
if (StringUtils.hasLength(excludeCiphers)) {
612620
sslContextFactory.setExcludeCipherSuites(
613621
StringUtils.getTrimmedStrings(excludeCiphers));
614-
LOG.info("Excluded Cipher List:" + excludeCiphers);
622+
LOG.info("Excluded Cipher List:{}", excludeCiphers);
623+
}
624+
625+
if (StringUtils.hasLength(includeCiphers)) {
626+
sslContextFactory.setIncludeCipherSuites(
627+
StringUtils.getTrimmedStrings(includeCiphers));
628+
LOG.info("Included Cipher List:{}", includeCiphers);
615629
}
616630

617631
setEnabledProtocols(sslContextFactory);

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/ssl/SSLFactory.java

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
package org.apache.hadoop.security.ssl;
1919

20+
import org.apache.commons.collections4.CollectionUtils;
2021
import org.apache.hadoop.classification.InterfaceAudience;
2122
import org.apache.hadoop.classification.InterfaceStability;
2223
import org.apache.hadoop.conf.Configuration;
@@ -38,10 +39,9 @@
3839
import java.io.IOException;
3940
import java.net.HttpURLConnection;
4041
import java.security.GeneralSecurityException;
41-
import java.util.ArrayList;
4242
import java.util.Arrays;
43-
import java.util.Iterator;
4443
import java.util.List;
44+
import java.util.stream.Stream;
4545

4646
/**
4747
* Factory that creates SSLEngine and SSLSocketFactory instances using
@@ -101,6 +101,9 @@ public enum Mode { CLIENT, SERVER }
101101
public static final String SSL_SERVER_EXCLUDE_CIPHER_LIST =
102102
"ssl.server.exclude.cipher.list";
103103

104+
public static final String SSL_SERVER_INCLUDE_CIPHER_LIST =
105+
"ssl.server.include.cipher.list";
106+
104107
public static final String KEY_MANAGER_SSLCERTIFICATE =
105108
IBM_JAVA ? "ibmX509" :
106109
KeyManagerFactory.getDefaultAlgorithm();
@@ -125,6 +128,7 @@ public enum Mode { CLIENT, SERVER }
125128

126129
private String[] enabledProtocols = null;
127130
private List<String> excludeCiphers;
131+
private List<String> includeCiphers;
128132

129133
/**
130134
* Creates an SSLFactory.
@@ -153,9 +157,13 @@ public SSLFactory(Mode mode, Configuration conf) {
153157
SSL_ENABLED_PROTOCOLS_DEFAULT);
154158
excludeCiphers = Arrays.asList(
155159
sslConf.getTrimmedStrings(SSL_SERVER_EXCLUDE_CIPHER_LIST));
160+
includeCiphers = Arrays.asList(
161+
sslConf.getTrimmedStrings(SSL_SERVER_INCLUDE_CIPHER_LIST));
156162
if (LOG.isDebugEnabled()) {
157163
LOG.debug("will exclude cipher suites: {}",
158164
StringUtils.join(",", excludeCiphers));
165+
LOG.debug("will include cipher suites: {}",
166+
StringUtils.join(",", includeCiphers));
159167
}
160168
}
161169

@@ -261,30 +269,23 @@ public SSLEngine createSSLEngine()
261269
} else {
262270
sslEngine.setUseClientMode(false);
263271
sslEngine.setNeedClientAuth(requireClientCert);
264-
disableExcludedCiphers(sslEngine);
272+
callSetEnabledCipherSuites(sslEngine);
265273
}
266274
sslEngine.setEnabledProtocols(enabledProtocols);
267275
return sslEngine;
268276
}
269277

270-
private void disableExcludedCiphers(SSLEngine sslEngine) {
271-
String[] cipherSuites = sslEngine.getEnabledCipherSuites();
272-
273-
ArrayList<String> defaultEnabledCipherSuites =
274-
new ArrayList<String>(Arrays.asList(cipherSuites));
275-
Iterator iterator = excludeCiphers.iterator();
276-
277-
while(iterator.hasNext()) {
278-
String cipherName = (String)iterator.next();
279-
if(defaultEnabledCipherSuites.contains(cipherName)) {
280-
defaultEnabledCipherSuites.remove(cipherName);
281-
LOG.debug("Disabling cipher suite {}.", cipherName);
282-
}
278+
private void callSetEnabledCipherSuites(SSLEngine sslEngine) {
279+
Stream<String> cipherSuites = Arrays.stream(sslEngine.getSupportedCipherSuites());
280+
if (CollectionUtils.isNotEmpty(includeCiphers)) {
281+
cipherSuites = cipherSuites.filter(s -> includeCiphers.contains(s));
283282
}
284-
285-
cipherSuites = defaultEnabledCipherSuites.toArray(
286-
new String[defaultEnabledCipherSuites.size()]);
287-
sslEngine.setEnabledCipherSuites(cipherSuites);
283+
if (CollectionUtils.isNotEmpty(excludeCiphers)) {
284+
cipherSuites = cipherSuites.filter(s -> !excludeCiphers.contains(s));
285+
}
286+
String[] enabledCipherSuites = cipherSuites.toArray(String[]::new);
287+
LOG.debug("Enabled cipher suites: {}", StringUtils.join(",", enabledCipherSuites));
288+
sslEngine.setEnabledCipherSuites(enabledCipherSuites);
288289
}
289290

290291
/**

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/StringUtils.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,4 +1367,14 @@ public static String wrap(String str, int wrapLength, String newLineStr,
13671367
return wrappedLine.toString();
13681368
}
13691369
}
1370+
1371+
/**
1372+
* Checks whether the given string is not {@code null} and has a length greater than zero.
1373+
*
1374+
* @param str the string to check
1375+
* @return {@code true} if the string is not {@code null} and not empty; {@code false} otherwise
1376+
*/
1377+
public static boolean hasLength(String str) {
1378+
return str != null && !str.isEmpty();
1379+
}
13701380
}

hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestHttpCookieFlag.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ public static void setUp() throws Exception {
105105
.trustStore(sslConf.get("ssl.server.truststore.location"),
106106
sslConf.get("ssl.server.truststore.password"),
107107
sslConf.get("ssl.server.truststore.type", "jks"))
108-
.excludeCiphers(
109-
sslConf.get("ssl.server.exclude.cipher.list"))
108+
.excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list"))
109+
.includeCiphers(sslConf.get("ssl.server.include.cipher.list"))
110110
.build();
111111
server.addServlet("echo", "/echo", TestHttpServer.EchoServlet.class);
112112
server.start();

hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestSSLHttpServer.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,9 @@ private static void setupServer(Configuration conf, Configuration sslConf)
149149
sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".location"),
150150
sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".password"),
151151
sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".type", "jks"))
152-
.excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list")).build();
152+
.excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list"))
153+
.includeCiphers(sslConf.get("ssl.server.include.cipher.list"))
154+
.build();
153155
server.addServlet(SERVLET_NAME_ECHO, SERVLET_PATH_ECHO, EchoServlet.class);
154156
server.addServlet(SERVLET_NAME_LONGHEADER, SERVLET_PATH_LONGHEADER,
155157
LongHeaderServlet.class);

hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/http/TestSSLHttpServerConfigs.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ private HttpServer2 setupServer(String keyStoreKeyPassword,
110110
sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".location"),
111111
trustStorePassword,
112112
sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".type", "jks"))
113-
.excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list")).build();
113+
.excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list"))
114+
.includeCiphers(sslConf.get("ssl.server.include.cipher.list"))
115+
.build();
114116

115117
return server;
116118
}

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSUtil.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1630,7 +1630,9 @@ public static HttpServer2.Builder loadSslConfToHttpServerBuilder(HttpServer2.Bui
16301630
getPassword(sslConf, DFS_SERVER_HTTPS_TRUSTSTORE_PASSWORD_KEY),
16311631
sslConf.get("ssl.server.truststore.type", "jks"))
16321632
.excludeCiphers(
1633-
sslConf.get("ssl.server.exclude.cipher.list"));
1633+
sslConf.get("ssl.server.exclude.cipher.list"))
1634+
.includeCiphers(
1635+
sslConf.get("ssl.server.include.cipher.list"));
16341636
}
16351637

16361638
/**

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/util/WebAppUtils.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,9 @@ public static HttpServer2.Builder loadSslConfiguration(
494494
getPassword(sslConf, WEB_APP_TRUSTSTORE_PASSWORD_KEY),
495495
sslConf.get("ssl.server.truststore.type", "jks"))
496496
.excludeCiphers(
497-
sslConf.get("ssl.server.exclude.cipher.list"));
497+
sslConf.get("ssl.server.exclude.cipher.list"))
498+
.includeCiphers(
499+
sslConf.get("ssl.server.include.cipher.list"));
498500
}
499501

500502
/**

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/webapp/util/TestWebServiceClient.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ void testCreateClient() throws Exception {
8585
sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".location"),
8686
sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".password"),
8787
sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".type", "jks"))
88-
.excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list")).build();
88+
.excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list"))
89+
.includeCiphers(sslConf.get("ssl.server.include.cipher.list"))
90+
.build();
8991
server.addServlet(SERVLET_NAME_ECHO, SERVLET_PATH_ECHO, EchoServlet.class);
9092
server.start();
9193

0 commit comments

Comments
 (0)