HADOOP-12846. Credential Provider Recursive Dependencies. Contributed by Larry McCay.
This commit is contained in:
parent
f9692770a5
commit
7634d404b7
|
@ -1761,6 +1761,9 @@ Release 2.8.0 - UNRELEASED
|
||||||
HADOOP-12849. TestSymlinkLocalFSFileSystem fails intermittently.
|
HADOOP-12849. TestSymlinkLocalFSFileSystem fails intermittently.
|
||||||
(Mingliang Liu via cnauroth)
|
(Mingliang Liu via cnauroth)
|
||||||
|
|
||||||
|
HADOOP-12846. Credential Provider Recursive Dependencies.
|
||||||
|
(Larry McCay via cnauroth)
|
||||||
|
|
||||||
Release 2.7.3 - UNRELEASED
|
Release 2.7.3 - UNRELEASED
|
||||||
|
|
||||||
INCOMPATIBLE CHANGES
|
INCOMPATIBLE CHANGES
|
||||||
|
|
|
@ -18,14 +18,34 @@
|
||||||
|
|
||||||
package org.apache.hadoop.security;
|
package org.apache.hadoop.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
|
import org.apache.hadoop.security.alias.CredentialProviderFactory;
|
||||||
import org.apache.hadoop.security.alias.JavaKeyStoreProvider;
|
import org.apache.hadoop.security.alias.JavaKeyStoreProvider;
|
||||||
import org.apache.hadoop.security.alias.LocalJavaKeyStoreProvider;
|
import org.apache.hadoop.security.alias.LocalJavaKeyStoreProvider;
|
||||||
|
|
||||||
public class ProviderUtils {
|
/**
|
||||||
|
* Utility methods for both key and credential provider APIs.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class ProviderUtils {
|
||||||
|
private static final Log LOG = LogFactory.getLog(ProviderUtils.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hidden ctor to ensure that this utility class isn't
|
||||||
|
* instantiated explicitly.
|
||||||
|
*/
|
||||||
|
private ProviderUtils() {
|
||||||
|
// hide ctor for checkstyle compliance
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a nested URI to decode the underlying path. The translation takes
|
* Convert a nested URI to decode the underlying path. The translation takes
|
||||||
* the authority and parses it into the underlying scheme and authority.
|
* the authority and parses it into the underlying scheme and authority.
|
||||||
|
@ -35,12 +55,16 @@ public class ProviderUtils {
|
||||||
* @return the unnested path
|
* @return the unnested path
|
||||||
*/
|
*/
|
||||||
public static Path unnestUri(URI nestedUri) {
|
public static Path unnestUri(URI nestedUri) {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
String authority = nestedUri.getAuthority();
|
||||||
|
if (authority != null) {
|
||||||
String[] parts = nestedUri.getAuthority().split("@", 2);
|
String[] parts = nestedUri.getAuthority().split("@", 2);
|
||||||
StringBuilder result = new StringBuilder(parts[0]);
|
result.append(parts[0]);
|
||||||
result.append("://");
|
result.append("://");
|
||||||
if (parts.length == 2) {
|
if (parts.length == 2) {
|
||||||
result.append(parts[1]);
|
result.append(parts[1]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
result.append(nestedUri.getPath());
|
result.append(nestedUri.getPath());
|
||||||
if (nestedUri.getQuery() != null) {
|
if (nestedUri.getQuery() != null) {
|
||||||
result.append("?");
|
result.append("?");
|
||||||
|
@ -79,4 +103,75 @@ public class ProviderUtils {
|
||||||
"//file" + localFile.getSchemeSpecificPart(), localFile.getFragment());
|
"//file" + localFile.getSchemeSpecificPart(), localFile.getFragment());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There are certain integrations of the credential provider API in
|
||||||
|
* which a recursive dependency between the provider and the hadoop
|
||||||
|
* filesystem abstraction causes a problem. These integration points
|
||||||
|
* need to leverage this utility method to remove problematic provider
|
||||||
|
* types from the existing provider path within the configuration.
|
||||||
|
*
|
||||||
|
* @param config the existing configuration with provider path
|
||||||
|
* @param fileSystemClass the class which providers must be compatible
|
||||||
|
* @return Configuration clone with new provider path
|
||||||
|
*/
|
||||||
|
public static Configuration excludeIncompatibleCredentialProviders(
|
||||||
|
Configuration config, Class<? extends FileSystem> fileSystemClass)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
String providerPath = config.get(
|
||||||
|
CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH);
|
||||||
|
|
||||||
|
if (providerPath == null) {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
StringBuffer newProviderPath = new StringBuffer();
|
||||||
|
String[] providers = providerPath.split(",");
|
||||||
|
Path path = null;
|
||||||
|
for (String provider: providers) {
|
||||||
|
try {
|
||||||
|
path = unnestUri(new URI(provider));
|
||||||
|
Class<? extends FileSystem> clazz = null;
|
||||||
|
try {
|
||||||
|
String scheme = path.toUri().getScheme();
|
||||||
|
clazz = FileSystem.getFileSystemClass(scheme, config);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
// not all providers are filesystem based
|
||||||
|
// for instance user:/// will not be able to
|
||||||
|
// have a filesystem class associated with it.
|
||||||
|
if (newProviderPath.length() > 0) {
|
||||||
|
newProviderPath.append(",");
|
||||||
|
}
|
||||||
|
newProviderPath.append(provider);
|
||||||
|
}
|
||||||
|
if (clazz != null) {
|
||||||
|
if (fileSystemClass.isAssignableFrom(clazz)) {
|
||||||
|
LOG.debug("Filesystem based provider" +
|
||||||
|
" excluded from provider path due to recursive dependency: "
|
||||||
|
+ provider);
|
||||||
|
} else {
|
||||||
|
if (newProviderPath.length() > 0) {
|
||||||
|
newProviderPath.append(",");
|
||||||
|
}
|
||||||
|
newProviderPath.append(provider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
LOG.warn("Credential Provider URI is invalid." + provider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String effectivePath = newProviderPath.toString();
|
||||||
|
if (effectivePath.equals(providerPath)) {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
Configuration conf = new Configuration(config);
|
||||||
|
if (effectivePath.equals("")) {
|
||||||
|
conf.unset(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH);
|
||||||
|
} else {
|
||||||
|
conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH,
|
||||||
|
effectivePath);
|
||||||
|
}
|
||||||
|
return conf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.apache.hadoop.classification.InterfaceAudience;
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.security.ProviderUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key provider that simply returns the storage account key from the
|
* Key provider that simply returns the storage account key from the
|
||||||
|
@ -41,7 +42,9 @@ public class SimpleKeyProvider implements KeyProvider {
|
||||||
throws KeyProviderException {
|
throws KeyProviderException {
|
||||||
String key = null;
|
String key = null;
|
||||||
try {
|
try {
|
||||||
char[] keyChars = conf.getPassword(getStorageAccountKeyName(accountName));
|
Configuration c = ProviderUtils.excludeIncompatibleCredentialProviders(
|
||||||
|
conf, NativeAzureFileSystem.class);
|
||||||
|
char[] keyChars = c.getPassword(getStorageAccountKeyName(accountName));
|
||||||
if (keyChars != null) {
|
if (keyChars != null) {
|
||||||
key = new String(keyChars);
|
key = new String(keyChars);
|
||||||
}
|
}
|
||||||
|
|
|
@ -461,4 +461,39 @@ public class TestWasbUriAndConfiguration {
|
||||||
FileSystem.closeAll();
|
FileSystem.closeAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCredentialProviderPathExclusions() throws Exception {
|
||||||
|
String providerPath =
|
||||||
|
"user:///,jceks://wasb/user/hrt_qa/sqoopdbpasswd.jceks," +
|
||||||
|
"jceks://hdfs@nn1.example.com/my/path/test.jceks";
|
||||||
|
Configuration config = new Configuration();
|
||||||
|
config.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH,
|
||||||
|
providerPath);
|
||||||
|
String newPath = "user:///,jceks://hdfs@nn1.example.com/my/path/test.jceks";
|
||||||
|
|
||||||
|
excludeAndTestExpectations(config, newPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExcludeAllProviderTypesFromConfig() throws Exception {
|
||||||
|
String providerPath =
|
||||||
|
"jceks://wasb/tmp/test.jceks," +
|
||||||
|
"jceks://wasb@/my/path/test.jceks";
|
||||||
|
Configuration config = new Configuration();
|
||||||
|
config.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH,
|
||||||
|
providerPath);
|
||||||
|
String newPath = null;
|
||||||
|
|
||||||
|
excludeAndTestExpectations(config, newPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void excludeAndTestExpectations(Configuration config, String newPath)
|
||||||
|
throws Exception {
|
||||||
|
Configuration conf = ProviderUtils.excludeIncompatibleCredentialProviders(
|
||||||
|
config, NativeAzureFileSystem.class);
|
||||||
|
String effectivePath = conf.get(
|
||||||
|
CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, null);
|
||||||
|
assertEquals(newPath, effectivePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue