Skip to content

Failure Store Access Authorization #123986

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

Merged

Conversation

n1v0lg
Copy link
Contributor

@n1v0lg n1v0lg commented Mar 4, 2025

This PR implements authorization logic for failure store access. It builds on #122715.

Access to the failure store is granted by two privileges: read_failure_store and manage_failure_store. Either of these privileges lets a user access a failure store via the ::failures selector, as well as access its backing failure indices. read_failure_store grants read access (for example to search documents in a failure store), manage_failure_store grants access to write operations, such as rollover. Users with only read or manage on a data stream do not get failure store access. Vice versa, users with read_failure_store and manage_failure_store do not get access to regular data in a data stream.

The PR implements this by making authorization logic selector-aware. It involves two main changes:

  1. Index permission groups now compare the selector under which an index resource is accessed to the selector associated with the group.
  2. The AuthorizedIndices interface likewise uses selectors to decide which indices to treat as authorized. This part of the change requires a sizable refactor and changes to the interface.

The high-level behavior for selector-aware search is as follows:

For a user with read_failure_store over data stream logs:

  • POST /logs::failures/_search returns the documents in the failure store.
  • POST /logs/_search returns a 403.
  • POST /logs/_search?ignore_unavailable=true and POST /*/_search return an empty result.

Similarly, for a user with read over data stream logs:

  • POST /logs::failures/_search returns a 403.
  • POST /logs/_search returns documents in the data stream.
  • POST /logs::failures/_search?ignore_unavailable=true and POST /*::failures/_search return an empty result.

A user with both read and read_failure_store over data stream logs gets access to both POST /logs::failures/_search and POST /logs/_search.

The index privilege all automatically grants access to both data and the failures store, as well as all hypothetical future selectors.

Resolves: ES-10873

@n1v0lg n1v0lg changed the title Failure store access - selector-aware authorization Failure Store Access Authorization Mar 18, 2025
@@ -0,0 +1,5 @@
pr: 123986
summary: Failure Store Access Authorization
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Eh, actually I'll remove this and turn this into a non-issue. There are follow ups to complete this and besides the failure store itself isn't available yet so it's not really an enhancement.

Copy link
Contributor

@slobodanadamovic slobodanadamovic left a comment

Choose a reason for hiding this comment

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

LGTM This turned out pretty neat! Nice job on integration tests. 👍

I've left couple of optional suggestions and a few non-blocking questions. Two main questions that I think we should discuss (and optionally address in the followup) are:

  1. Should read_failure_store include the same automaton as read? I think we can drop the indices:admin/resolve/cluster since CCS/CCR are not supported in the MVP.
  2. Do we want to allow that having read_failure_store over .fs* grants read access to failure store backing indices (just like read)? In theory we could support it.

assertThat(isIndexVisible("index1", "data"), is(true));
assertThat(isIndexVisible("index1", "failures"), is(false)); // *
// * Indices don't have failure components so the failure component is not visible

assertThat(isIndexVisible("data-stream1", null), is(true));
assertThat(isIndexVisible("data-stream1", "*"), is(true));
Copy link
Contributor

@slobodanadamovic slobodanadamovic Mar 17, 2025

Choose a reason for hiding this comment

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

This seems to be leftover from the #121900 PR, right?
Looking closer, these tests seem to pass with any string value for the selector, except the failures.
Not something we should do in this PR, but I wonder if we should validate the selector inside the isIndexVisible method and only accept null, failures or data?

Copy link
Contributor Author

@n1v0lg n1v0lg Mar 20, 2025

Choose a reason for hiding this comment

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

Yup, I'll note this down to look into in a follow up.

@@ -500,14 +550,16 @@ public IndicesAccessControl authorize(
int totalResourceCount = 0;
Map<String, IndexAbstraction> lookup = metadata.getIndicesLookup();
for (String indexOrAlias : requestedIndicesOrAliases) {
// Remove any selectors from abstraction name. Discard them for this check as we do not have access control for them (yet)
// Remove any selectors from abstraction name. Access control is based on the `selector` field of the IndexResource
Tuple<String, String> expressionAndSelector = IndexNameExpressionResolver.splitSelectorExpression(indexOrAlias);
indexOrAlias = expressionAndSelector.v1();
Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good to me!

assertThat(authorizedIndices.check(SecuritySystemIndices.SECURITY_MAIN_ALIAS, IndexComponentSelector.DATA), is(false));
}

public void testDataStreamsAreNotIncludedInAuthorizedIndicesWithFailuresSelector() {
Copy link
Contributor

@slobodanadamovic slobodanadamovic Mar 18, 2025

Choose a reason for hiding this comment

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

Some more unit tests we could consider adding (can be done in a followup PR):

  • having read_failure_store over .* and allow_restricted_indices=true does not grant access to system indices
  • having read_failure_store over .fs* indices does not grant read access to failure store indices (depends on the decision if we actually want to allow this - see my other comment)
  • having read_failure_store over * grants read access to datastreams with their failure store backing indices
  • having manage_failure_store over *, .fs* or datastream name does not grant access for TransportSearchAction.TYPE.name() but it should for TransportDeleteIndexAction.TYPE.name()
  • in general, more test coverage for resolveAuthorizedIndicesFromRole and e.g. TransportDeleteIndexAction with roles that grant manage_failure_store and manage

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Absolutely. Adding these in a follow up.

entry("cross_cluster_replication_internal", CROSS_CLUSTER_REPLICATION_INTERNAL),
DataStream.isFailureStoreFeatureFlagEnabled() ? entry("read_failure_store", READ_FAILURE_STORE) : null
).filter(Objects::nonNull).collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue))
private static final Map<String, IndexPrivilege> VALUES = combineSortedInOrder(
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the explanation!

);
}

// TODO remove me (this has >700 usages in tests which would make for a horrible diff; will remove this once the main PR is merged)
Copy link
Contributor

Choose a reason for hiding this comment

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

+1

@n1v0lg n1v0lg added the auto-merge-without-approval Automatically merge pull request when CI checks pass (NB doesn't wait for reviews!) label Mar 20, 2025
@elasticsearchmachine elasticsearchmachine merged commit c58ac45 into elastic:main Mar 20, 2025
22 checks passed
@n1v0lg n1v0lg deleted the read-failure-store-privilege-authz branch March 20, 2025 09:10
@gmarouli
Copy link
Contributor

🥳

@n1v0lg
Copy link
Contributor Author

n1v0lg commented Mar 20, 2025

💚 All backports created successfully

Status Branch Result
8.x

Questions ?

Please refer to the Backport tool documentation

elasticsearchmachine pushed a commit that referenced this pull request Mar 20, 2025
* Resolve

* Fix more tests

* Fix bwc test

* Undo spacing diff

* More whitespace stuff

* Another

* More
afoucret pushed a commit to afoucret/elasticsearch that referenced this pull request Mar 21, 2025
This PR implements authorization logic for failure store access. It
builds on elastic#122715.

Access to the failure store is granted by two privileges:
`read_failure_store` and `manage_failure_store`. Either of these
privileges lets a user access a failure store via the `::failures`
selector, as well as access its backing failure indices. 
`read_failure_store` grants read access (for example to search documents
in a failure store), `manage_failure_store` grants access to write
operations, such as rollover. Users with only `read` or `manage` on a
data stream do not get failure store access. Vice versa, users with
`read_failure_store` and `manage_failure_store` do not get access to
regular data in a data stream. 

The PR implements this by making authorization logic selector-aware. It
involves two main changes:

1. Index permission groups now compare the selector under which an index resource is accessed to the selector associated with the group.
2. The `AuthorizedIndices` interface likewise uses selectors to decide which indices to treat as authorized. This part of the change requires a sizable refactor and changes to the interface. 

The high-level behavior for selector-aware search is as follows:

For a user with `read_failure_store` over data stream `logs`:

- `POST /logs::failures/_search` returns the documents in the failure store.
- `POST /logs/_search` returns a 403.
- `POST /logs/_search?ignore_unavailable=true` and `POST /*/_search` return an empty result.

Similarly, for a user with `read` over data stream `logs`:

- `POST /logs::failures/_search` returns a 403.
- `POST /logs/_search` returns documents in the data stream.
- `POST /logs::failures/_search?ignore_unavailable=true` and `POST /*::failures/_search` return an empty result.

A user with both `read` and `read_failure_store` over data stream `logs`
gets access to both `POST /logs::failures/_search` and `POST
/logs/_search`.

The index privilege `all` automatically grants access to both data and
the failures store, as well as all hypothetical future selectors. 

Resolves: ES-10873
smalyshev pushed a commit to smalyshev/elasticsearch that referenced this pull request Mar 21, 2025
This PR implements authorization logic for failure store access. It
builds on elastic#122715.

Access to the failure store is granted by two privileges:
`read_failure_store` and `manage_failure_store`. Either of these
privileges lets a user access a failure store via the `::failures`
selector, as well as access its backing failure indices. 
`read_failure_store` grants read access (for example to search documents
in a failure store), `manage_failure_store` grants access to write
operations, such as rollover. Users with only `read` or `manage` on a
data stream do not get failure store access. Vice versa, users with
`read_failure_store` and `manage_failure_store` do not get access to
regular data in a data stream. 

The PR implements this by making authorization logic selector-aware. It
involves two main changes:

1. Index permission groups now compare the selector under which an index resource is accessed to the selector associated with the group.
2. The `AuthorizedIndices` interface likewise uses selectors to decide which indices to treat as authorized. This part of the change requires a sizable refactor and changes to the interface. 

The high-level behavior for selector-aware search is as follows:

For a user with `read_failure_store` over data stream `logs`:

- `POST /logs::failures/_search` returns the documents in the failure store.
- `POST /logs/_search` returns a 403.
- `POST /logs/_search?ignore_unavailable=true` and `POST /*/_search` return an empty result.

Similarly, for a user with `read` over data stream `logs`:

- `POST /logs::failures/_search` returns a 403.
- `POST /logs/_search` returns documents in the data stream.
- `POST /logs::failures/_search?ignore_unavailable=true` and `POST /*::failures/_search` return an empty result.

A user with both `read` and `read_failure_store` over data stream `logs`
gets access to both `POST /logs::failures/_search` and `POST
/logs/_search`.

The index privilege `all` automatically grants access to both data and
the failures store, as well as all hypothetical future selectors. 

Resolves: ES-10873
omricohenn pushed a commit to omricohenn/elasticsearch that referenced this pull request Mar 28, 2025
This PR implements authorization logic for failure store access. It
builds on elastic#122715.

Access to the failure store is granted by two privileges:
`read_failure_store` and `manage_failure_store`. Either of these
privileges lets a user access a failure store via the `::failures`
selector, as well as access its backing failure indices. 
`read_failure_store` grants read access (for example to search documents
in a failure store), `manage_failure_store` grants access to write
operations, such as rollover. Users with only `read` or `manage` on a
data stream do not get failure store access. Vice versa, users with
`read_failure_store` and `manage_failure_store` do not get access to
regular data in a data stream. 

The PR implements this by making authorization logic selector-aware. It
involves two main changes:

1. Index permission groups now compare the selector under which an index resource is accessed to the selector associated with the group.
2. The `AuthorizedIndices` interface likewise uses selectors to decide which indices to treat as authorized. This part of the change requires a sizable refactor and changes to the interface. 

The high-level behavior for selector-aware search is as follows:

For a user with `read_failure_store` over data stream `logs`:

- `POST /logs::failures/_search` returns the documents in the failure store.
- `POST /logs/_search` returns a 403.
- `POST /logs/_search?ignore_unavailable=true` and `POST /*/_search` return an empty result.

Similarly, for a user with `read` over data stream `logs`:

- `POST /logs::failures/_search` returns a 403.
- `POST /logs/_search` returns documents in the data stream.
- `POST /logs::failures/_search?ignore_unavailable=true` and `POST /*::failures/_search` return an empty result.

A user with both `read` and `read_failure_store` over data stream `logs`
gets access to both `POST /logs::failures/_search` and `POST
/logs/_search`.

The index privilege `all` automatically grants access to both data and
the failures store, as well as all hypothetical future selectors. 

Resolves: ES-10873
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auto-merge-without-approval Automatically merge pull request when CI checks pass (NB doesn't wait for reviews!) >non-issue :Security/Authorization Roles, Privileges, DLS/FLS, RBAC/ABAC serverless-linked Added by automation, don't add manually Team:Security Meta label for security team v9.1.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants