-
Notifications
You must be signed in to change notification settings - Fork 9.1k
HADOOP-19406. ABFS: [FNSOverBlob] Support User Delegation SAS for FNS Blob #7523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b124f05
dab40ab
317c523
48b46cf
e1e66c8
021abb2
a01beb7
6651d38
e95e230
82ef314
87e0664
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ | |
|
||
package org.apache.hadoop.fs.azurebfs; | ||
|
||
import javax.annotation.Nullable; | ||
import java.io.File; | ||
import java.io.FileNotFoundException; | ||
import java.io.IOException; | ||
|
@@ -41,7 +42,6 @@ | |
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.Future; | ||
import javax.annotation.Nullable; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
@@ -118,8 +118,24 @@ | |
import static org.apache.hadoop.fs.CommonConfigurationKeys.IOSTATISTICS_LOGGING_LEVEL; | ||
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.AbfsStatistic.CALL_APPEND; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_CREATE; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_CREATE_NON_RECURSIVE; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_DELETE; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_EXIST; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_GET_DELEGATION_TOKEN; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_GET_FILE_STATUS; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_LIST_STATUS; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_MKDIRS; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_OPEN; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_RENAME; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.DIRECTORIES_CREATED; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.DIRECTORIES_DELETED; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.ERROR_IGNORED; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.FILES_CREATED; | ||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.FILES_DELETED; | ||
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.CPK_IN_NON_HNS_ACCOUNT_ERROR_MESSAGE; | ||
import static org.apache.hadoop.fs.azurebfs.constants.AbfsServiceType.DFS; | ||
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.DATA_BLOCKS_BUFFER; | ||
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_ACCOUNT_IS_HNS_ENABLED; | ||
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_BLOCK_UPLOAD_ACTIVE_BLOCKS; | ||
|
@@ -240,16 +256,18 @@ public void initialize(URI uri, Configuration configuration) | |
|
||
/* | ||
* Validates if the correct SAS Token provider is configured for non-HNS accounts. | ||
* For non-HNS accounts, if the authentication type is set to SAS, only a fixed SAS Token is supported as of now. | ||
* A custom SAS Token Provider should not be configured in such cases, as it will override the FixedSASTokenProvider and render it unused. | ||
* If the namespace is not enabled and the FixedSASTokenProvider is not configured, | ||
* For non-HNS accounts with Blob endpoint, both fixed SAS Token and custom SAS Token provider are supported. | ||
* For non-HNS accounts with DFS endpoint, if the authentication type is set to SAS, only fixed SAS Token is supported as of now. | ||
* A custom SAS Token Provider should not be configured in this case as it will override the FixedSASTokenProvider and render it unused. | ||
* If the namespace is not enabled and the FixedSASTokenProvider is not configured for non-HNS accounts with DFS endpoint, | ||
* an InvalidConfigurationValueException will be thrown. | ||
* | ||
* @throws InvalidConfigurationValueException if account is not namespace enabled and FixedSASTokenProvider is not configured. | ||
*/ | ||
try { | ||
if (abfsConfiguration.getAuthType(abfsConfiguration.getAccountName()) == AuthType.SAS && // Auth type is SAS | ||
!tryGetIsNamespaceEnabled(new TracingContext(initFSTracingContext)) && // Account is FNS | ||
abfsConfiguration.getFsConfiguredServiceType() == DFS && // Service type is DFS | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the constant for DFS There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is using the AbfsServiceType.DFS constant here |
||
!abfsConfiguration.isFixedSASTokenProviderConfigured()) { // Fixed SAS Token Provider is not configured | ||
throw new InvalidConfigurationValueException(FS_AZURE_SAS_FIXED_TOKEN, UNAUTHORIZED_SAS); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -367,7 +367,7 @@ public ListResponseData listPath(final String relativePath, final boolean recurs | |
abfsUriQueryBuilder.addQuery(QUERY_PARAM_DELIMITER, FORWARD_SLASH); | ||
} | ||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_MAX_RESULTS, String.valueOf(listMaxResults)); | ||
appendSASTokenToQuery(relativePath, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder); | ||
appendSASTokenToQuery(relativePath, SASTokenProvider.LIST_OPERATION_BLOB, abfsUriQueryBuilder); | ||
anujmodi2021 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
final URL url = createRequestUrl(abfsUriQueryBuilder.toString()); | ||
final AbfsRestOperation op = getAbfsRestOperation( | ||
|
@@ -555,11 +555,14 @@ public AbfsRestOperation createPathRestOp(final String path, | |
final ContextEncryptionAdapter contextEncryptionAdapter, | ||
final TracingContext tracingContext) throws AzureBlobFileSystemException { | ||
final List<AbfsHttpHeader> requestHeaders = createDefaultHeaders(); | ||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder(); | ||
if (isFile) { | ||
addEncryptionKeyRequestHeaders(path, requestHeaders, true, | ||
contextEncryptionAdapter, tracingContext); | ||
appendSASTokenToQuery(path, SASTokenProvider.CREATE_FILE_OPERATION, abfsUriQueryBuilder); | ||
} else { | ||
requestHeaders.add(new AbfsHttpHeader(X_MS_META_HDI_ISFOLDER, TRUE)); | ||
appendSASTokenToQuery(path, SASTokenProvider.CREATE_DIRECTORY_OPERATION, abfsUriQueryBuilder); | ||
} | ||
requestHeaders.add(new AbfsHttpHeader(CONTENT_LENGTH, ZERO)); | ||
if (isAppendBlob) { | ||
|
@@ -574,9 +577,6 @@ public AbfsRestOperation createPathRestOp(final String path, | |
requestHeaders.add(new AbfsHttpHeader(HttpHeaderConfigurations.IF_MATCH, eTag)); | ||
} | ||
|
||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder(); | ||
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder); | ||
|
||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString()); | ||
final AbfsRestOperation op = getAbfsRestOperation( | ||
AbfsRestOperationType.PutBlob, | ||
|
@@ -698,7 +698,7 @@ public AbfsRestOperation acquireLease(final String path, | |
|
||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder(); | ||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, LEASE); | ||
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder); | ||
appendSASTokenToQuery(path, SASTokenProvider.LEASE_BLOB_OPERATION, abfsUriQueryBuilder); | ||
|
||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString()); | ||
final AbfsRestOperation op = getAbfsRestOperation( | ||
|
@@ -726,7 +726,7 @@ public AbfsRestOperation renewLease(final String path, final String leaseId, | |
|
||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder(); | ||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, LEASE); | ||
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder); | ||
appendSASTokenToQuery(path, SASTokenProvider.LEASE_BLOB_OPERATION, abfsUriQueryBuilder); | ||
|
||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString()); | ||
final AbfsRestOperation op = getAbfsRestOperation( | ||
|
@@ -754,7 +754,7 @@ public AbfsRestOperation releaseLease(final String path, final String leaseId, | |
|
||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder(); | ||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, LEASE); | ||
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder); | ||
appendSASTokenToQuery(path, SASTokenProvider.LEASE_BLOB_OPERATION, abfsUriQueryBuilder); | ||
|
||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString()); | ||
final AbfsRestOperation op = getAbfsRestOperation( | ||
|
@@ -781,7 +781,7 @@ public AbfsRestOperation breakLease(final String path, | |
|
||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder(); | ||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, LEASE); | ||
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder); | ||
appendSASTokenToQuery(path, SASTokenProvider.LEASE_BLOB_OPERATION, abfsUriQueryBuilder); | ||
|
||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString()); | ||
final AbfsRestOperation op = getAbfsRestOperation( | ||
|
@@ -829,6 +829,8 @@ destination, sourceEtag, isAtomicRenameKey(source), tracingContext | |
if (blobRenameHandler.execute(false)) { | ||
final AbfsUriQueryBuilder abfsUriQueryBuilder | ||
= createDefaultUriQueryBuilder(); | ||
appendSASTokenToQuery(source, SASTokenProvider.RENAME_SOURCE_OPERATION, | ||
abfsUriQueryBuilder); | ||
final URL url = createRequestUrl(destination, | ||
abfsUriQueryBuilder.toString()); | ||
final List<AbfsHttpHeader> requestHeaders = createDefaultHeaders(); | ||
|
@@ -902,7 +904,7 @@ public AbfsRestOperation append(final String path, | |
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, BLOCK); | ||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_BLOCKID, reqParams.getBlockId()); | ||
|
||
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, | ||
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.WRITE_OPERATION, | ||
abfsUriQueryBuilder, cachedSasToken); | ||
|
||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString()); | ||
|
@@ -975,7 +977,7 @@ public AbfsRestOperation appendBlock(final String path, | |
} | ||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder(); | ||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, APPEND_BLOCK); | ||
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder); | ||
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.WRITE_OPERATION, abfsUriQueryBuilder); | ||
|
||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString()); | ||
final AbfsRestOperation op = getAbfsRestOperation( | ||
|
@@ -1067,7 +1069,7 @@ public AbfsRestOperation flush(byte[] buffer, | |
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder(); | ||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, BLOCKLIST); | ||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_CLOSE, String.valueOf(isClose)); | ||
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, | ||
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.WRITE_OPERATION, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is using WRITE_OPERATION for the flush call a good idea? Should we create another operation type for this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have the same operation type for DFS flush call as well. We can keep it as it is I think |
||
abfsUriQueryBuilder, cachedSasToken); | ||
|
||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString()); | ||
|
@@ -1129,7 +1131,7 @@ public AbfsRestOperation setPathProperties(final String path, | |
|
||
AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder(); | ||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, METADATA); | ||
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder); | ||
appendSASTokenToQuery(path, SASTokenProvider.SET_PROPERTIES_OPERATION, abfsUriQueryBuilder); | ||
|
||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString()); | ||
final AbfsRestOperation op = getAbfsRestOperation( | ||
|
@@ -1208,7 +1210,7 @@ public AbfsRestOperation getPathStatus(final String path, | |
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder(); | ||
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_UPN, | ||
String.valueOf(getAbfsConfiguration().isUpnUsed())); | ||
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, | ||
appendSASTokenToQuery(path, SASTokenProvider.GET_PROPERTIES_OPERATION, | ||
abfsUriQueryBuilder); | ||
|
||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString()); | ||
|
@@ -1287,7 +1289,7 @@ public AbfsRestOperation read(final String path, | |
} | ||
|
||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder(); | ||
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, | ||
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.READ_OPERATION, | ||
abfsUriQueryBuilder, cachedSasToken); | ||
|
||
URL url = createRequestUrl(path, abfsUriQueryBuilder.toString()); | ||
|
@@ -1449,7 +1451,7 @@ public AbfsRestOperation getBlockList(final String path, | |
final List<AbfsHttpHeader> requestHeaders = createDefaultHeaders(); | ||
|
||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder(); | ||
String operation = SASTokenProvider.FIXED_SAS_STORE_OPERATION; | ||
String operation = SASTokenProvider.READ_OPERATION; | ||
appendSASTokenToQuery(path, operation, abfsUriQueryBuilder); | ||
|
||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, BLOCKLIST); | ||
|
@@ -1487,9 +1489,9 @@ public AbfsRestOperation copyBlob(Path sourceBlobPath, | |
String dstBlobRelativePath = destinationBlobPath.toUri().getPath(); | ||
String srcBlobRelativePath = sourceBlobPath.toUri().getPath(); | ||
appendSASTokenToQuery(dstBlobRelativePath, | ||
SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilderDst); | ||
SASTokenProvider.COPY_BLOB_DST_OPERATION, abfsUriQueryBuilderDst); | ||
appendSASTokenToQuery(srcBlobRelativePath, | ||
SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilderSrc); | ||
SASTokenProvider.COPY_BLOB_SRC_OPERATION, abfsUriQueryBuilderSrc); | ||
final URL url = createRequestUrl(dstBlobRelativePath, | ||
abfsUriQueryBuilderDst.toString()); | ||
final String sourcePathUrl = createRequestUrl(srcBlobRelativePath, | ||
|
@@ -1523,7 +1525,7 @@ public AbfsRestOperation deleteBlobPath(final Path blobPath, | |
AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder(); | ||
String blobRelativePath = blobPath.toUri().getPath(); | ||
appendSASTokenToQuery(blobRelativePath, | ||
SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder); | ||
SASTokenProvider.DELETE_OPERATION, abfsUriQueryBuilder); | ||
final URL url = createRequestUrl(blobRelativePath, | ||
abfsUriQueryBuilder.toString()); | ||
final List<AbfsHttpHeader> requestHeaders = createDefaultHeaders(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -652,13 +652,17 @@ To know more about how SAS Authentication works refer to | |
[Grant limited access to Azure Storage resources using shared access signatures (SAS)](https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview) | ||
|
||
There are three types of SAS supported by Azure Storage: | ||
- [User Delegation SAS](https://learn.microsoft.com/en-us/rest/api/storageservices/create-user-delegation-sas): Recommended for use with ABFS Driver with HNS Enabled ADLS Gen2 accounts. It is Identity based SAS that works at blob/directory level) | ||
- [User Delegation SAS](https://learn.microsoft.com/en-us/rest/api/storageservices/create-user-delegation-sas): | ||
SAS-based authentication works with HNS-enabled ADLS Gen2 accounts | ||
(recommended for use with ABFS) and is also supported with non-HNS (FNS) Blob | ||
accounts. However, it is **NOT SUPPORTED** with FNS-DFS accounts. | ||
- [Service SAS](https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas): Global and works at container level. | ||
- [Account SAS](https://learn.microsoft.com/en-us/rest/api/storageservices/create-account-sas): Global and works at account level. | ||
|
||
#### Known Issues With SAS | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SAS-based authentication works with HNS-enabled ADLS Gen2 accounts (recommended for use with ABFS) and is also supported with non-HNS (FNS) Blob accounts. However, it is not supported with FNS-DFS accounts. - better to frame like this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Taken |
||
- SAS Based Authentication works only with HNS Enabled ADLS Gen2 Accounts which | ||
is a recommended account type to be used with ABFS. | ||
- SAS Based Authentication works with HNS Enabled ADLS Gen2 Accounts (which | ||
is a recommended account type to be used with ABFS). It is also supported with | ||
non-HNS (FNS) Blob accounts. It is **NOT SUPPORTED** with FNS-DFS accounts. | ||
- Certain root level operations are known to fail with SAS Based Authentication. | ||
|
||
#### Using User Delegation SAS with ABFS | ||
|
@@ -1465,7 +1469,12 @@ Once the above properties are configured, `hdfs dfs -ls abfs://container1@abfswa | |
|
||
Following failures are known and expected to fail as of now. | ||
1. AzureBlobFileSystem.setXAttr() and AzureBlobFileSystem.getXAttr() will fail when attempted on root ("/") path with `Operation failed: "The request URI is invalid.", HTTP 400 Bad Request` | ||
|
||
2. If you're using user-delegation SAS authentication: | ||
- Listing operation for HNS accounts (on DFS endpoint) works with SAS token supporting either blob or directory | ||
scopes (Signed Resource Type as Blob or Directory), | ||
though it is intended to work only at the directory scope. It is a known bug. | ||
- AzureBlobFileSystem.getFileStatus() is expected to fail at root ("/") path with | ||
`Operation failed: "Server failed to authenticate the request.", HTTP 401 Unauthorized Error` | ||
## <a name="testing"></a> Testing ABFS | ||
|
||
See the relevant section in [Testing Azure](testing_azure.html). |
Uh oh!
There was an error while loading. Please reload this page.