Skip to content

Commit cd7007a

Browse files
committed
CDPD-10718. HADOOP-16986. S3A to not need wildfly on the classpath. (apache#1948)
Contributed by Steve Loughran This is a successor to HADOOP-16346, which enabled the S3A connector to load the native openssl SSL libraries for better HTTPS performance. That patch required wildfly.jar to be on the classpath. This update: * Makes wildfly.jar optional except in the special case that "fs.s3a.ssl.channel.mode" is set to "openssl" * Retains the declaration of wildfly.jar as a compile-time dependency in the hadoop-aws POM. This means that unless explicitly excluded, applications importing that published maven artifact will, transitively, add the specified wildfly JAR into their classpath for compilation/testing/ distribution. This is done for packaging and to offer that optional speedup. It is not mandatory: applications importing the hadoop-aws POM can exclude it if they choose. Change-Id: I76e7c2baa209b4c826b96532243044de16470375
1 parent 0aefded commit cd7007a

File tree

5 files changed

+284
-94
lines changed

5 files changed

+284
-94
lines changed

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

Lines changed: 74 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.io.IOException;
2222
import java.net.InetAddress;
2323
import java.net.Socket;
24-
import java.net.SocketException;
2524
import java.security.KeyManagementException;
2625
import java.security.NoSuchAlgorithmException;
2726
import java.util.ArrayList;
@@ -31,11 +30,9 @@
3130
import javax.net.ssl.SSLSocket;
3231
import javax.net.ssl.SSLSocketFactory;
3332

33+
import com.google.common.annotations.VisibleForTesting;
3434
import org.slf4j.Logger;
3535
import org.slf4j.LoggerFactory;
36-
import org.wildfly.openssl.OpenSSLProvider;
37-
import org.wildfly.openssl.SSL;
38-
3936

4037
/**
4138
* A {@link SSLSocketFactory} that can delegate to various SSL implementations.
@@ -60,8 +57,8 @@
6057
* </p>
6158
*
6259
* In order to load OpenSSL, applications must ensure the wildfly-openssl
63-
* artifact is on the classpath. Currently, only ABFS and S3A provide
64-
* wildfly-openssl as a runtime dependency.
60+
* artifact is on the classpath. Currently, only ABFS declares
61+
* wildfly-openssl as an explicit dependency.
6562
*/
6663
public final class DelegatingSSLSocketFactory extends SSLSocketFactory {
6764

@@ -110,7 +107,16 @@ public static synchronized void initializeDefaultFactory(
110107
}
111108

112109
/**
113-
* Singletone instance of the SSLSocketFactory.
110+
* For testing only: reset the socket factory.
111+
*/
112+
@VisibleForTesting
113+
public static synchronized void resetDefaultFactory() {
114+
LOG.info("Resetting default SSL Socket Factory");
115+
instance = null;
116+
}
117+
118+
/**
119+
* Singleton instance of the SSLSocketFactory.
114120
*
115121
* SSLSocketFactory must be initialized with appropriate SSLChannelMode
116122
* using initializeDefaultFactory method.
@@ -126,9 +132,7 @@ private DelegatingSSLSocketFactory(SSLChannelMode preferredChannelMode)
126132
throws IOException {
127133
try {
128134
initializeSSLContext(preferredChannelMode);
129-
} catch (NoSuchAlgorithmException e) {
130-
throw new IOException(e);
131-
} catch (KeyManagementException e) {
135+
} catch (NoSuchAlgorithmException | KeyManagementException e) {
132136
throw new IOException(e);
133137
}
134138

@@ -146,42 +150,23 @@ private DelegatingSSLSocketFactory(SSLChannelMode preferredChannelMode)
146150
}
147151

148152
private void initializeSSLContext(SSLChannelMode preferredChannelMode)
149-
throws NoSuchAlgorithmException, KeyManagementException {
153+
throws NoSuchAlgorithmException, KeyManagementException, IOException {
154+
LOG.debug("Initializing SSL Context to channel mode {}",
155+
preferredChannelMode);
150156
switch (preferredChannelMode) {
151157
case Default:
152-
if (!openSSLProviderRegistered) {
153-
OpenSSLProvider.register();
154-
openSSLProviderRegistered = true;
155-
}
156158
try {
157-
java.util.logging.Logger logger = java.util.logging.Logger.getLogger(
158-
SSL.class.getName());
159-
logger.setLevel(Level.WARNING);
160-
ctx = SSLContext.getInstance("openssl.TLS");
161-
ctx.init(null, null, null);
162-
// Strong reference needs to be kept to logger until initialization of
163-
// SSLContext finished (see HADOOP-16174):
164-
logger.setLevel(Level.INFO);
159+
bindToOpenSSLProvider();
165160
channelMode = SSLChannelMode.OpenSSL;
166-
} catch (NoSuchAlgorithmException e) {
167-
LOG.debug("Failed to load OpenSSL. Falling back to the JSSE default.");
161+
} catch (LinkageError | NoSuchAlgorithmException | RuntimeException e) {
162+
LOG.debug("Failed to load OpenSSL. Falling back to the JSSE default.",
163+
e);
168164
ctx = SSLContext.getDefault();
169165
channelMode = SSLChannelMode.Default_JSSE;
170166
}
171167
break;
172168
case OpenSSL:
173-
if (!openSSLProviderRegistered) {
174-
OpenSSLProvider.register();
175-
openSSLProviderRegistered = true;
176-
}
177-
java.util.logging.Logger logger = java.util.logging.Logger.getLogger(
178-
SSL.class.getName());
179-
logger.setLevel(Level.WARNING);
180-
ctx = SSLContext.getInstance("openssl.TLS");
181-
ctx.init(null, null, null);
182-
// Strong reference needs to be kept to logger until initialization of
183-
// SSLContext finished (see HADOOP-16174):
184-
logger.setLevel(Level.INFO);
169+
bindToOpenSSLProvider();
185170
channelMode = SSLChannelMode.OpenSSL;
186171
break;
187172
case Default_JSSE:
@@ -193,11 +178,38 @@ private void initializeSSLContext(SSLChannelMode preferredChannelMode)
193178
channelMode = SSLChannelMode.Default_JSSE_with_GCM;
194179
break;
195180
default:
196-
throw new NoSuchAlgorithmException("Unknown channel mode: "
181+
throw new IOException("Unknown channel mode: "
197182
+ preferredChannelMode);
198183
}
199184
}
200185

186+
/**
187+
* Bind to the OpenSSL provider via wildfly.
188+
* This MUST be the only place where wildfly classes are referenced,
189+
* so ensuring that any linkage problems only surface here where they may
190+
* be caught by the initialization code.
191+
*/
192+
private void bindToOpenSSLProvider()
193+
throws NoSuchAlgorithmException, KeyManagementException {
194+
if (!openSSLProviderRegistered) {
195+
LOG.debug("Attempting to register OpenSSL provider");
196+
org.wildfly.openssl.OpenSSLProvider.register();
197+
openSSLProviderRegistered = true;
198+
}
199+
// Strong reference needs to be kept to logger until initialization of
200+
// SSLContext finished (see HADOOP-16174):
201+
java.util.logging.Logger logger = java.util.logging.Logger.getLogger(
202+
"org.wildfly.openssl.SSL");
203+
Level originalLevel = logger.getLevel();
204+
try {
205+
logger.setLevel(Level.WARNING);
206+
ctx = SSLContext.getInstance("openssl.TLS");
207+
ctx.init(null, null, null);
208+
} finally {
209+
logger.setLevel(originalLevel);
210+
}
211+
}
212+
201213
public String getProviderName() {
202214
return providerName;
203215
}
@@ -212,86 +224,80 @@ public String[] getSupportedCipherSuites() {
212224
return ciphers.clone();
213225
}
214226

227+
/**
228+
* Get the channel mode of this instance.
229+
* @return a channel mode.
230+
*/
231+
public SSLChannelMode getChannelMode() {
232+
return channelMode;
233+
}
234+
215235
public Socket createSocket() throws IOException {
216236
SSLSocketFactory factory = ctx.getSocketFactory();
217-
SSLSocket ss = (SSLSocket) factory.createSocket();
218-
configureSocket(ss);
219-
return ss;
237+
return configureSocket(factory.createSocket());
220238
}
221239

222240
@Override
223241
public Socket createSocket(Socket s, String host, int port,
224242
boolean autoClose) throws IOException {
225243
SSLSocketFactory factory = ctx.getSocketFactory();
226-
SSLSocket ss = (SSLSocket) factory.createSocket(s, host, port, autoClose);
227244

228-
configureSocket(ss);
229-
return ss;
245+
return configureSocket(
246+
factory.createSocket(s, host, port, autoClose));
230247
}
231248

232249
@Override
233250
public Socket createSocket(InetAddress address, int port,
234251
InetAddress localAddress, int localPort)
235252
throws IOException {
236253
SSLSocketFactory factory = ctx.getSocketFactory();
237-
SSLSocket ss = (SSLSocket) factory
238-
.createSocket(address, port, localAddress, localPort);
239-
240-
configureSocket(ss);
241-
return ss;
254+
return configureSocket(factory
255+
.createSocket(address, port, localAddress, localPort));
242256
}
243257

244258
@Override
245259
public Socket createSocket(String host, int port, InetAddress localHost,
246260
int localPort) throws IOException {
247261
SSLSocketFactory factory = ctx.getSocketFactory();
248-
SSLSocket ss = (SSLSocket) factory
249-
.createSocket(host, port, localHost, localPort);
250262

251-
configureSocket(ss);
252-
253-
return ss;
263+
return configureSocket(factory
264+
.createSocket(host, port, localHost, localPort));
254265
}
255266

256267
@Override
257268
public Socket createSocket(InetAddress host, int port) throws IOException {
258269
SSLSocketFactory factory = ctx.getSocketFactory();
259-
SSLSocket ss = (SSLSocket) factory.createSocket(host, port);
260-
261-
configureSocket(ss);
262270

263-
return ss;
271+
return configureSocket(factory.createSocket(host, port));
264272
}
265273

266274
@Override
267275
public Socket createSocket(String host, int port) throws IOException {
268276
SSLSocketFactory factory = ctx.getSocketFactory();
269-
SSLSocket ss = (SSLSocket) factory.createSocket(host, port);
270-
271-
configureSocket(ss);
272277

273-
return ss;
278+
return configureSocket(factory.createSocket(host, port));
274279
}
275280

276-
private void configureSocket(SSLSocket ss) throws SocketException {
277-
ss.setEnabledCipherSuites(ciphers);
281+
private Socket configureSocket(Socket socket) {
282+
((SSLSocket) socket).setEnabledCipherSuites(ciphers);
283+
return socket;
278284
}
279285

280286
private String[] alterCipherList(String[] defaultCiphers) {
281287

282-
ArrayList<String> preferredSuits = new ArrayList<>();
288+
ArrayList<String> preferredSuites = new ArrayList<>();
283289

284290
// Remove GCM mode based ciphers from the supported list.
285291
for (int i = 0; i < defaultCiphers.length; i++) {
286292
if (defaultCiphers[i].contains("_GCM_")) {
287293
LOG.debug("Removed Cipher - {} from list of enabled SSLSocket ciphers",
288294
defaultCiphers[i]);
289295
} else {
290-
preferredSuits.add(defaultCiphers[i]);
296+
preferredSuites.add(defaultCiphers[i]);
291297
}
292298
}
293299

294-
ciphers = preferredSuits.toArray(new String[0]);
300+
ciphers = preferredSuites.toArray(new String[0]);
295301
return ciphers;
296302
}
297-
}
303+
}

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/impl/NetworkBinding.java

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,26 +61,28 @@ public class NetworkBinding {
6161
* @param awsConf the {@link ClientConfiguration} to set the
6262
* SSLConnectionSocketFactory for.
6363
* @throws IOException if there is an error while initializing the
64-
* {@link SSLSocketFactory}.
64+
* {@link SSLSocketFactory} other than classloader problems.
6565
*/
6666
public static void bindSSLChannelMode(Configuration conf,
6767
ClientConfiguration awsConf) throws IOException {
68-
try {
69-
// Validate that SSL_CHANNEL_MODE is set to a valid value.
70-
String channelModeString = conf.get(
71-
SSL_CHANNEL_MODE, DEFAULT_SSL_CHANNEL_MODE.name());
72-
DelegatingSSLSocketFactory.SSLChannelMode channelMode = null;
73-
for (DelegatingSSLSocketFactory.SSLChannelMode mode :
74-
DelegatingSSLSocketFactory.SSLChannelMode.values()) {
75-
if (mode.name().equalsIgnoreCase(channelModeString)) {
76-
channelMode = mode;
77-
}
78-
}
79-
if (channelMode == null) {
80-
throw new IllegalArgumentException(channelModeString +
81-
" is not a valid value for " + SSL_CHANNEL_MODE);
68+
69+
// Validate that SSL_CHANNEL_MODE is set to a valid value.
70+
String channelModeString = conf.getTrimmed(
71+
SSL_CHANNEL_MODE, DEFAULT_SSL_CHANNEL_MODE.name());
72+
DelegatingSSLSocketFactory.SSLChannelMode channelMode = null;
73+
for (DelegatingSSLSocketFactory.SSLChannelMode mode :
74+
DelegatingSSLSocketFactory.SSLChannelMode.values()) {
75+
if (mode.name().equalsIgnoreCase(channelModeString)) {
76+
channelMode = mode;
8277
}
78+
}
79+
if (channelMode == null) {
80+
throw new IllegalArgumentException(channelModeString +
81+
" is not a valid value for " + SSL_CHANNEL_MODE);
82+
}
8383

84+
DelegatingSSLSocketFactory.initializeDefaultFactory(channelMode);
85+
try {
8486
// Look for AWS_SOCKET_FACTORY_CLASSNAME on the classpath and instantiate
8587
// an instance using the DelegatingSSLSocketFactory as the
8688
// SSLSocketFactory.
@@ -89,7 +91,6 @@ public static void bindSSLChannelMode(Configuration conf,
8991
Constructor<?> factoryConstructor =
9092
sslConnectionSocketFactory.getDeclaredConstructor(
9193
SSLSocketFactory.class, HostnameVerifier.class);
92-
DelegatingSSLSocketFactory.initializeDefaultFactory(channelMode);
9394
awsConf.getApacheHttpClientConfig().setSslSocketFactory(
9495
(com.amazonaws.thirdparty.apache.http.conn.ssl.
9596
SSLConnectionSocketFactory) factoryConstructor
@@ -98,7 +99,7 @@ public static void bindSSLChannelMode(Configuration conf,
9899
(HostnameVerifier) null));
99100
} catch (ClassNotFoundException | NoSuchMethodException |
100101
IllegalAccessException | InstantiationException |
101-
InvocationTargetException e) {
102+
InvocationTargetException | LinkageError e) {
102103
LOG.debug("Unable to create class {}, value of {} will be ignored",
103104
AWS_SOCKET_FACTORY_CLASSNAME, SSL_CHANNEL_MODE, e);
104105
}

hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/performance.md

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ in Java 9, so if `default_jsse` is specified and applications run on Java
539539
includes GCM in the list of cipher suites on Java 8, so it is equivalent to
540540
running with the vanilla JSSE.
541541

542-
### OpenSSL Acceleration
542+
### <a name="openssl"></a> OpenSSL Acceleration
543543

544544
**Experimental Feature**
545545

@@ -552,8 +552,8 @@ significant performance benefit over the JSSE.
552552
S3A uses the
553553
[WildFly OpenSSL](https://github.com/wildfly-security/wildfly-openssl) library
554554
to bind OpenSSL to the Java JSSE APIs. This library allows S3A to
555-
transparently read data using OpenSSL. The wildfly-openssl library is a
556-
runtime dependency of S3A and contains native libraries for binding the Java
555+
transparently read data using OpenSSL. The `wildfly-openssl` library is an
556+
optional runtime dependency of S3A and contains native libraries for binding the Java
557557
JSSE to OpenSSL.
558558

559559
WildFly OpenSSL must load OpenSSL itself. This can be done using the system
@@ -596,19 +596,37 @@ exception and S3A initialization will fail.
596596

597597
Supported values for `fs.s3a.ssl.channel.mode`:
598598

599-
| fs.s3a.ssl.channel.mode Value | Description |
599+
| `fs.s3a.ssl.channel.mode` Value | Description |
600600
|-------------------------------|-------------|
601-
| default_jsse | Uses Java JSSE without GCM on Java 8 |
602-
| default_jsse_with_gcm | Uses Java JSSE |
603-
| default | Uses OpenSSL, falls back to default_jsse if OpenSSL cannot be loaded |
604-
| openssl | Uses OpenSSL, fails if OpenSSL cannot be loaded |
601+
| `default_jsse` | Uses Java JSSE without GCM on Java 8 |
602+
| `default_jsse_with_gcm` | Uses Java JSSE |
603+
| `default` | Uses OpenSSL, falls back to `default_jsse` if OpenSSL cannot be loaded |
604+
| `openssl` | Uses OpenSSL, fails if OpenSSL cannot be loaded |
605605

606606
The naming convention is setup in order to preserve backwards compatibility
607-
with HADOOP-15669.
607+
with the ABFS support of [HADOOP-15669](https://issues.apache.org/jira/browse/HADOOP-15669).
608608

609609
Other options may be added to `fs.s3a.ssl.channel.mode` in the future as
610610
further SSL optimizations are made.
611611

612+
### WildFly classpath requirements
613+
614+
For OpenSSL acceleration to work, a compatible version of the
615+
wildfly JAR must be on the classpath. This is not explicitly declared
616+
in the dependencies of the published `hadoop-aws` module, as it is
617+
optional.
618+
619+
If the wildfly JAR is not found, the network acceleration will fall back
620+
to the JVM, always.
621+
622+
Note: there have been compatibility problems with wildfly JARs and openSSL
623+
releases in the past: version 1.0.4.Final is not compatible with openssl 1.1.1.
624+
An extra complication was older versions of the `azure-data-lake-store-sdk`
625+
JAR used in `hadoop-azure-datalake` contained an unshaded copy of the 1.0.4.Final
626+
classes, causing binding problems even when a later version was explicitly
627+
being placed on the classpath.
628+
629+
612630
## Tuning FileSystem Initialization.
613631

614632
When an S3A Filesystem instance is created and initialized, the client

0 commit comments

Comments
 (0)