blob: ca1b09e9bdc93baa9ff12e0a390e7f3203df74f0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.fs.s3a.impl;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import com.amazonaws.ClientConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.security.ssl.DelegatingSSLSocketFactory;
import static org.apache.hadoop.fs.s3a.Constants.DEFAULT_ENDPOINT;
import static org.apache.hadoop.fs.s3a.Constants.DEFAULT_SSL_CHANNEL_MODE;
import static org.apache.hadoop.fs.s3a.Constants.ENDPOINT;
import static org.apache.hadoop.fs.s3a.Constants.SSL_CHANNEL_MODE;
/**
* Configures network settings when communicating with AWS services.
*/
public class NetworkBinding {
private static final Logger LOG =
LoggerFactory.getLogger(NetworkBinding.class);
private static final String AWS_SOCKET_FACTORY_CLASSNAME = "com.amazonaws" +
".thirdparty.apache.http.conn.ssl.SSLConnectionSocketFactory";
/**
* Configures the {@link com.amazonaws.thirdparty.apache.http.conn.ssl
* .SSLConnectionSocketFactory} used by the AWS SDK. A custom
* SSLConnectionSocketFactory can be set using the method
* {@link com.amazonaws.ApacheHttpClientConfig#setSslSocketFactory(
* com.amazonaws.thirdparty.apache.http.conn.socket.ConnectionSocketFactory)}.
* If {@link com.amazonaws.thirdparty.apache.http.conn.ssl
* .SSLConnectionSocketFactory} cannot be found on the classpath, the value
* of {@link org.apache.hadoop.fs.s3a.Constants#SSL_CHANNEL_MODE} is ignored.
*
* @param conf the {@link Configuration} used to get the client specified
* value of {@link org.apache.hadoop.fs.s3a.Constants
* #SSL_CHANNEL_MODE}
* @param awsConf the {@link ClientConfiguration} to set the
* SSLConnectionSocketFactory for.
* @throws IOException if there is an error while initializing the
* {@link SSLSocketFactory} other than classloader problems.
*/
public static void bindSSLChannelMode(Configuration conf,
ClientConfiguration awsConf) throws IOException {
// Validate that SSL_CHANNEL_MODE is set to a valid value.
String channelModeString = conf.getTrimmed(
SSL_CHANNEL_MODE, DEFAULT_SSL_CHANNEL_MODE.name());
DelegatingSSLSocketFactory.SSLChannelMode channelMode = null;
for (DelegatingSSLSocketFactory.SSLChannelMode mode :
DelegatingSSLSocketFactory.SSLChannelMode.values()) {
if (mode.name().equalsIgnoreCase(channelModeString)) {
channelMode = mode;
}
}
if (channelMode == null) {
throw new IllegalArgumentException(channelModeString +
" is not a valid value for " + SSL_CHANNEL_MODE);
}
DelegatingSSLSocketFactory.initializeDefaultFactory(channelMode);
try {
// Look for AWS_SOCKET_FACTORY_CLASSNAME on the classpath and instantiate
// an instance using the DelegatingSSLSocketFactory as the
// SSLSocketFactory.
Class<?> sslConnectionSocketFactory = Class.forName(
AWS_SOCKET_FACTORY_CLASSNAME);
Constructor<?> factoryConstructor =
sslConnectionSocketFactory.getDeclaredConstructor(
SSLSocketFactory.class, HostnameVerifier.class);
awsConf.getApacheHttpClientConfig().setSslSocketFactory(
(com.amazonaws.thirdparty.apache.http.conn.ssl.
SSLConnectionSocketFactory) factoryConstructor
.newInstance(DelegatingSSLSocketFactory
.getDefaultFactory(),
(HostnameVerifier) null));
} catch (ClassNotFoundException | NoSuchMethodException |
IllegalAccessException | InstantiationException |
InvocationTargetException | LinkageError e) {
LOG.debug("Unable to create class {}, value of {} will be ignored",
AWS_SOCKET_FACTORY_CLASSNAME, SSL_CHANNEL_MODE, e);
}
}
/**
* Given an S3 bucket region as returned by a bucket location query,
* fix it into a form which can be used by other AWS commands.
* <p>
* <a href="https://forums.aws.amazon.com/thread.jspa?messageID=796829">
* https://forums.aws.amazon.com/thread.jspa?messageID=796829</a>
* </p>
* See also {@code com.amazonaws.services.s3.model.Region.fromValue()}
* for its conversion logic.
* @param region region from S3 call.
* @return the region to use in DDB etc.
*/
public static String fixBucketRegion(final String region) {
return region == null || region.equals("US")
? "us-east-1"
: region;
}
/**
* Log the dns address associated with s3 endpoint. If endpoint is
* not set in the configuration, the {@code Constants#DEFAULT_ENDPOINT}
* will be used.
* @param conf input configuration.
*/
public static void logDnsLookup(Configuration conf) {
String endPoint = conf.getTrimmed(ENDPOINT, DEFAULT_ENDPOINT);
String hostName = endPoint;
if (!endPoint.isEmpty() && LOG.isDebugEnabled()) {
// Updating the hostname if there is a scheme present.
if (endPoint.contains("://")) {
try {
URI uri = new URI(endPoint);
hostName = uri.getHost();
} catch (URISyntaxException e) {
LOG.debug("Got URISyntaxException, ignoring");
}
}
LOG.debug("Bucket endpoint : {}, Hostname : {}, DNSAddress : {}",
endPoint,
hostName,
NetUtils.normalizeHostName(hostName));
}
}
}