Skip to content

Commit

Permalink
[8.x] [UA] Remove `delete` from `.tasks` permissi…
Browse files Browse the repository at this point in the history
…on check (elastic#203379) (elastic#203417)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[UA] Remove `delete` from `.tasks` permission
check (elastic#203379)](elastic#203379)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Jean-Louis
Leysens","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-12-09T13:22:07Z","message":"[UA]
Remove `delete` from `.tasks` permission check (elastic#203379)\n\n##
Summary\r\n\r\n* Removes `delete` permission check on `.tasks`\r\n* Task
doc deletion best effort\r\n* Creates a `warning` level log if deletion
from `.tasks` fails like:\r\n```\r\n[2024-12-09T10:50:28.398+01:00][WARN
][plugins.upgradeAssistant.reindex_worker] ResponseError:
security_exception\r\nlog.ts:66\r\n\tRoot
causes:\r\nlog.ts:66\r\n\t\tsecurity_exception: action
[indices:data/write/bulk[s]] is unauthorized for API key id [___] of
user [elastic] on restricted indices [.tasks], this action is granted by
the index privileges
[create_doc,create,delete,index,write,all]\r\n```\r\n\r\n## How to
test\r\n\r\n1. Follow
[these\r\nsteps](https://github.com/elastic/kibana-team/issues/1249#issuecomment-2514462816),\r\nbut
instead of creating a data stream create an index in 7.x\r\n2. Start ES
on v8.x\r\n3. Checkout Kibana `8.x` locally and apply
[the\r\ndiff](https://patch-diff.githubusercontent.com/raw/elastic/kibana/pull/203379.diff)\r\nfrom
this branch\r\n4. Start Kibana\r\n5. Log in as `elastic` or some other
admin/superuser\r\n6. Go to UA and reindex the index you
created\r\n\r\n## Resources\r\n\r\n### Outdated 7.x
guidance\r\n\r\n\r\nhttps://www.elastic.co/guide/en/elasticsearch/reference/7.17/docs-update-by-query.html#docs-update-by-query-task-api\r\n\r\n>
When you are done with a task, you should delete the task document
so\r\nElasticsearch can reclaim the
space.","sha":"1f09537e15983bb384b461c51a0ad501dc77ff52","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Core","release_note:skip","Feature:Upgrade
Assistant","v9.0.0","backport:version","v8.18.0","v8.17.1"],"title":"[UA]
Remove `delete` from `.tasks` permission
check","number":203379,"url":"https://github.com/elastic/kibana/pull/203379","mergeCommit":{"message":"[UA]
Remove `delete` from `.tasks` permission check (elastic#203379)\n\n##
Summary\r\n\r\n* Removes `delete` permission check on `.tasks`\r\n* Task
doc deletion best effort\r\n* Creates a `warning` level log if deletion
from `.tasks` fails like:\r\n```\r\n[2024-12-09T10:50:28.398+01:00][WARN
][plugins.upgradeAssistant.reindex_worker] ResponseError:
security_exception\r\nlog.ts:66\r\n\tRoot
causes:\r\nlog.ts:66\r\n\t\tsecurity_exception: action
[indices:data/write/bulk[s]] is unauthorized for API key id [___] of
user [elastic] on restricted indices [.tasks], this action is granted by
the index privileges
[create_doc,create,delete,index,write,all]\r\n```\r\n\r\n## How to
test\r\n\r\n1. Follow
[these\r\nsteps](https://github.com/elastic/kibana-team/issues/1249#issuecomment-2514462816),\r\nbut
instead of creating a data stream create an index in 7.x\r\n2. Start ES
on v8.x\r\n3. Checkout Kibana `8.x` locally and apply
[the\r\ndiff](https://patch-diff.githubusercontent.com/raw/elastic/kibana/pull/203379.diff)\r\nfrom
this branch\r\n4. Start Kibana\r\n5. Log in as `elastic` or some other
admin/superuser\r\n6. Go to UA and reindex the index you
created\r\n\r\n## Resources\r\n\r\n### Outdated 7.x
guidance\r\n\r\n\r\nhttps://www.elastic.co/guide/en/elasticsearch/reference/7.17/docs-update-by-query.html#docs-update-by-query-task-api\r\n\r\n>
When you are done with a task, you should delete the task document
so\r\nElasticsearch can reclaim the
space.","sha":"1f09537e15983bb384b461c51a0ad501dc77ff52"}},"sourceBranch":"main","suggestedTargetBranches":["8.x","8.17"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/203379","number":203379,"mergeCommit":{"message":"[UA]
Remove `delete` from `.tasks` permission check (elastic#203379)\n\n##
Summary\r\n\r\n* Removes `delete` permission check on `.tasks`\r\n* Task
doc deletion best effort\r\n* Creates a `warning` level log if deletion
from `.tasks` fails like:\r\n```\r\n[2024-12-09T10:50:28.398+01:00][WARN
][plugins.upgradeAssistant.reindex_worker] ResponseError:
security_exception\r\nlog.ts:66\r\n\tRoot
causes:\r\nlog.ts:66\r\n\t\tsecurity_exception: action
[indices:data/write/bulk[s]] is unauthorized for API key id [___] of
user [elastic] on restricted indices [.tasks], this action is granted by
the index privileges
[create_doc,create,delete,index,write,all]\r\n```\r\n\r\n## How to
test\r\n\r\n1. Follow
[these\r\nsteps](https://github.com/elastic/kibana-team/issues/1249#issuecomment-2514462816),\r\nbut
instead of creating a data stream create an index in 7.x\r\n2. Start ES
on v8.x\r\n3. Checkout Kibana `8.x` locally and apply
[the\r\ndiff](https://patch-diff.githubusercontent.com/raw/elastic/kibana/pull/203379.diff)\r\nfrom
this branch\r\n4. Start Kibana\r\n5. Log in as `elastic` or some other
admin/superuser\r\n6. Go to UA and reindex the index you
created\r\n\r\n## Resources\r\n\r\n### Outdated 7.x
guidance\r\n\r\n\r\nhttps://www.elastic.co/guide/en/elasticsearch/reference/7.17/docs-update-by-query.html#docs-update-by-query-task-api\r\n\r\n>
When you are done with a task, you should delete the task document
so\r\nElasticsearch can reclaim the
space.","sha":"1f09537e15983bb384b461c51a0ad501dc77ff52"}},{"branch":"8.x","label":"v8.18.0","branchLabelMappingKey":"^v8.18.0$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.17","label":"v8.17.1","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Jean-Louis Leysens <[email protected]>
  • Loading branch information
kibanamachine and jloleysens authored Dec 9, 2024
1 parent 2e79baa commit 2131153
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { esIndicesStateCheck } from '../es_indices_state_check';
import { versionService } from '../version';

import { ReindexService, reindexServiceFactory } from './reindex_service';
import { error } from './error';

const asApiResponse = <T>(body: T): TransportResult<T> =>
({
Expand Down Expand Up @@ -111,7 +112,7 @@ describe('reindexService', () => {
},
{
names: ['.tasks'],
privileges: ['read', 'delete'],
privileges: ['read'],
},
],
},
Expand Down Expand Up @@ -141,7 +142,7 @@ describe('reindexService', () => {
},
{
names: ['.tasks'],
privileges: ['read', 'delete'],
privileges: ['read'],
},
],
},
Expand Down Expand Up @@ -611,11 +612,11 @@ describe('reindexService', () => {
});
});

it('fails if docs created is less than count in source index', async () => {
it('does not throw if task doc deletion returns a bad result', async () => {
clusterClient.asCurrentUser.tasks.get.mockResponseOnce({
completed: true,
// @ts-expect-error not full interface
task: { status: { created: 95, total: 95 } },
task: { status: { created: 100, total: 100 } },
});

clusterClient.asCurrentUser.count.mockResponseOnce(
Expand All @@ -625,11 +626,51 @@ describe('reindexService', () => {
}
);

clusterClient.asCurrentUser.delete.mockResponseOnce({
// @ts-expect-error not known result
result: '!?',
});

const updatedOp = await service.processNextStep(reindexOp);
expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.reindexStarted);
expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed);
expect(updatedOp.attributes.errorMessage).not.toBeNull();
expect(log.error).toHaveBeenCalledWith(expect.any(String));
expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.reindexCompleted);
expect(updatedOp.attributes.reindexTaskPercComplete).toEqual(1);
expect(clusterClient.asCurrentUser.delete).toHaveBeenCalledWith({
index: '.tasks',
id: 'xyz',
});
expect(log.warn).toHaveBeenCalledTimes(1);
expect(log.warn).toHaveBeenCalledWith(
error.reindexTaskCannotBeDeleted(
`Could not delete reindexing task xyz, got response "!?"`
)
);
});

it('does not throw if task doc deletion throws', async () => {
clusterClient.asCurrentUser.tasks.get.mockResponseOnce({
completed: true,
// @ts-expect-error not full interface
task: { status: { created: 100, total: 100 } },
});

clusterClient.asCurrentUser.count.mockResponseOnce(
// @ts-expect-error not full interface
{
count: 100,
}
);

clusterClient.asCurrentUser.delete.mockRejectedValue(new Error('FAILED!'));

const updatedOp = await service.processNextStep(reindexOp);
expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.reindexCompleted);
expect(updatedOp.attributes.reindexTaskPercComplete).toEqual(1);
expect(clusterClient.asCurrentUser.delete).toHaveBeenCalledWith({
index: '.tasks',
id: 'xyz',
});
expect(log.warn).toHaveBeenCalledTimes(1);
expect(log.warn).toHaveBeenCalledWith(new Error('FAILED!'));
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,14 +308,21 @@ export const reindexServiceFactory = (
});
}

// Delete the task from ES .tasks index
const deleteTaskResp = await esClient.delete({
index: '.tasks',
id: taskId,
});

if (deleteTaskResp.result !== 'deleted') {
throw error.reindexTaskCannotBeDeleted(`Could not delete reindexing task ${taskId}`);
try {
// Delete the task from ES .tasks index
const deleteTaskResp = await esClient.delete({
index: '.tasks',
id: taskId,
});
if (deleteTaskResp.result !== 'deleted') {
log.warn(
error.reindexTaskCannotBeDeleted(
`Could not delete reindexing task ${taskId}, got response "${deleteTaskResp.result}"`
)
);
}
} catch (e) {
log.warn(e);
}

return reindexOp;
Expand Down Expand Up @@ -396,24 +403,21 @@ export const reindexServiceFactory = (
names.push(sourceName);
}

// Otherwise, query for required privileges for this index.
const body = {
cluster: ['manage'],
index: [
{
names,
allow_restricted_indices: true,
privileges: ['all'],
},
{
names: ['.tasks'],
privileges: ['read', 'delete'],
},
],
} as any;

const resp = await esClient.security.hasPrivileges({
body,
body: {
cluster: ['manage'],
index: [
{
names,
allow_restricted_indices: true,
privileges: ['all'],
},
{
names: ['.tasks'],
privileges: ['read'],
},
],
},
});

return resp.has_all_requested;
Expand Down

0 comments on commit 2131153

Please sign in to comment.