Skip to content

Conversation

@richard-dennehy
Copy link
Contributor

Record security exception for resolved index expressions with CONCRETE_RESOURCE_UNAUTHORIZED results

@richard-dennehy richard-dennehy added >non-issue :Security/Security Security issues without another label Team:Security Meta label for security team labels Sep 29, 2025
@richard-dennehy richard-dennehy marked this pull request as ready for review September 29, 2025 16:15
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-security (Team:Security)

if (e instanceof IndexNotFoundException) {
listener.onFailure(e);
} else {
listener.onFailure(actionDenied(authentication, authzInfo, action, request, e));
Copy link
Contributor

@n1v0lg n1v0lg Oct 7, 2025

Choose a reason for hiding this comment

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

We need to set security exceptions in the case where the action actually succeeded (due to ignore_unavailable=true) instead of when it failed. When it failed, the recorded expressions are irrelevant since we will throw directly. So we need to take the request post resolution and see if:

  1. it has resolved expressions recorded
  2. if any of those have CONCRETE_RESOURCE_UNAUTHORIZED set

If the above is true, we need to set an actionDenied exception as you do below

@n1v0lg n1v0lg requested review from ywangd and removed request for ywangd October 7, 2025 16:04
@n1v0lg
Copy link
Contributor

n1v0lg commented Oct 7, 2025

Sorry, only got to this late!

@richard-dennehy could you update the branch, address this bit, and the req a review from @ywangd when it's ready? If my comment doesn't make sense I hope Yang can provide guidance. The Jira, for reference is ES-13005.

@ywangd
Copy link
Member

ywangd commented Oct 8, 2025

I am bit unsure about the need of this change. Is there a JIRA ticket for it that might provide some more context.

We record CONCRETE_RESOURCE_UNAUTHORIZED with null exception in IndexAbstractionResolver when the index is unauthorized. In CrossProjectIndexResolutionValidator, we check and create a security exception for them. So it already works as is today. Is the problem that this exception does not have sufficient information to be user-friendly and we want something like the exception created by AuthorizationService#actionDenied?

@richard-dennehy
Copy link
Contributor Author

Nikolaj mentioned the Jira ticket in his last comment: ES-13005

But there's not a great deal of context provided there, tbh

@ywangd
Copy link
Member

ywangd commented Oct 9, 2025

OK thanks for the clarification. I think I understand it better now. In CPS, we defer security exception for unauthorized concrete index so that when AuthorizationResultListener#onResponse is called, it is possible that the final validation after CPS fanout resolution still fails even when the result.isGranted() == true.

public void onResponse(T result) {
if (result.isGranted()) {

Therefore, in addition to call handleFailure in the else branch, we want to iterate ResolvedIndexExpressions to check for any local expressions recorded with CONCRETE_RESOURCE_UNAUTHORIZED and set an exception, generated by calling accessDenied, for them. These exceptions are then used by CrossProjectIndexResolutionValidator#securityException for error reporting. I hope this makes sense.

PS: There is a question about how auditing should behave in this case. But we can defer it for this work.

Comment on lines 3729 to 3732
equalTo(
"action [indices:data/read/search] is unauthorized for user [user] with effective roles [partial-access-role], "
+ "this action is granted by the index privileges [read,all]"
)
Copy link
Member

Choose a reason for hiding this comment

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

We should assert the index name is included in the error message

Copy link
Member

@ywangd ywangd left a comment

Choose a reason for hiding this comment

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

I have a few comments. Could you also raise a linked serverless PR to update CrossProjectSearchIT with error message assertions for 403 cases in the section of "missing and unauthorized concrete resources -- strict indices options -- errors"?

: "If the local resolution result is SUCCESS, exception must be null";
Objects.requireNonNull(exception);

this.exception = exception;
Copy link
Member

Choose a reason for hiding this comment

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

We should also assert this.exception is null before set it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is causing the CPS Rest IT to fail - I need to figure out why that's happening

Copy link
Member

Choose a reason for hiding this comment

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

Ah OK. I think this is the same double security filtering issue which required the method setResolvedIndexExpressionsIfUnset.

We can do something similar, i.e. set the exception when this.exception is null. If this.exception is already set, assert the two exceptions have the same type and error message (stacktraces are not necessarily the same).

when(crossProjectModeDecider.crossProjectEnabled()).thenReturn(true);
when(crossProjectModeDecider.resolvesCrossProject(any())).thenReturn(true);

mockMetadataWithIndex("available-index");
Copy link
Member

Choose a reason for hiding this comment

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

The deciding factor here is that the user has no access to the index regardless whether it exists or not. So I'd suggest:

  1. rename available-index and not-available-index to accessible-index and not-accessible-index.
  2. Randomly also add not-accessible-index to the project metadata. The behaviour should not change whether this index exists or not
  3. random between indices request between accessible-index,not-accessible-index and just not-accessible-index. The behaviour should be the same.

@ywangd
Copy link
Member

ywangd commented Oct 20, 2025

Btw, updating CrossProjectSearchIT as suggested above will require updating CrossProjectIndexResolutionValidator as well. I would be OK if you prefer to have both changes as a follow-up.

@richard-dennehy richard-dennehy requested review from a team as code owners October 24, 2025 13:27
@richard-dennehy richard-dennehy force-pushed the resolved-index-expression-record-exceptions branch from 3753285 to b81d263 Compare October 24, 2025 13:31
@elasticsearchmachine elasticsearchmachine added the serverless-linked Added by automation, don't add manually label Oct 27, 2025
Copy link
Member

@ywangd ywangd left a comment

Choose a reason for hiding this comment

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

Only a small comment. Could you please make sure CI is green? The serverless side PR is having formatting issue.

Comment on lines 129 to 131
} else {
this.exception = exception;
}
Copy link
Member

Choose a reason for hiding this comment

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

This violates what the method name says, i.e. set if unset, since this can happen when it is already set but the newer exception has identical message.

Copy link
Member

@ywangd ywangd left a comment

Choose a reason for hiding this comment

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

LGTM

@richard-dennehy richard-dennehy merged commit 7edc047 into elastic:main Oct 29, 2025
40 checks passed
@richard-dennehy richard-dennehy deleted the resolved-index-expression-record-exceptions branch October 29, 2025 09:02
chrisparrinello pushed a commit to chrisparrinello/elasticsearch that referenced this pull request Nov 3, 2025
)

* record security exceptions in resolved index expressions

* [CI] Auto commit changes from spotless

* override equality in LocalExpressions

* [CI] Auto commit changes from spotless

* record exception for unresolved index when ignore_unavailable is true

* clean up

* spotless

* enable CPS mode in test

* include index name in exception message

* [CI] Auto commit changes from spotless

* address review comments

* [CI] Auto commit changes from spotless

* assert exception is not set twice

* use recorded exception in CrossProjectIndexResolutionValidator

* [CI] Auto commit changes from spotless

* actually only set exception if unset

---------

Co-authored-by: elasticsearchmachine <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

>non-issue :Security/Security Security issues without another label serverless-linked Added by automation, don't add manually Team:Security Meta label for security team v9.3.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants