Skip to content

Tasks: Only require task permissions #35667

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
merged 5 commits into from
Nov 28, 2018

Conversation

nik9000
Copy link
Member

@nik9000 nik9000 commented Nov 16, 2018

Right now using the GET /_tasks/<taskid> API and causing a task to opt
in to saving its result after being completed requires permissions on
the .tasks index. When we built this we thought that that was fine,
but we've since moved towards not leaking details like "persisting task
results after the task is completed is done by saving them into an index
named .tasks." A more modern way of doing this would be to save the
tasks into the index "under the hood" and to have APIs to manage the
saved tasks. This is the first step down that road: it drops the
requirement to have permissions to interact with the .tasks index when
fetching task statuses and when persisting statuses beyond the lifetime
of the task.

In particular, this moves the concept of the "origin" of an action into
a more prominent place in the Elasticsearch server. The origin of an
action is ignored by the server, but the security plugin uses the origin
to make requests on behalf of a user in such a way that the user need
not have permissions to perform these actions. It can be made to be
fairly precise. More specifically, we can create an internal user just
for the tasks API that just has permission to interact with the .tasks
index. This change doesn't do that, instead, it uses the ubiquitus
"xpack" user which has most permissions because it is simpler. Adding
the tasks user is something I'd like to get to in a follow up change.

Instead, the majority of this change is about moving the "origin"
concept from the security portion of x-pack into the server. This should
allow any code to use the origin. To keep the change managable I've also
opted to deprecate rather than remove the "origin" helpers in the
security code. Removing them is almost entirely mechanical and I'd like
to that in a follow up as well.

Relates to #35573

Right now using the `GET /_tasks/<taskid>` API and causing a task to opt
in to saving its result after being completed requires permissions on
the `.tasks` index. When we built this we thought that that was fine,
but we've since moved towards not leaking details like "persisting task
results after the task is completed is done by saving them into an index
named `.tasks`." A more modern way of doing this would be to save the
tasks into the index "under the hood" and to have APIs to manage the
saved tasks. This is the first step down that road: it drops the
requirement to have permissions to interact with the `.tasks` index when
fetching task statuses and when persisting statuses beyond the lifetime
of the task.

In particular, this moves the concept of the "origin" of an action into
a more prominent place in the Elasticsearch server. The origin of an
action is ignored by the server, but the security plugin uses the origin
to make requests on behalf of a user in such a way that the user need
not have permissions to perform these actions. It *can* be made to be
fairly precise. More specifically, we can create an internal user just
for the tasks API that just has permission to interact with the `.tasks`
index. This change doesn't do that, instead, it uses the ubiquitus
"xpack" user which has most permissions because it is simpler. Adding
the tasks user is something I'd like to get to in a follow up change.

Instead, the majority of this change is about moving the "origin"
concept from the security portion of x-pack into the server. This should
allow any code to use the origin. To keep the change managable I've also
opted to deprecate rather than remove the "origin" helpers in the
security code. Removing them is almost entirely mechanical and I'd like
to that in a follow up as well.

Relates to elastic#35573
@nik9000 nik9000 added >enhancement :Distributed Coordination/Task Management Issues for anything around the Tasks API - both persistent and node level. v7.0.0 v6.6.0 labels Nov 16, 2018
@nik9000 nik9000 requested review from imotov and jaymode November 16, 2018 20:56
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-distributed

@@ -316,3 +314,51 @@ setup:
index: dest
dest:
index: source

---
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the first thing I made in this PR. It is kind of a shame I couldn't drag it to the front because it exposes the problem perfectly

@@ -90,60 +91,45 @@ public void testShouldSetUser() {
}

