com.sap.hana.datalake.files.HdlfsConnectionConfigurator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sap-hdlfs Show documentation
Show all versions of sap-hdlfs Show documentation
An implementation of org.apache.hadoop.fs.FileSystem targeting SAP HANA Data Lake Files.
// © 2021-2022 SAP SE or an SAP affiliate company. All rights reserved.
package com.sap.hana.datalake.files;
import com.sap.hana.datalake.files.utils.PemSslUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.authentication.client.ConnectionConfigurator;
import org.apache.hadoop.security.ssl.FileBasedKeyStoresFactory;
import org.apache.hadoop.security.ssl.KeyStoresFactory;
import org.apache.hadoop.security.ssl.SSLFactory;
import org.apache.hadoop.security.ssl.SSLHostnameVerifier;
import org.apache.hadoop.util.ReflectionUtils;
import org.apache.hadoop.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.security.GeneralSecurityException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class HdlfsConnectionConfigurator implements ConnectionConfigurator {
private static final Logger LOG = LoggerFactory.getLogger(HdlfsConnectionConfigurator.class);
private final ConnectionConfigurator sslConfigurator;
private final Configuration conf;
private int readTimeout = HdlfsConstants.FS_HDLFS_READ_TIMEOUT_DEFAULT;
private int connectTimeout = HdlfsConstants.FS_HDLFS_CONNECTION_TIMEOUT_DEFAULT;
private SSLSocketFactory socketFactory;
private HostnameVerifier hostnameVerifier;
private String fileContainer;
private boolean impersonationEnabled;
private String impersonationRoles;
private String impersonationUserEncoded;
private String connectionId;
private boolean isSSLEnabled;
private String[] enabledProtocols = null;
private Map additionalHeaders;
public HdlfsConnectionConfigurator(final Configuration conf, final ConnectionConfigurator sslConfigurator) {
this.conf = conf;
this.sslConfigurator = sslConfigurator;
this.additionalHeaders = new HashMap<>();
}
public HdlfsConnectionConfigurator(final Configuration conf, final Integer connectTimeout, final Integer readTimeout) throws IOException {
this(conf, null);
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
try {
this.parseConfig();
this.enabledProtocols = this.conf.getStrings(SSLFactory.SSL_ENABLED_PROTOCOLS_KEY, SSLFactory.SSL_ENABLED_PROTOCOLS_DEFAULT);
this.hostnameVerifier = SSLFactory.getHostnameVerifier(StringUtils.toUpperCase(
this.conf.get(SSLFactory.SSL_HOSTNAME_VERIFIER_KEY, SSLHostnameVerifier.DEFAULT.toString()).trim()));
// if both PEM file paths are configured we use them instead
this.socketFactory = this.isPemConfigured() ? this.getSocketFactoryForPem() : this.getSocketFactoryForKeyStores();
} catch (final Exception ex) {
throw new IOException(ex);
}
}
public String getFileContainer() {
return this.fileContainer;
}
public boolean isImpersonationEnabled() {
return this.impersonationEnabled;
}
public String getImpersonationUserEncoded() {
return this.impersonationUserEncoded;
}
public String getConnectionId() {
return this.connectionId;
}
public boolean isSSLEnabled() {
return this.isSSLEnabled;
}
public String getImpersonationRoles() {
return this.impersonationRoles;
}
public Map getAdditionalHeaders() {
return this.additionalHeaders;
}
/**
* parse the file system config to check for specified properties.
*/
private void parseConfig() {
// parse the filecontainer if present along with impersonation flag
this.fileContainer = this.conf.get(HdlfsConstants.FS_HDLFS_FILECONTAINER_KEY);
this.impersonationEnabled = this.conf.getBoolean(HdlfsConstants.FS_HDLFS_IMPERSONATION_ENABLED_KEY, false);
this.additionalHeaders = this.conf.getPropsWithPrefix(HdlfsConstants.FS_HDLFS_HTTP_CLIENT_ADDITIONAL_HEADERS_KEY_PREFIX);
if (this.impersonationEnabled) {
final String impersonationUser = this.conf.get(HdlfsConstants.FS_HDLFS_IMPERSONATION_USER_KEY, "");
if (!impersonationUser.isEmpty()) {
this.impersonationUserEncoded = Base64.getEncoder().encodeToString(impersonationUser.getBytes(HdlfsConstants.DEFAULT_CHARSET));
this.impersonationRoles = this.conf.get(HdlfsConstants.FS_HDLFS_IMPERSONATION_USER_ROLES);
}
}
// parse the connection id property is present
this.connectionId = this.conf.get(HdlfsConstants.FS_HDLFS_CONNECTION_ID);
this.isSSLEnabled = this.conf.getBoolean(HdlfsConstants.FS_HDLFS_SSL_ENABLED_KEY, true);
}
@Override
public HttpURLConnection configure(final HttpURLConnection conn) {
this.configureSSLConn(conn);
if (this.getFileContainer() != null && !this.getFileContainer().isEmpty()) {
LOG.debug("File container specified, setting header [{}]", HdlfsConstants.FS_HDLFS_CONTAINER_HEADER);
conn.setRequestProperty(HdlfsConstants.FS_HDLFS_CONTAINER_HEADER, this.getFileContainer());
}
for (final Map.Entry header : this.getAdditionalHeaders().entrySet()) {
conn.setRequestProperty(header.getKey(), header.getValue());
}
final boolean impersonationEnabled = this.isImpersonationEnabled();
final String encodedUser = this.getImpersonationUserEncoded();
final String roles = this.getImpersonationRoles();
if (impersonationEnabled && encodedUser != null && !encodedUser.isEmpty()) {
LOG.debug("Impersonation enabled, setting headers [{}] , [{}], and [{}]",
HdlfsConstants.TRUSTED_USER_HEADER, HdlfsConstants.TRUSTED_USER_ROLES_HEADER, HdlfsConstants.TRUSTED_USER_ENCODING_HEADER);
conn.setRequestProperty(HdlfsConstants.TRUSTED_USER_HEADER, encodedUser);
conn.setRequestProperty(HdlfsConstants.TRUSTED_USER_ENCODING_HEADER, HdlfsConstants.TRUSTED_USER_ENCODING);
if (roles != null && !roles.isEmpty()) {
conn.setRequestProperty(HdlfsConstants.TRUSTED_USER_ROLES_HEADER, roles);
}
}
if (this.getConnectionId() != null && !this.getConnectionId().isEmpty()) {
LOG.debug("Connection-Id specified, setting header [{}]", HdlfsConstants.FS_HDLFS_CONNECTION_ID_HEADER);
conn.setRequestProperty(HdlfsConstants.FS_HDLFS_CONNECTION_ID_HEADER, this.getConnectionId());
}
return conn;
}
protected void configureSSLConn(final HttpURLConnection conn) {
if (conn instanceof HttpsURLConnection) {
final HttpsURLConnection secureConn = (HttpsURLConnection) conn;
secureConn.setSSLSocketFactory(this.socketFactory);
secureConn.setHostnameVerifier(this.hostnameVerifier);
}
conn.setConnectTimeout(this.connectTimeout);
conn.setReadTimeout(this.readTimeout);
}
protected KeyStoresFactory getKeyStoresFactory(final Configuration sslConf) {
final Class klass = this.conf.getClass(HdlfsConstants.FS_HDLFS_KEYSTORES_FACTORY_CLASS_KEY, FileBasedKeyStoresFactory.class, KeyStoresFactory.class);
return ReflectionUtils.newInstance(klass, sslConf);
}
protected SSLContext createSSLContext(final KeyManager[] keyManagers, final TrustManager[] trustManagers) throws GeneralSecurityException {
final SSLContext context = SSLContext.getInstance("TLS");
context.init(keyManagers, trustManagers, null);
context.getDefaultSSLParameters().setProtocols(this.enabledProtocols);
return context;
}
private SSLSocketFactory getSocketFactoryForPem() throws GeneralSecurityException, IOException {
final KeyManager[] keyManagers = PemSslUtils.createKeyManagers(this.conf.get(HdlfsConstants.FS_HDLFS_SSL_CERTFILE_KEY), this.conf.get(HdlfsConstants.FS_HDLFS_SSL_KEYFILE_KEY));
final TrustManager[] trustManagers = PemSslUtils.createTrustManagers(this.conf.get(HdlfsConstants.FS_HDLFS_SSL_CAPATH_KEY));
final SSLContext context = this.createSSLContext(keyManagers, trustManagers);
return context.getSocketFactory();
}
private SSLSocketFactory getSocketFactoryForKeyStores() throws GeneralSecurityException, IOException {
final Configuration sslConf = this.readSSLConfiguration();
final KeyStoresFactory keystoresFactory = this.getKeyStoresFactory(sslConf);
keystoresFactory.init(SSLFactory.Mode.CLIENT);
final SSLContext context = this.createSSLContext(keystoresFactory.getKeyManagers(), keystoresFactory.getTrustManagers());
return context.getSocketFactory();
}
protected Configuration readSSLConfiguration() throws IOException {
final Configuration sslConf = new Configuration(this.conf);
final String keyStoreFactoryClassConfig = this.conf.get(HdlfsConstants.FS_HDLFS_KEYSTORES_FACTORY_CLASS_KEY);
final boolean isCustomKeystoresFactory = keyStoreFactoryClassConfig != null && !keyStoreFactoryClassConfig.isEmpty();
// we do not require to parse keystore/truststore config in case 1) a custom keystores factory is used OR
// 2) ssl is disabled
if (isCustomKeystoresFactory || !this.isSSLEnabled()) {
return sslConf;
}
sslConf.setBoolean(SSLFactory.SSL_REQUIRE_CLIENT_CERT_KEY, true);
// Default file based, so we need to load our configurations
final String keystoreLocation = this.conf.get(HdlfsConstants.FS_HDLFS_SSL_KEYSTORE_LOCATION_KEY);
final String keystorePassword = this.conf.get(HdlfsConstants.FS_HDLFS_SSL_KEYSTORE_PASSWORD_KEY, HdlfsConstants.FS_HDLFS_SSL_KEYSTORE_PASSWORD_DEFAULT);
final String keystoreKeyPassword = this.conf.get(HdlfsConstants.FS_HDLFS_SSL_KEYSTORE_KEYPASSWORD_KEY, keystorePassword);
final String keystoreType = this.conf.get(HdlfsConstants.FS_HDLFS_SSL_KEYSTORE_TYPE_KEY, HdlfsConstants.FS_HDLFS_SSL_KEYSTORE_TYPE_DEFAULT);
final String truststoreLocation = this.conf.get(HdlfsConstants.FS_HDLFS_SSL_TRUSTSTORE_LOCATION_KEY, HdlfsConstants.FS_HDLFS_SSL_TRUSTSTORE_LOCATION_DEFAULT);
final String truststorePassword = this.conf.get(HdlfsConstants.FS_HDLFS_SSL_TRUSTSTORE_PASSWORD_KEY, HdlfsConstants.FS_HDLFS_SSL_TRUSTSTORE_PASSWORD_DEFAULT);
final String truststoreType = this.conf.get(HdlfsConstants.FS_HDLFS_SSL_TRUSTSTORE_TYPE_KEY, HdlfsConstants.FS_HDLFS_SSL_TRUSTSTORE_TYPE_DEFAULT);
if (keystoreLocation == null || keystoreLocation.isEmpty()) {
throw new IOException("Keystore location was not provided.");
}
sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
FileBasedKeyStoresFactory.SSL_KEYSTORE_LOCATION_TPL_KEY), keystoreLocation);
sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
FileBasedKeyStoresFactory.SSL_KEYSTORE_PASSWORD_TPL_KEY), keystorePassword);
sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
FileBasedKeyStoresFactory.SSL_KEYSTORE_KEYPASSWORD_TPL_KEY), keystoreKeyPassword);
sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
FileBasedKeyStoresFactory.SSL_KEYSTORE_TYPE_TPL_KEY), keystoreType);
sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
FileBasedKeyStoresFactory.SSL_TRUSTSTORE_LOCATION_TPL_KEY), truststoreLocation);
sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
FileBasedKeyStoresFactory.SSL_TRUSTSTORE_PASSWORD_TPL_KEY), truststorePassword);
sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
FileBasedKeyStoresFactory.SSL_TRUSTSTORE_TYPE_TPL_KEY), truststoreType);
return sslConf;
}
private boolean isPemConfigured() {
final String certFile = this.conf.get(HdlfsConstants.FS_HDLFS_SSL_CERTFILE_KEY, "");
final String keyFile = this.conf.get(HdlfsConstants.FS_HDLFS_SSL_KEYFILE_KEY, "");
return !certFile.isEmpty() && !keyFile.isEmpty();
}
}
// © 2021-2022 SAP SE or an SAP affiliate company. All rights reserved.