HADOOP-12862. LDAP Group Mapping over SSL can not specify trust store. Contributed by Wei-Chiu Chuang and Konstantin Shvachko.

This commit is contained in:
Konstantin V Shvachko 2018-03-29 17:13:18 -07:00
parent 2c6cfad5a3
commit 2216bde322
4 changed files with 105 additions and 11 deletions

View File

@ -2335,7 +2335,7 @@ public class Configuration implements Iterable<Map.Entry<String,String>>,
* @return password or null if not found
* @throws IOException
*/
protected char[] getPasswordFromCredentialProviders(String name)
public char[] getPasswordFromCredentialProviders(String name)
throws IOException {
char[] pass = null;
try {

View File

@ -109,6 +109,27 @@ public class LdapGroupsMapping
public static final String LDAP_KEYSTORE_PASSWORD_FILE_KEY = LDAP_KEYSTORE_PASSWORD_KEY + ".file";
public static final String LDAP_KEYSTORE_PASSWORD_FILE_DEFAULT = "";
/**
* File path to the location of the SSL truststore to use
*/
public static final String LDAP_TRUSTSTORE_KEY = LDAP_CONFIG_PREFIX +
".ssl.truststore";
/**
* The key of the credential entry containing the password for
* the LDAP SSL truststore
*/
public static final String LDAP_TRUSTSTORE_PASSWORD_KEY =
LDAP_CONFIG_PREFIX +".ssl.truststore.password";
/**
* The path to a file containing the password for
* the LDAP SSL truststore
*/
public static final String LDAP_TRUSTSTORE_PASSWORD_FILE_KEY =
LDAP_TRUSTSTORE_PASSWORD_KEY + ".file";
/*
* User to bind to the LDAP server with
*/
@ -226,6 +247,8 @@ public class LdapGroupsMapping
private boolean useSsl;
private String keystore;
private String keystorePass;
private String truststore;
private String truststorePass;
private String bindUser;
private String bindPassword;
private String userbaseDN;
@ -526,8 +549,19 @@ public class LdapGroupsMapping
// Set up SSL security, if necessary
if (useSsl) {
env.put(Context.SECURITY_PROTOCOL, "ssl");
System.setProperty("javax.net.ssl.keyStore", keystore);
System.setProperty("javax.net.ssl.keyStorePassword", keystorePass);
if (!keystore.isEmpty()) {
System.setProperty("javax.net.ssl.keyStore", keystore);
}
if (!keystorePass.isEmpty()) {
System.setProperty("javax.net.ssl.keyStorePassword", keystorePass);
}
if (!truststore.isEmpty()) {
System.setProperty("javax.net.ssl.trustStore", truststore);
}
if (!truststorePass.isEmpty()) {
System.setProperty("javax.net.ssl.trustStorePassword",
truststorePass);
}
}
env.put(Context.SECURITY_PRINCIPAL, bindUser);
@ -572,15 +606,10 @@ public class LdapGroupsMapping
if (ldapUrl == null || ldapUrl.isEmpty()) {
throw new RuntimeException("LDAP URL is not configured");
}
useSsl = conf.getBoolean(LDAP_USE_SSL_KEY, LDAP_USE_SSL_DEFAULT);
keystore = conf.get(LDAP_KEYSTORE_KEY, LDAP_KEYSTORE_DEFAULT);
keystorePass = getPassword(conf, LDAP_KEYSTORE_PASSWORD_KEY,
LDAP_KEYSTORE_PASSWORD_DEFAULT);
if (keystorePass.isEmpty()) {
keystorePass = extractPassword(conf.get(LDAP_KEYSTORE_PASSWORD_FILE_KEY,
LDAP_KEYSTORE_PASSWORD_FILE_DEFAULT));
if (useSsl) {
loadSslConf(conf);
}
bindUser = conf.get(BIND_USER_KEY, BIND_USER_DEFAULT);
@ -643,6 +672,47 @@ public class LdapGroupsMapping
this.conf = conf;
}
private void loadSslConf(Configuration sslConf) {
keystore = sslConf.get(LDAP_KEYSTORE_KEY, LDAP_KEYSTORE_DEFAULT);
keystorePass = getPassword(sslConf, LDAP_KEYSTORE_PASSWORD_KEY,
LDAP_KEYSTORE_PASSWORD_DEFAULT);
if (keystorePass.isEmpty()) {
keystorePass = extractPassword(sslConf.get(
LDAP_KEYSTORE_PASSWORD_FILE_KEY,
LDAP_KEYSTORE_PASSWORD_FILE_DEFAULT));
}
truststore = sslConf.get(LDAP_TRUSTSTORE_KEY, "");
truststorePass = getPasswordFromCredentialProviders(
sslConf, LDAP_TRUSTSTORE_PASSWORD_KEY, "");
if (truststorePass.isEmpty()) {
truststorePass = extractPassword(
sslConf.get(LDAP_TRUSTSTORE_PASSWORD_FILE_KEY, ""));
}
}
String getPasswordFromCredentialProviders(
Configuration conf, String alias, String defaultPass) {
String password = defaultPass;
try {
char[] passchars = conf.getPasswordFromCredentialProviders(alias);
if (passchars != null) {
password = new String(passchars);
}
} catch (IOException ioe) {
LOG.warn("Exception while trying to get password for alias {}: {}",
alias, ioe);
}
return password;
}
/**
* Passwords should not be stored in configuration. Use
* {@link #getPasswordFromCredentialProviders(
* Configuration, String, String)}
* to avoid reading passwords from a configuration file.
*/
@Deprecated
String getPassword(Configuration conf, String alias, String defaultPass) {
String password = defaultPass;
try {

View File

@ -316,6 +316,27 @@
</description>
</property>
<property>
<name>hadoop.security.group.mapping.ldap.ssl.truststore</name>
<value></value>
<description>
File path to the SSL truststore that contains the root certificate used to
sign the LDAP server's certificate. Specify this if the LDAP server's
certificate is not signed by a well known certificate authority.
</description>
</property>
<property>
<name>hadoop.security.group.mapping.ldap.ssl.truststore.password.file</name>
<value></value>
<description>
The path to a file containing the password of the LDAP SSL truststore.
IMPORTANT: This file should be readable only by the Unix user running
the daemons.
</description>
</property>
<property>
<name>hadoop.security.group.mapping.ldap.bind.user</name>
<value></value>

View File

@ -112,6 +112,9 @@ For some LDAP servers, such as Active Directory, the user object returned in the
Therefore, it is possible to infer the user's groups from the first query without sending the second one, and it may reduce group name resolution latency incurred by the second query. If it fails to get group names, it will fall back to the typical two-query scenario and send the second query to get group names.
To enable this feature, set `hadoop.security.group.mapping.ldap.search.attr.memberof` to `memberOf`, and Hadoop will resolve group names using this attribute in the user object.
If the LDAP server's certificate is not signed by a well known certificate authority, specify the path to the truststore in `hadoop.security.group.mapping.ldap.ssl.truststore`.
Similar to keystore, specify the truststore password file in `hadoop.security.group.mapping.ldap.ssl.truststore.password.file`.
Composite Groups Mapping
--------
`CompositeGroupsMapping` works by enumerating a list of service providers in `hadoop.security.group.mapping.providers`.