public void testSwitchAndExecuteXpackSecurityUser() throws Exception {
SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
Copy link
Member Author

Choose a reason for hiding this comment

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

I yanked all of this into a common test case method below now that I have an extra case. I made an extra case for the tasks API because I had anticipated making the tasks user but now I've decided it is a good thing for a follow up.

@@ -279,18 +277,6 @@ public void testKibanaSystemRole() {
assertThat(kibanaRole.indices().allowedIndicesMatcher(MultiSearchAction.NAME).test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(READ_CROSS_CLUSTER_NAME).test(index), is(false));

// Tasks index
final String taskIndex = org.elasticsearch.tasks.TaskResultsService.TASK_INDEX;
Copy link
Member Author

Choose a reason for hiding this comment

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

These shouldn't be required any more.

@@ -42,32 +42,6 @@
import static org.mockito.Mockito.when;

public class ClientHelperTests extends ESTestCase {

public void testStashContext() {
Copy link
Member Author

Choose a reason for hiding this comment

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

Moved to ThreadContextTests.

@@ -118,9 +118,7 @@
RoleDescriptor.IndicesPrivileges.builder()
.indices(".monitoring-*").privileges("read", "read_cross_cluster").build(),
RoleDescriptor.IndicesPrivileges.builder()
.indices(".management-beats").privileges("create_index", "read", "write").build(),
RoleDescriptor.IndicesPrivileges.builder()
Copy link
Member Author

Choose a reason for hiding this comment

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

Shouldn't be needed any more.

@@ -725,12 +724,6 @@ public void testTasksWaitForAllTask() throws Exception {
}

public void testTaskStoringSuccesfulResult() throws Exception {
// Randomly create an empty index to make sure the type is created automatically
Copy link
Member Author

Choose a reason for hiding this comment

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

I removed this because it made writing the assertions about setting the origin very difficult and it isn't really a supported configuration.

@@ -743,23 +736,20 @@ public void testTaskStoringSuccesfulResult() throws Exception {
TaskInfo taskInfo = events.get(0);
TaskId taskId = taskInfo.getTaskId();

GetResponse resultDoc = client()
Copy link
Member Author

Choose a reason for hiding this comment

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

I replaced these with the tasks API calls because it gives me a good opportunity to assert that they find the documents while setting the origin.

@@ -70,7 +71,7 @@

@Inject
public TaskResultsService(Client client, ClusterService clusterService) {
this.client = client;
this.client = new OriginSettingClient(client, "tasks");
Copy link
Member Author

Choose a reason for hiding this comment

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

OriginSettingClient removes a lot of the ceremony around this!

Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be this.client = new OriginSettingClient(client, TASKS_ORIGIN)?

Copy link
Member Author

Choose a reason for hiding this comment

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

👍

@@ -47,15 +45,14 @@
*/
public class PersistentTasksService extends AbstractComponent {

private static final String ACTION_ORIGIN_TRANSIENT_NAME = "action.origin";
Copy link
Member Author

Choose a reason for hiding this comment

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

Now that the server has ThreadContext.stashWithOrigin and OriginSettingClient we can use it. I believe this change is a noop, and the tests seem to agree, but I'd love a second set of eyes on it.

@@ -25,6 +25,7 @@
* Action for retrieving a list of currently running tasks
*/
public class GetTaskAction extends Action<GetTaskResponse> {
public static final String TASKS_ORIGIN = "tasks";
Copy link
Member Author

Choose a reason for hiding this comment

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

I stuck this here because it felt like a fairly right place at the time. Now that I look at it again maybe it should be on something a little more centrally located. But it is about actions so I stuck it on an action. I'm not sure of a better spot.

Copy link
Contributor

@imotov imotov left a comment

Choose a reason for hiding this comment

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

Looks reasonable to me from the task management perspective, but I would leave it to @jaymode to review the security aspects.

@@ -70,7 +71,7 @@

@Inject
public TaskResultsService(Client client, ClusterService clusterService) {
this.client = client;
this.client = new OriginSettingClient(client, "tasks");
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be this.client = new OriginSettingClient(client, TASKS_ORIGIN)?

@@ -210,7 +212,7 @@ void getFinishedTaskFromIndex(Task thisTask, GetTaskRequest request, ActionListe
GetRequest get = new GetRequest(TaskResultsService.TASK_INDEX, TaskResultsService.TASK_TYPE,
request.getTaskId().toString());
get.setParentTask(clusterService.localNode().getId(), thisTask.getId());
client.get(get, new ActionListener<GetResponse>() {
new OriginSettingClient(client, TASKS_ORIGIN).get(get, new ActionListener<GetResponse>() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason why this is not done once in the TransportGetTaskAction's constructor?

Copy link
Member Author

Choose a reason for hiding this comment

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

I had a good reason for doing it this way when I wrote it at first but now it doesn't seem like a good reason. I'm switching it to what you suggest.

Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@jaymode jaymode requested a review from tvernum November 26, 2018 21:49
Copy link
Member

@jaymode jaymode left a comment

Choose a reason for hiding this comment

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

Overall I like the changes. I left a few comments

public <T extends TransportResponse> void sendRequest(
Transport.Connection connection, String action, TransportRequest request,
TransportRequestOptions options, TransportResponseHandler<T> handler) {
if (action.startsWith("indices:data/write/bulk[s]")) {
Copy link
Member

Choose a reason for hiding this comment

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

I think this would be simpler if this check got folded into shouldHaveOrigin

Copy link
Member

@jaymode jaymode left a comment

Choose a reason for hiding this comment

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

LGTM

@nik9000 nik9000 merged commit 0588dad into elastic:master Nov 28, 2018
@nik9000
Copy link
Member Author

nik9000 commented Nov 28, 2018

Thanks for reviewing @jaymode and @imotov!

About the BWC stuff - I think doing this without BWC would break a mixed cluster in new and intriguing ways. Ways that I'm not 100% ready to think about right now but will poke apart in the follow up that adds the user.

nik9000 added a commit that referenced this pull request Nov 28, 2018
Right now using the `GET /_tasks/<taskid>` API and causing a task to opt
in to saving its result after being completed requires permissions on
the `.tasks` index. When we built this we thought that that was fine,
but we've since moved towards not leaking details like "persisting task
results after the task is completed is done by saving them into an index
named `.tasks`." A more modern way of doing this would be to save the
tasks into the index "under the hood" and to have APIs to manage the
saved tasks. This is the first step down that road: it drops the
requirement to have permissions to interact with the `.tasks` index when
fetching task statuses and when persisting statuses beyond the lifetime
of the task.

In particular, this moves the concept of the "origin" of an action into
a more prominent place in the Elasticsearch server. The origin of an
action is ignored by the server, but the security plugin uses the origin
to make requests on behalf of a user in such a way that the user need
not have permissions to perform these actions. It *can* be made to be
fairly precise. More specifically, we can create an internal user just
for the tasks API that just has permission to interact with the `.tasks`
index. This change doesn't do that, instead, it uses the ubiquitus
"xpack" user which has most permissions because it is simpler. Adding
the tasks user is something I'd like to get to in a follow up change.

Instead, the majority of this change is about moving the "origin"
concept from the security portion of x-pack into the server. This should
allow any code to use the origin. To keep the change managable I've also
opted to deprecate rather than remove the "origin" helpers in the
security code. Removing them is almost entirely mechanical and I'd like
to that in a follow up as well.

Relates to #35573
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:Distributed Coordination/Task Management Issues for anything around the Tasks API - both persistent and node level. >enhancement v6.6.0 v7.0.0-beta1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants