-
Notifications
You must be signed in to change notification settings - Fork 25.2k
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
Tasks: Only require task permissions #35667
Conversation
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
Pinging @elastic/es-distributed |
@@ -316,3 +314,51 @@ setup: | |||
index: dest | |||
dest: | |||
index: source | |||
|
|||
--- |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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
)?
There was a problem hiding this comment.
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"; |
There was a problem hiding this comment.
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"; |
There was a problem hiding this comment.
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.
There was a problem hiding this 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"); |
There was a problem hiding this comment.
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>() { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this 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
server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TestTaskPlugin.java
Show resolved
Hide resolved
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]")) { |
There was a problem hiding this comment.
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
...plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationUtils.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
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 optin 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 thetasks 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 whenfetching 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