Fix rollover authorization checks - #157955
Conversation
Authorization for a rollover now consists of 3 parts: - `indices:admin/rollover` on the target alias (unchanged first pass). - `indices:admin/create` on the resolved new index name - the new pass. The resolved name comes from the shared MetadataRolloverService.resolveRolloverIndexName, computed once and reused by both passes. - `indices:admin/aliases` on the target plus body aliases plus the new index name
|
Hi @slobodanadamovic, I've created a changelog YAML for you. |
🔍 Preview links for changed docs⏳ Building and deploying preview... View progress This comment will be updated with preview links when the build is complete. |
ℹ️ Important: Docs version tagging👋 Thanks for updating the docs! Just a friendly reminder that our docs are now cumulative. This means all 9.x versions are documented on the same page and published off of the main branch, instead of creating separate pages for each minor version. We use applies_to tags to mark version-specific features and changes. Expand for a quick overviewWhen to use applies_to tags:✅ At the page level to indicate which products/deployments the content applies to (mandatory) What NOT to do:❌ Don't remove or replace information that applies to an older version 🤔 Need help?
|
| private final String[] checkedNames; | ||
|
|
||
| DataStreamAuthorizationRequest(ModifyDataStreamsAction.Request delegate, String[] dataStreamNames) { | ||
| SecondaryAuthorizationRequest(IndicesRequest delegate, String[] checkedNames) { |
There was a problem hiding this comment.
I'm open to any naming suggestions here. The SecondaryAuthorizationRequest's intention is to be a wrapper for any sub-sequent authorization calls. It's usage is primarily for auditing the correct names (indices, aliases or data streams)
There was a problem hiding this comment.
Pull request overview
Adds multi-stage rollover authorization to prevent unauthorized index creation and alias attachment.
Changes:
- Adds create-index and alias authorization passes.
- Adds unit and REST integration coverage.
- Exposes rollover index-name resolution for authorization.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
AuthorizationService.java |
Implements secondary authorization checks. |
AuthorizationServiceTests.java |
Tests rollover authorization and auditing. |
RolloverAliasSecurityRestIT.java |
Adds end-to-end security tests. |
MetadataRolloverService.java |
Exposes date-math name resolution. |
157955.yaml |
Adds the changelog entry. |
Suppressed comments (1)
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java:840
- This resolution is not guaranteed to produce the name that is eventually created.
resolveRolloverIndexNamereads the current clock, whileTransportRolloverActionresolves the expression again during the pre-check and cluster-state task (TransportRolloverAction.java:216and:555). A valid expression with second- or millisecond-level formatting can therefore be authorized as one exact index and later create another. Bind the authorized concrete name (or timestamp) to execution, or authorize the final name.
// Authorize the same concrete name MetadataRolloverService will create.
rolloverNewIndexName = MetadataRolloverService.resolveRolloverIndexName(rolloverRequest.getNewIndexName());
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if (request instanceof RolloverRequest rolloverRequest) { | ||
| aliases = rolloverRequest.getCreateIndexRequest().aliases(); | ||
| if (rolloverRequest.getNewIndexName() != null) { | ||
| // Authorize the same concrete name MetadataRolloverService will create. | ||
| rolloverNewIndexName = MetadataRolloverService.resolveRolloverIndexName(rolloverRequest.getNewIndexName()); |
| // Substitute a request whose indices() are the names this pass checks, so audit events and the denial | ||
| // message name the offending alias or index instead of the original request's rollover target. | ||
| final RequestInfo aliasesRequestInfo = new RequestInfo( | ||
| requestInfo.getAuthentication(), | ||
| request, | ||
| new SecondaryAuthorizationRequest((IndicesRequest) requestInfo.getRequest(), checkedNames.toArray(String[]::new)), |
| } else if (request instanceof CreateDataStreamAction.Request | ||
| || request instanceof MigrateToDataStreamAction.Request | ||
| || request instanceof PastTimeSeriesIndexCreationAction.Request) { | ||
| aliases = Set.of(); | ||
| } else { | ||
| // Casting to CreateIndexRequest is safe only because it's the last possible option; always keep it last! | ||
| aliases = ((CreateIndexRequest) request).aliases(); | ||
| } |
Authorization for a rollover now consists of 3 parts:
indices:admin/rolloveron the target alias (unchanged first pass)indices:admin/createon the resolved new index name - the new second passindices:admin/aliaseson the target plus body aliases plus the new index name - the new third pass