Skip to content

Commit

Permalink
HADOOP-19137. [ABFS] Prevent ABFS initialization for non-hierarchal-n…
Browse files Browse the repository at this point in the history
…amespace account if Customer-provided-key configs given. (#6752) (#6880)

Customer-provided-keys (CPK) configs are not allowed with non-hierarchal-namespace (non-HNS) accounts for ABFS. This patch aims to prevent ABFS initialization for non-HNS accounts if CPK configs are provided.

Contributed by: Pranav Saxena
  • Loading branch information
saxenapranav authored Jun 11, 2024
1 parent 5826b15 commit b69e7d1
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 142 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

import javax.annotation.Nullable;

import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.classification.VisibleForTesting;
import org.apache.hadoop.fs.impl.BackReference;
import org.apache.hadoop.security.ProviderUtils;
Expand Down Expand Up @@ -115,6 +116,7 @@
import static org.apache.hadoop.fs.CommonConfigurationKeys.IOSTATISTICS_LOGGING_LEVEL_DEFAULT;
import static org.apache.hadoop.fs.Options.OpenFileOptions.FS_OPTION_OPENFILE_STANDARD_OPTIONS;
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.*;
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.CPK_IN_NON_HNS_ACCOUNT_ERROR_MESSAGE;
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.DATA_BLOCKS_BUFFER;
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_BLOCK_UPLOAD_ACTIVE_BLOCKS;
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_BLOCK_UPLOAD_BUFFER_DIR;
Expand Down Expand Up @@ -223,6 +225,26 @@ public void initialize(URI uri, Configuration configuration)
}
}

/*
* Non-hierarchical-namespace account can not have a customer-provided-key(CPK).
* Fail initialization of filesystem if the configs are provided. CPK is of
* two types: GLOBAL_KEY, and ENCRYPTION_CONTEXT.
*/
if ((isEncryptionContextCPK(abfsConfiguration) || isGlobalKeyCPK(
abfsConfiguration))
&& !getIsNamespaceEnabled(
new TracingContext(clientCorrelationId, fileSystemId,
FSOperationType.CREATE_FILESYSTEM, tracingHeaderFormat,
listener))) {
/*
* Close the filesystem gracefully before throwing exception. Graceful close
* will ensure that all resources are released properly.
*/
close();
throw new PathIOException(uri.getPath(),
CPK_IN_NON_HNS_ACCOUNT_ERROR_MESSAGE);
}

LOG.trace("Initiate check for delegation token manager");
if (UserGroupInformation.isSecurityEnabled()) {
this.delegationTokenEnabled = abfsConfiguration.isDelegationTokenManagerEnabled();
Expand All @@ -239,6 +261,15 @@ public void initialize(URI uri, Configuration configuration)
LOG.debug("Initializing AzureBlobFileSystem for {} complete", uri);
}

private boolean isGlobalKeyCPK(final AbfsConfiguration abfsConfiguration) {
return StringUtils.isNotEmpty(
abfsConfiguration.getEncodedClientProvidedEncryptionKey());
}

private boolean isEncryptionContextCPK(final AbfsConfiguration abfsConfiguration) {
return abfsConfiguration.createEncryptionContextProvider() != null;
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
import org.apache.hadoop.fs.azurebfs.security.ContextEncryptionAdapter;
import org.apache.hadoop.fs.azurebfs.security.NoContextEncryptionAdapter;
import org.apache.hadoop.fs.azurebfs.utils.EncryptionType;
import org.apache.hadoop.fs.azurebfs.utils.NamespaceUtil;
import org.apache.hadoop.fs.impl.BackReference;
import org.apache.hadoop.fs.PathIOException;

Expand Down Expand Up @@ -182,7 +181,7 @@ public class AzureBlobFileSystemStore implements Closeable, ListingSupport {
private final AbfsConfiguration abfsConfiguration;
private final Set<String> azureAtomicRenameDirSet;
private Set<String> azureInfiniteLeaseDirSet;
private Trilean isNamespaceEnabled;
private volatile Trilean isNamespaceEnabled;
private final AuthType authType;
private final UserGroupInformation userGroupInformation;
private final IdentityTransformerInterface identityTransformer;
Expand Down Expand Up @@ -364,19 +363,62 @@ private String[] authorityParts(URI uri) throws InvalidUriAuthorityException, In
return authorityParts;
}

/**
* Resolves namespace information of the filesystem from the state of {@link #isNamespaceEnabled}.
* if the state is UNKNOWN, it will be determined by making a GET_ACL request
* to the root of the filesystem. GET_ACL call is synchronized to ensure a single
* call is made to determine the namespace information in case multiple threads are
* calling this method at the same time. The resolution of namespace information
* would be stored back as state of {@link #isNamespaceEnabled}.
*
* @param tracingContext tracing context
* @return true if namespace is enabled, false otherwise.
* @throws AzureBlobFileSystemException server errors.
*/
public boolean getIsNamespaceEnabled(TracingContext tracingContext)
throws AzureBlobFileSystemException {
try {
return this.isNamespaceEnabled.toBoolean();
return isNamespaceEnabled();
} catch (TrileanConversionException e) {
LOG.debug("isNamespaceEnabled is UNKNOWN; fall back and determine through"
+ " getAcl server call", e);
}

isNamespaceEnabled = Trilean.getTrilean(NamespaceUtil.isNamespaceEnabled(client, tracingContext));
return getNamespaceEnabledInformationFromServer(tracingContext);
}

private synchronized boolean getNamespaceEnabledInformationFromServer(
final TracingContext tracingContext) throws AzureBlobFileSystemException {
if (isNamespaceEnabled != Trilean.UNKNOWN) {
return isNamespaceEnabled.toBoolean();
}
try {
LOG.debug("Get root ACL status");
getClient().getAclStatus(AbfsHttpConstants.ROOT_PATH, tracingContext);
isNamespaceEnabled = Trilean.getTrilean(true);
} catch (AbfsRestOperationException ex) {
// Get ACL status is a HEAD request, its response doesn't contain
// errorCode
// So can only rely on its status code to determine its account type.
if (HttpURLConnection.HTTP_BAD_REQUEST != ex.getStatusCode()) {
throw ex;
}
isNamespaceEnabled = Trilean.getTrilean(false);
} catch (AzureBlobFileSystemException ex) {
throw ex;
}
return isNamespaceEnabled.toBoolean();
}

/**
* @return true if namespace is enabled, false otherwise.
* @throws TrileanConversionException if namespaceEnabled information is UNKNOWN
*/
@VisibleForTesting
boolean isNamespaceEnabled() throws TrileanConversionException {
return this.isNamespaceEnabled.toBoolean();
}

@VisibleForTesting
URIBuilder getURIBuilder(final String hostName, boolean isSecure) {
String scheme = isSecure ? FileSystemUriSchemes.HTTPS_SCHEME : FileSystemUriSchemes.HTTP_SCHEME;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.util.VersionInfo;

import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_ENCRYPTION_CONTEXT_PROVIDER_TYPE;
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_ENCRYPTION_ENCODED_CLIENT_PROVIDED_KEY;
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_ENCRYPTION_ENCODED_CLIENT_PROVIDED_KEY_SHA;

/**
* Responsible to keep all constant keys used in abfs rest client here.
*/
Expand Down Expand Up @@ -165,5 +169,35 @@ public static ApiVersion getCurrentVersion() {
*/
public static final Integer HTTP_STATUS_CATEGORY_QUOTIENT = 100;

/**
* List of configurations that are related to Customer-Provided-Keys.
* <ol>
* <li>
* {@value ConfigurationKeys#FS_AZURE_ENCRYPTION_CONTEXT_PROVIDER_TYPE}
* for ENCRYPTION_CONTEXT cpk-type.
* </li>
* <li>
* {@value ConfigurationKeys#FS_AZURE_ENCRYPTION_ENCODED_CLIENT_PROVIDED_KEY} and
* {@value ConfigurationKeys#FS_AZURE_ENCRYPTION_ENCODED_CLIENT_PROVIDED_KEY_SHA}
* for GLOBAL_KEY cpk-type.
* </li>
* </ol>
* List: {@value}
*/
private static final String CPK_CONFIG_LIST =
FS_AZURE_ENCRYPTION_CONTEXT_PROVIDER_TYPE + ", "
+ FS_AZURE_ENCRYPTION_ENCODED_CLIENT_PROVIDED_KEY + ", "
+ FS_AZURE_ENCRYPTION_ENCODED_CLIENT_PROVIDED_KEY_SHA;

/**
* Exception message on filesystem init if customer-provided-keys configs are provided
* for a non-hierarchical-namespace account: {@value}
*/
public static final String CPK_IN_NON_HNS_ACCOUNT_ERROR_MESSAGE =
"Non hierarchical-namespace account can not have configs enabled for "
+ "Customer Provided Keys. Following configs can not be given with "
+ "non-hierarchical-namespace account:"
+ CPK_CONFIG_LIST;

private AbfsHttpConstants() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsInvalidChecksumException;
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsDriverException;
import org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode;
import org.apache.hadoop.fs.azurebfs.utils.NamespaceUtil;
import org.apache.hadoop.fs.store.LogExactlyOnce;
import org.apache.hadoop.fs.azurebfs.AzureBlobFileSystemStore.Permissions;
import org.apache.hadoop.fs.azurebfs.extensions.EncryptionContextProvider;
Expand Down Expand Up @@ -120,7 +119,6 @@ public class AbfsClient implements Closeable {
private final AbfsThrottlingIntercept intercept;

private final ListeningScheduledExecutorService executorService;
private Boolean isNamespaceEnabled;

private boolean renameResilience;

Expand Down Expand Up @@ -305,9 +303,6 @@ private void addEncryptionKeyRequestHeaders(String path,
List<AbfsHttpHeader> requestHeaders, boolean isCreateFileRequest,
ContextEncryptionAdapter contextEncryptionAdapter, TracingContext tracingContext)
throws AzureBlobFileSystemException {
if (!getIsNamespaceEnabled(tracingContext)) {
return;
}
String encodedKey, encodedKeySHA256;
switch (encryptionType) {
case GLOBAL_KEY:
Expand Down Expand Up @@ -1489,15 +1484,6 @@ public synchronized String getAccessToken() throws IOException {
}
}

private synchronized Boolean getIsNamespaceEnabled(TracingContext tracingContext)
throws AzureBlobFileSystemException {
if (isNamespaceEnabled == null) {
setIsNamespaceEnabled(NamespaceUtil.isNamespaceEnabled(this,
tracingContext));
}
return isNamespaceEnabled;
}

protected Boolean getIsPaginatedDeleteEnabled() {
return abfsConfiguration.isPaginatedDeleteEnabled();
}
Expand Down Expand Up @@ -1687,11 +1673,6 @@ void setEncryptionContextProvider(EncryptionContextProvider provider) {
encryptionContextProvider = provider;
}

@VisibleForTesting
void setIsNamespaceEnabled(final Boolean isNamespaceEnabled) {
this.isNamespaceEnabled = isNamespaceEnabled;
}

/**
* Getter for abfsCounters from AbfsClient.
* @return AbfsCounters instance.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
import org.apache.hadoop.fs.azurebfs.security.AbfsDelegationTokenManager;
import org.apache.hadoop.fs.azurebfs.services.AbfsClient;
import org.apache.hadoop.fs.azurebfs.services.AbfsClientUtils;
import org.apache.hadoop.fs.azurebfs.services.AbfsOutputStream;
import org.apache.hadoop.fs.azurebfs.services.AuthType;
import org.apache.hadoop.fs.azurebfs.services.ITestAbfsClient;
Expand Down Expand Up @@ -209,8 +208,6 @@ public void setup() throws Exception {
wasb = new NativeAzureFileSystem(azureNativeFileSystemStore);
wasb.initialize(wasbUri, rawConfig);
}
// Todo: To be fixed in HADOOP-19137
AbfsClientUtils.setIsNamespaceEnabled(abfs.getAbfsClient(), true);
}

@After
Expand Down
Loading

0 comments on commit b69e7d1

Please sign in to comment.