Skip to content

Commit ac8970d

Browse files
committed
Enforce that S3 credentials are vended when requested
This is a follow-up change to #2672 striving to improve user-facing error reporting for S3 storage systems without STS. * Update call paths leading to `AccessConfig` to indicate whether the caller requires credentials to be vended or not. * Add checks to `AwsCredentialsStorageIntegration` (leading to 400) that S3 storage credentials are vended when requested. * Only those S3 systems where STS is not available (or disabled / not permitted) are affected. * Other storage integrations are not affected by this PR.
1 parent ea50fe3 commit ac8970d

23 files changed

+207
-66
lines changed

polaris-core/src/main/java/org/apache/polaris/core/persistence/AtomicOperationMetaStoreManager.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,7 @@ private void revokeGrantRecord(
15801580
boolean allowListOperation,
15811581
@Nonnull Set<String> allowedReadLocations,
15821582
@Nonnull Set<String> allowedWriteLocations,
1583+
boolean credentialsRequired,
15831584
Optional<String> refreshCredentialsEndpoint) {
15841585

15851586
// get meta store session we should be using
@@ -1616,12 +1617,13 @@ private void revokeGrantRecord(
16161617

16171618
try {
16181619
AccessConfig accessConfig =
1619-
storageIntegration.getSubscopedCreds(
1620+
storageIntegration.getAccessConfig(
16201621
callCtx.getRealmConfig(),
16211622
allowListOperation,
16221623
allowedReadLocations,
16231624
allowedWriteLocations,
1624-
refreshCredentialsEndpoint);
1625+
refreshCredentialsEndpoint,
1626+
credentialsRequired);
16251627
return new ScopedCredentialsResult(accessConfig);
16261628
} catch (Exception ex) {
16271629
return new ScopedCredentialsResult(

polaris-core/src/main/java/org/apache/polaris/core/persistence/TransactionWorkspaceMetaStoreManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ public ScopedCredentialsResult getSubscopedCredsForEntity(
346346
boolean allowListOperation,
347347
@Nonnull Set<String> allowedReadLocations,
348348
@Nonnull Set<String> allowedWriteLocations,
349+
boolean credentialsRequired,
349350
Optional<String> refreshCredentialsEndpoint) {
350351
return delegate.getSubscopedCredsForEntity(
351352
callCtx,
@@ -355,6 +356,7 @@ public ScopedCredentialsResult getSubscopedCredsForEntity(
355356
allowListOperation,
356357
allowedReadLocations,
357358
allowedWriteLocations,
359+
credentialsRequired,
358360
refreshCredentialsEndpoint);
359361
}
360362

polaris-core/src/main/java/org/apache/polaris/core/persistence/transactional/TransactionalMetaStoreManagerImpl.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2073,6 +2073,7 @@ private PolarisEntityResolver resolveSecurableToRoleGrant(
20732073
boolean allowListOperation,
20742074
@Nonnull Set<String> allowedReadLocations,
20752075
@Nonnull Set<String> allowedWriteLocations,
2076+
boolean credentialsRequired,
20762077
Optional<String> refreshCredentialsEndpoint) {
20772078

20782079
// get meta store session we should be using
@@ -2104,12 +2105,13 @@ private PolarisEntityResolver resolveSecurableToRoleGrant(
21042105

21052106
try {
21062107
AccessConfig accessConfig =
2107-
storageIntegration.getSubscopedCreds(
2108+
storageIntegration.getAccessConfig(
21082109
callCtx.getRealmConfig(),
21092110
allowListOperation,
21102111
allowedReadLocations,
21112112
allowedWriteLocations,
2112-
refreshCredentialsEndpoint);
2113+
refreshCredentialsEndpoint,
2114+
credentialsRequired);
21132115
return new ScopedCredentialsResult(accessConfig);
21142116
} catch (Exception ex) {
21152117
return new ScopedCredentialsResult(

polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisCredentialVendor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ public interface PolarisCredentialVendor {
4242
* supported by the storage type it will be returned to the client in the appropriate
4343
* properties. The endpoint may be relative to the base URI and the client is responsible for
4444
* handling the relative path
45+
* @param credentialsRequired if {@code true} indicates that the caller requires the returned
46+
* {@link AccessConfig} to have storage credentials; if {@code false} the returned {@link
47+
* AccessConfig} may or may not contain credentials.
4548
* @return an enum map containing the scoped credentials
4649
*/
4750
@Nonnull
@@ -53,5 +56,6 @@ ScopedCredentialsResult getSubscopedCredsForEntity(
5356
boolean allowListOperation,
5457
@Nonnull Set<String> allowedReadLocations,
5558
@Nonnull Set<String> allowedWriteLocations,
59+
boolean credentialsRequired,
5660
Optional<String> refreshCredentialsEndpoint);
5761
}

polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageIntegration.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,18 @@ public String getStorageIdentifierOrId() {
6060
* supported by the storage type it will be returned to the client in the appropriate
6161
* properties. The endpoint may be relative to the base URI and the client is responsible for
6262
* handling the relative path
63+
* @param credentialsRequired if {@code true} indicates that the caller requires the returned
64+
* {@link AccessConfig} to have storage credentials; if {@code false} the returned {@link
65+
* AccessConfig} may or may not contain credentials.
6366
* @return An enum map including the scoped credentials
6467
*/
65-
public abstract AccessConfig getSubscopedCreds(
68+
public abstract AccessConfig getAccessConfig(
6669
@Nonnull RealmConfig realmConfig,
6770
boolean allowListOperation,
6871
@Nonnull Set<String> allowedReadLocations,
6972
@Nonnull Set<String> allowedWriteLocations,
70-
Optional<String> refreshCredentialsEndpoint);
73+
Optional<String> refreshCredentialsEndpoint,
74+
boolean credentialsRequired);
7175

7276
/**
7377
* Validate access for the provided operation actions and locations.

polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegration.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import static org.apache.polaris.core.config.FeatureConfiguration.STORAGE_CREDENTIAL_DURATION_SECONDS;
2222

23+
import com.google.common.base.Preconditions;
2324
import jakarta.annotation.Nonnull;
2425
import java.net.URI;
2526
import java.util.HashMap;
@@ -70,19 +71,25 @@ public AwsCredentialsStorageIntegration(
7071

7172
/** {@inheritDoc} */
7273
@Override
73-
public AccessConfig getSubscopedCreds(
74+
public AccessConfig getAccessConfig(
7475
@Nonnull RealmConfig realmConfig,
7576
boolean allowListOperation,
7677
@Nonnull Set<String> allowedReadLocations,
7778
@Nonnull Set<String> allowedWriteLocations,
78-
Optional<String> refreshCredentialsEndpoint) {
79+
Optional<String> refreshCredentialsEndpoint,
80+
boolean credentialsRequired) {
7981
int storageCredentialDurationSeconds =
8082
realmConfig.getConfig(STORAGE_CREDENTIAL_DURATION_SECONDS);
8183
AwsStorageConfigurationInfo storageConfig = config();
8284
String region = storageConfig.getRegion();
8385
AccessConfig.Builder accessConfig = AccessConfig.builder();
8486

85-
if (shouldUseSts(storageConfig)) {
87+
boolean shouldUseSts = shouldUseSts(storageConfig);
88+
Preconditions.checkArgument(
89+
!credentialsRequired || shouldUseSts,
90+
"Credential vending was requested, but STS is not available");
91+
92+
if (shouldUseSts) {
8693
AssumeRoleRequest.Builder request =
8794
AssumeRoleRequest.builder()
8895
.externalId(storageConfig.getExternalId())

polaris-core/src/main/java/org/apache/polaris/core/storage/azure/AzureCredentialsStorageIntegration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,14 @@ public AzureCredentialsStorageIntegration(AzureStorageConfigurationInfo config)
7373
}
7474

7575
@Override
76-
public AccessConfig getSubscopedCreds(
76+
public AccessConfig getAccessConfig(
7777
@Nonnull RealmConfig realmConfig,
7878
boolean allowListOperation,
7979
@Nonnull Set<String> allowedReadLocations,
8080
@Nonnull Set<String> allowedWriteLocations,
81-
Optional<String> refreshCredentialsEndpoint) {
81+
Optional<String> refreshCredentialsEndpoint,
82+
boolean credentialsRequired) {
83+
8284
String loc =
8385
!allowedWriteLocations.isEmpty()
8486
? allowedWriteLocations.stream().findAny().orElse(null)

polaris-core/src/main/java/org/apache/polaris/core/storage/cache/StorageCredentialCache.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ private long maxCacheDurationMs(RealmConfig realmConfig) {
101101
* @param allowListOperation whether allow list action on the provided read and write locations
102102
* @param allowedReadLocations a set of allowed to read locations
103103
* @param allowedWriteLocations a set of allowed to write locations.
104+
* @param credentialsRequired if {@code true} indicates that the caller requires the returned
105+
* {@link AccessConfig} to have storage credentials; if {@code false} the returned {@link
106+
* AccessConfig} may or may not contain credentials.
104107
* @return the a map of string containing the scoped creds information
105108
*/
106109
public AccessConfig getOrGenerateSubScopeCreds(
@@ -110,7 +113,8 @@ public AccessConfig getOrGenerateSubScopeCreds(
110113
boolean allowListOperation,
111114
@Nonnull Set<String> allowedReadLocations,
112115
@Nonnull Set<String> allowedWriteLocations,
113-
Optional<String> refreshCredentialsEndpoint) {
116+
Optional<String> refreshCredentialsEndpoint,
117+
boolean credentialsRequired) {
114118
if (!isTypeSupported(polarisEntity.getType())) {
115119
diagnostics.fail(
116120
"entity_type_not_suppported_to_scope_creds", "type={}", polarisEntity.getType());
@@ -122,7 +126,8 @@ public AccessConfig getOrGenerateSubScopeCreds(
122126
allowListOperation,
123127
allowedReadLocations,
124128
allowedWriteLocations,
125-
refreshCredentialsEndpoint);
129+
refreshCredentialsEndpoint,
130+
credentialsRequired);
126131
LOGGER.atDebug().addKeyValue("key", key).log("subscopedCredsCache");
127132
Function<StorageCredentialCacheKey, StorageCredentialCacheEntry> loader =
128133
k -> {
@@ -136,6 +141,7 @@ public AccessConfig getOrGenerateSubScopeCreds(
136141
k.allowedListAction(),
137142
k.allowedReadLocations(),
138143
k.allowedWriteLocations(),
144+
k.credentialsRequired(),
139145
k.refreshCredentialsEndpoint());
140146
if (scopedCredentialsResult.isSuccess()) {
141147
long maxCacheDurationMs = maxCacheDurationMs(callCtx.getRealmConfig());

polaris-core/src/main/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheKey.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,17 @@ public interface StorageCredentialCacheKey {
5151
@Value.Parameter(order = 7)
5252
Optional<String> refreshCredentialsEndpoint();
5353

54+
@Value.Parameter(order = 8)
55+
boolean credentialsRequired();
56+
5457
static StorageCredentialCacheKey of(
5558
String realmId,
5659
PolarisEntity entity,
5760
boolean allowedListAction,
5861
Set<String> allowedReadLocations,
5962
Set<String> allowedWriteLocations,
60-
Optional<String> refreshCredentialsEndpoint) {
63+
Optional<String> refreshCredentialsEndpoint,
64+
boolean credentialsRequired) {
6165
String storageConfigSerializedStr =
6266
entity
6367
.getInternalPropertiesAsMap()
@@ -69,6 +73,7 @@ static StorageCredentialCacheKey of(
6973
allowedListAction,
7074
allowedReadLocations,
7175
allowedWriteLocations,
72-
refreshCredentialsEndpoint);
76+
refreshCredentialsEndpoint,
77+
credentialsRequired);
7378
}
7479
}

polaris-core/src/main/java/org/apache/polaris/core/storage/gcp/GcpCredentialsStorageIntegration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,14 @@ public GcpCredentialsStorageIntegration(
7272
}
7373

7474
@Override
75-
public AccessConfig getSubscopedCreds(
75+
public AccessConfig getAccessConfig(
7676
@Nonnull RealmConfig realmConfig,
7777
boolean allowListOperation,
7878
@Nonnull Set<String> allowedReadLocations,
7979
@Nonnull Set<String> allowedWriteLocations,
80-
Optional<String> refreshCredentialsEndpoint) {
80+
Optional<String> refreshCredentialsEndpoint,
81+
boolean credentialsRequired) {
82+
8183
try {
8284
sourceCredentials.refresh();
8385
} catch (IOException e) {

0 commit comments

Comments
 (0)