Browse Source

Don't require separate privilege for internal detail of put pipeline (#60106)

Putting an ingest pipeline used to require that the user calling
it had permission to get nodes info as well as permission to
manage ingest.  This was due to an internal implementaton detail
that was not visible to the end user.

This change alters the behaviour so that a user with the
manage_pipeline cluster privilege can put an ingest pipeline
regardless of whether they have the separate privilege to get
nodes info.  The internal implementation detail now runs as
the internal _xpack user when security is enabled.

Fixes #60041
David Roberts 5 năm trước cách đây
mục cha
commit
8543197b01

+ 7 - 2
server/src/main/java/org/elasticsearch/action/ingest/PutPipelineTransportAction.java

@@ -25,6 +25,7 @@ import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest;
 import org.elasticsearch.action.support.ActionFilters;
 import org.elasticsearch.action.support.master.AcknowledgedResponse;
 import org.elasticsearch.action.support.master.TransportMasterNodeAction;
+import org.elasticsearch.client.OriginSettingClient;
 import org.elasticsearch.client.node.NodeClient;
 import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.block.ClusterBlockException;
@@ -43,10 +44,12 @@ import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
+import static org.elasticsearch.ingest.IngestService.INGEST_ORIGIN;
+
 public class PutPipelineTransportAction extends TransportMasterNodeAction<PutPipelineRequest, AcknowledgedResponse> {
 
     private final IngestService ingestService;
-    private final NodeClient client;
+    private final OriginSettingClient client;
 
     @Inject
     public PutPipelineTransportAction(ThreadPool threadPool, TransportService transportService,
@@ -56,7 +59,9 @@ public class PutPipelineTransportAction extends TransportMasterNodeAction<PutPip
             PutPipelineAction.NAME, transportService, ingestService.getClusterService(),
             threadPool, actionFilters, PutPipelineRequest::new, indexNameExpressionResolver
         );
-        this.client = client;
+        // This client is only used to perform an internal implementation detail,
+        // so uses an internal origin context rather than the user context
+        this.client = new OriginSettingClient(client, INGEST_ORIGIN);
         this.ingestService = ingestService;
     }
 

+ 2 - 0
server/src/main/java/org/elasticsearch/ingest/IngestService.java

@@ -82,6 +82,8 @@ public class IngestService implements ClusterStateApplier, ReportingService<Inge
 
     public static final String NOOP_PIPELINE_NAME = "_none";
 
+    public static final String INGEST_ORIGIN = "ingest";
+
     private static final Logger logger = LogManager.getLogger(IngestService.class);
 
     private final ClusterService clusterService;

+ 1 - 1
server/src/main/java/org/elasticsearch/persistent/PersistentTasksService.java

@@ -48,7 +48,7 @@ public class PersistentTasksService {
 
     private static final Logger logger = LogManager.getLogger(PersistentTasksService.class);
 
-    private static final String PERSISTENT_TASK_ORIGIN = "persistent_tasks";
+    public static final String PERSISTENT_TASK_ORIGIN = "persistent_tasks";
 
     private final Client client;
     private final ClusterService clusterService;

+ 2 - 1
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ClientHelper.java

@@ -15,6 +15,7 @@ import org.elasticsearch.client.Client;
 import org.elasticsearch.client.OriginSettingClient;
 import org.elasticsearch.common.util.concurrent.ThreadContext;
 import org.elasticsearch.common.util.set.Sets;
+import org.elasticsearch.persistent.PersistentTasksService;
 import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
 import org.elasticsearch.xpack.core.security.authc.AuthenticationServiceField;
 
@@ -63,7 +64,7 @@ public final class ClientHelper {
     public static final String INDEX_LIFECYCLE_ORIGIN = "index_lifecycle";
     public static final String MONITORING_ORIGIN = "monitoring";
     public static final String DEPRECATION_ORIGIN = "deprecation";
-    public static final String PERSISTENT_TASK_ORIGIN = "persistent_tasks";
+    public static final String PERSISTENT_TASK_ORIGIN = PersistentTasksService.PERSISTENT_TASK_ORIGIN;
     public static final String ROLLUP_ORIGIN = "rollup";
     public static final String ENRICH_ORIGIN = "enrich";
     public static final String TRANSFORM_ORIGIN = "transform";

+ 1 - 4
x-pack/plugin/ml/qa/ml-with-security/build.gradle

@@ -146,9 +146,6 @@ integTest.runner {
     'ml/inference_crud/Test put model with empty input.field_names',
     'ml/inference_crud/Test PUT model where target type and inference config mismatch',
     'ml/inference_processor/Test create processor with missing mandatory fields',
-    'ml/inference_processor/Test create and delete pipeline with inference processor',
-    'ml/inference_processor/Test create processor with deprecated fields',
-    'ml/inference_processor/Test simulate',
     'ml/inference_stats_crud/Test get stats given missing trained model',
     'ml/inference_stats_crud/Test get stats given expression without matches and allow_no_match is false',
     'ml/jobs_crud/Test cannot create job with existing categorizer state document',
@@ -220,7 +217,7 @@ testClusters.integTest {
   testDistribution = 'DEFAULT'
   extraConfigFile 'roles.yml', file('roles.yml')
   user username: "x_pack_rest_user", password: "x-pack-test-password"
-  user username: "ml_admin", password: "x-pack-test-password", role: "minimal,machine_learning_admin"
+  user username: "ml_admin", password: "x-pack-test-password", role: "minimal,machine_learning_admin,ingest_admin"
   user username: "ml_user", password: "x-pack-test-password", role: "minimal,machine_learning_user"
   user username: "no_ml", password: "x-pack-test-password", role: "minimal"
   setting 'xpack.license.self_generated.type', 'trial'

+ 2 - 2
x-pack/plugin/ml/qa/ml-with-security/src/test/java/org/elasticsearch/smoketest/MlWithSecurityUserRoleIT.java

@@ -55,7 +55,8 @@ public class MlWithSecurityUserRoleIT extends MlWithSecurityIT {
         } catch (AssertionError ae) {
             assertThat(ae.getMessage(),
                 either(containsString("action [cluster:monitor/xpack/ml"))
-                    .or(containsString("action [cluster:admin/xpack/ml")));
+                    .or(containsString("action [cluster:admin/xpack/ml"))
+                    .or(containsString("action [cluster:admin/ingest")));
             assertThat(ae.getMessage(), containsString("returned [403 Forbidden]"));
             assertThat(ae.getMessage(), containsString("is unauthorized for user [ml_user]"));
         }
@@ -75,4 +76,3 @@ public class MlWithSecurityUserRoleIT extends MlWithSecurityIT {
         return new String[]{"ml_user", "x-pack-test-password"};
     }
 }
-

+ 2 - 0
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationUtils.java

@@ -20,6 +20,7 @@ import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 import static org.elasticsearch.action.admin.cluster.node.tasks.get.GetTaskAction.TASKS_ORIGIN;
+import static org.elasticsearch.ingest.IngestService.INGEST_ORIGIN;
 import static org.elasticsearch.xpack.core.ClientHelper.ASYNC_SEARCH_ORIGIN;
 import static org.elasticsearch.xpack.core.ClientHelper.ENRICH_ORIGIN;
 import static org.elasticsearch.xpack.core.ClientHelper.IDP_ORIGIN;
@@ -118,6 +119,7 @@ public final class AuthorizationUtils {
             case INDEX_LIFECYCLE_ORIGIN:
             case ENRICH_ORIGIN:
             case IDP_ORIGIN:
+            case INGEST_ORIGIN:
             case STACK_ORIGIN:
             case TASKS_ORIGIN:   // TODO use a more limited user for tasks
                 securityContext.executeAsUser(XPackUser.INSTANCE, consumer, Version.CURRENT);