Skip to content

Comments

Improve Access Delegation Mode Selection Algorithm#3750

Open
iting0321 wants to merge 5 commits intoapache:mainfrom
iting0321:improve-access-delegation-node-algo
Open

Improve Access Delegation Mode Selection Algorithm#3750
iting0321 wants to merge 5 commits intoapache:mainfrom
iting0321:improve-access-delegation-node-algo

Conversation

@iting0321
Copy link
Contributor

Summary

This PR introduces an improved algorithm for selecting the optimal AccessDelegationMode when clients request both VENDED_CREDENTIALS and REMOTE_SIGNING. The new implementation considers actual catalog capabilities (STS availability) rather than using simple heuristics.

Problem

Previously, when a client requested both VENDED_CREDENTIALS and REMOTE_SIGNING modes via the X-Iceberg-Access-Delegation header, Polaris would always prefer VENDED_CREDENTIALS.
However, this could lead to failures when:

  • STS is unavailable for the catalog's storage configuration
  • Credential subscoping is disabled via SKIP_CREDENTIAL_SUBSCOPING_INDIRECTION configuration

In these cases, REMOTE_SIGNING would be the better choice, but the original algorithm had no way to make this determination.

Solution

Introduced AccessDelegationModeResolver - a dedicated component that intelligently selects the optimal access delegation mode based on:

  • Client request - The modes specified in X-Iceberg-Access-Delegation header
  • STS availability - Checks AwsStorageConfigurationInfo.getStsUnavailable() flag
  • Credential subscoping configuration - Respects SKIP_CREDENTIAL_SUBSCOPING_INDIRECTION setting

How to Test

./gradlew :polaris-runtime-service:test --tests "AccessDelegationModeResolverTest"

Related Issues

#3090 - Improve access delegation mode selection algorithm

Checklist

  • 🛡️ Don't disclose security issues! (contact security@apache.org)
  • 🔗 Clearly explained why the changes are needed, or linked related issues: Fixes Improve access delegation mode selection algorithm #3090
  • 🧪 Added/updated tests with good coverage, or manually tested (and explained how) - Added AccessDelegationModeResolverTest with 15 unit tests
  • 💡 Added comments for complex logic
  • 🧾 Updated CHANGELOG.md (if needed)
  • 📚 Updated documentation in site/content/in-dev/unreleased (if needed)

@dimas-b
Copy link
Contributor

dimas-b commented Feb 12, 2026

Hi @iting0321 :

[...] In these cases, REMOTE_SIGNING would be the better choice [...]

I'm not sure Polaris supports remote signing yet 🤔

@adutra : WDYT?

Copy link
Contributor

@adutra adutra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @iting0321 for this nice improvement!

I had something similar in my PR for remote signing #2280.

Comment on lines 144 to 146
Boolean.TRUE.equals(
realmConfig.getConfig(
FeatureConfiguration.SKIP_CREDENTIAL_SUBSCOPING_INDIRECTION, catalogEntity));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Contrary to the javadocs, this method cannot return null since there is a default value (false).
  2. The SKIP_CREDENTIAL_SUBSCOPING_INDIRECTION is not overridable at catalog level.
Suggested change
Boolean.TRUE.equals(
realmConfig.getConfig(
FeatureConfiguration.SKIP_CREDENTIAL_SUBSCOPING_INDIRECTION, catalogEntity));
boolean skipCredentialSubscoping =
realmConfig.getConfig(FeatureConfiguration.SKIP_CREDENTIAL_SUBSCOPING_INDIRECTION);

catalogEntity.getName());
return REMOTE_SIGNING;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are missing one important check here:

      boolean credentialSubscopingAuthorized =
          baseCatalog instanceof IcebergCatalog
              || realmConfig.getConfig(
                  ALLOW_FEDERATED_CATALOGS_CREDENTIAL_VENDING, catalogEntity);

      if (!credentialSubscopingAuthorized) {
        LOGGER.debug(
            "Credential subscoping is not authorized for catalog {}, selecting REMOTE_SIGNING",
            catalogEntity.getName());
        return REMOTE_SIGNING;
      }

@adutra
Copy link
Contributor

adutra commented Feb 12, 2026

I'm not sure Polaris supports remote signing yet 🤔

@adutra : WDYT?

Polaris does not support remote signing yet indeed, but the changes in this PR go in the right direction, and imho, can be adopted before remote signing lands.

@dimas-b
Copy link
Contributor

dimas-b commented Feb 12, 2026

Improving access delegation mode selection before remote signing is fully supports sounds reasonable to me.

Please consider this check, though:

Preconditions.checkArgument(
delegationModes.isEmpty() || delegationModes.contains(VENDED_CREDENTIALS),
"Unsupported access delegation mode: %s",
accessDelegationMode);

That is to say, it might be best to do this validation only after the final mode resolution 🤔

@adutra
Copy link
Contributor

adutra commented Feb 12, 2026

That is to say, it might be best to do this validation only after the final mode resolution 🤔

I think this check is fine; as you noted, we don't support anything else than VENDED_CREDENTIALS for the time being.

I see @iting0321's work in this PR as a way to proactively prepare Polaris for remote signing – without actually making remote signing possible.

EnumSet<AccessDelegationMode> requestedModes) {
if (requestedModes.isEmpty()) {
return requestedModes;
resolvedAccessDelegationMode = AccessDelegationMode.UNKNOWN;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this PR, but I've always thought we should rename this mode to NONE, wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think NONE is more precise, since it clearly communicates that no delegation mode was requested or selected. I’m happy to add it in a follow-up PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original idea behind AccessDelegationMode.UNKNOWN was for it to represent cases when the client declared something unparseable. Cf. AccessDelegationMode.fromProtocolValuesList()

If we want to definitely resolve to one specific AccessDelegationMode why don't we feed raw protocol strings to AccessDelegationModeResolver instead?

* Resolves the optimal {@link AccessDelegationMode} to use based on the client's requested modes
* and the catalog's capabilities.
*
* <p>The selection algorithm is:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the rest of the javadocs starting from this line are rather for the implementation, not for the interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your feedback. I just removed it.

return delegationModes;
// Parse the modes from the header - validation will happen after mode resolution
// in IcebergCatalogHandler.resolveAccessDelegationModes()
return AccessDelegationMode.fromProtocolValuesList(accessDelegationMode);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to reject REMOTE_SIGNING explicitly until it's available, otherwise we could introduce regressions, wdyt?

Suggested change
return AccessDelegationMode.fromProtocolValuesList(accessDelegationMode);
var accessDelegationMode = AccessDelegationMode.fromProtocolValuesList(accessDelegationMode);
// TODO remove when remote signing is implemented
Preconditions.checkArgument(
accessDelegationMode != REMOTE_SIGNING,
"Unsupported access delegation mode: %s",
accessDelegationMode);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think we should reject REMOTE_SIGNING explicitly for now.
Since the resolver may accept it but the feature isn’t implemented, clients could see unexpected behavior. Rejecting unsupported modes in the adapter keeps the flow clean and avoids failures later on.

"Credential subscoping is skipped for this realm, selecting REMOTE_SIGNING");
return REMOTE_SIGNING;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are still missing this check:

#3750 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I missed that earlier. I just pushed a commit adding the missing check and the corresponding unit test in AccessDelegationModeResolverTest.java
Thanks for pointing it out!

@iting0321 iting0321 force-pushed the improve-access-delegation-node-algo branch from 36b24df to 1bf8c18 Compare February 13, 2026 16:55
iting0321 added 4 commits February 14, 2026 00:57
- Remove resolveToSet() method and use single AccessDelegationMode
- Store resolved mode as field for later retrieval
- Fix SKIP_CREDENTIAL_SUBSCOPING_INDIRECTION config access
- Update log message to reflect realm-level scope for credential subscoping
- Turn AccessDelegationModeResolver into an interface + CDI bean
@iting0321 iting0321 force-pushed the improve-access-delegation-node-algo branch from 1bf8c18 to 9d0af9b Compare February 13, 2026 16:57
Copy link
Contributor

@dimas-b dimas-b left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this, @iting0321 !

I agree with @adutra that is makes sense to add a resolver for access delegation modes. This PR seems to be moving in the right direction.


private EnumSet<AccessDelegationMode> parseAccessDelegationModes(String accessDelegationMode) {
EnumSet<AccessDelegationMode> delegationModes =
// Parse the modes from the header - validation will happen after mode resolution
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: header - nothing in this case binds to any "headers"... The first part of this comment looks a bit confusing to me.

// TODO remove when remote signing is implemented
Preconditions.checkArgument(
delegationModes.isEmpty() || delegationModes.contains(VENDED_CREDENTIALS),
!modes.contains(REMOTE_SIGNING),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion might be too strict. When Polaris supports VENDED_CREDENTIALS but does not support REMOTE_SIGNING, I do not see any hard in the client requesting both, as long as the resolution algorithm selects the supported more (i.e. VENDED_CREDENTIALS in the current state of the code).

*
* @return The resolved access delegation mode, or null if not yet resolved
*/
protected AccessDelegationMode getResolvedAccessDelegationMode() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is not used. Is it worth adding?

EnumSet<AccessDelegationMode> requestedModes) {
if (requestedModes.isEmpty()) {
return requestedModes;
resolvedAccessDelegationMode = AccessDelegationMode.UNKNOWN;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original idea behind AccessDelegationMode.UNKNOWN was for it to represent cases when the client declared something unparseable. Cf. AccessDelegationMode.fromProtocolValuesList()

If we want to definitely resolve to one specific AccessDelegationMode why don't we feed raw protocol strings to AccessDelegationModeResolver instead?

Comment on lines +109 to +112
LOGGER.warn(
"Unknown access delegation mode combination: {}, defaulting to VENDED_CREDENTIALS",
requestedModes);
return VENDED_CREDENTIALS;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about defaulting to VENDED_CREDENTIALS. The client may have a legitimate reason not to ask for VENDED_CREDENTIALS (e.g. due to risk of credential exposure), so forcing sensitive data into the response may not be advisable.

If we want to do this, I'd propose to add a feature flag (with default "off").

// For external/federated catalogs, check if ALLOW_FEDERATED_CATALOGS_CREDENTIAL_VENDING is enabled.
boolean credentialSubscopingAuthorized =
!catalogEntity.isExternal()
|| configurationStore.getConfiguration(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use RealmConfig (cf. #3573)

}

CatalogEntity catalogEntity = getResolvedCatalogEntity();
resolvedAccessDelegationMode = accessDelegationModeResolver().resolve(requestedModes, catalogEntity);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the resolvedAccessDelegationMode field really necessary? It does not appear to be read at all (only written). Do we expect more that one "resolve" call per API request?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve access delegation mode selection algorithm

3 participants