فهرست منبع

Handle unmatching remote cluster wildcards properly for IndicesRequest.SingleIndexNoWildcards requests (#109185)

When security is enabled and a IndicesRequest.SingleIndexNoWildcards request uses a wildcard for 
remote clusters that does not match (for example "*:myindex" when there are no configured remotes),
RBACEngine#authorizeIndexAction throws an assert error because it is given an empty list of indices.

This fix checks the IndicesRequest.SingleIndexNoWildcards special case in
IndicesAndAliasesResolver.resolveIndicesAndAliasesWithoutWildcards
and if splitLocalAndRemoteIndexNames filters out all indexes due to a non-matching remote cluster 
wildcard, that code now just throws the NoSuchRemoteClusterException (which is what RBACEngine
does when asserts are disabled).

This handles painless/execute requests to remote clusters.

Added note to painless/execute API docs about wildcards not being allowed
Michael Peterson 1 سال پیش
والد
کامیت
335c03afa9

+ 6 - 0
docs/changelog/109185.yaml

@@ -0,0 +1,6 @@
+pr: 109185
+summary: Handle unmatching remote cluster wildcards properly for `IndicesRequest.SingleIndexNoWildcards`
+  requests
+area: Authorization
+type: bug
+issues: []

+ 4 - 0
docs/painless/painless-guide/painless-execute-script.asciidoc

@@ -123,6 +123,10 @@ alias. For example, `remote1:my_index` indicates that you want to execute
 the painless script against the "my_index" index on the "remote1" cluster. This
 request will be forwarded to the "remote1" cluster if you have
 {ref}/remote-clusters-connect.html[configured a connection] to that remote cluster.
+
+NOTE: Wildcards are not accepted in the index expression for this endpoint. The
+expression `*:myindex` will return the error "No such remote cluster" and the
+expression `logs*` or `remote1:logs*` will return the error "index not found".
 ====
 
 `params`:: (`Map`, read-only)

+ 13 - 1
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolver.java

@@ -27,6 +27,7 @@ import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.core.Nullable;
 import org.elasticsearch.index.Index;
 import org.elasticsearch.index.IndexNotFoundException;
+import org.elasticsearch.transport.NoSuchRemoteClusterException;
 import org.elasticsearch.transport.RemoteClusterAware;
 import org.elasticsearch.transport.RemoteConnectionStrategy;
 import org.elasticsearch.transport.TransportRequest;
@@ -164,6 +165,18 @@ class IndicesAndAliasesResolver {
         final ResolvedIndices split;
         if (indicesRequest instanceof IndicesRequest.SingleIndexNoWildcards single && single.allowsRemoteIndices()) {
             split = remoteClusterResolver.splitLocalAndRemoteIndexNames(indicesRequest.indices());
+            // all indices can come back empty when the remote index expression included a cluster alias with a wildcard
+            // and no remote clusters are configured that match it
+            if (split.getLocal().isEmpty() && split.getRemote().isEmpty()) {
+                for (String indexExpression : indices) {
+                    String[] clusterAndIndex = indexExpression.split(":", 2);
+                    if (clusterAndIndex.length == 2) {
+                        if (clusterAndIndex[0].contains("*")) {
+                            throw new NoSuchRemoteClusterException(clusterAndIndex[0]);
+                        }
+                    }
+                }
+            }
         } else {
             split = new ResolvedIndices(Arrays.asList(indicesRequest.indices()), List.of());
         }
@@ -473,5 +486,4 @@ class IndicesAndAliasesResolver {
             return new ResolvedIndices(local == null ? List.of() : local, remote);
         }
     }
-
 }

+ 98 - 0
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java

@@ -58,6 +58,7 @@ import org.elasticsearch.protocol.xpack.graph.GraphExploreRequest;
 import org.elasticsearch.search.internal.ShardSearchRequest;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.threadpool.ThreadPool;
+import org.elasticsearch.transport.NoSuchRemoteClusterException;
 import org.elasticsearch.transport.TransportRequest;
 import org.elasticsearch.xpack.core.graph.action.GraphExploreAction;
 import org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef;
@@ -104,6 +105,7 @@ import static org.elasticsearch.test.TestMatchers.throwableWithMessage;
 import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.RESTRICTED_INDICES;
 import static org.elasticsearch.xpack.security.authz.AuthorizedIndicesTests.getRequestInfo;
 import static org.elasticsearch.xpack.security.support.SecuritySystemIndices.SECURITY_MAIN_ALIAS;
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.Matchers.arrayContaining;
@@ -455,6 +457,102 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
         );
     }
 
+    public void testResolveIndicesAndAliasesWithoutWildcardsWithSingleIndexNoWildcardsRequest() {
+        // test 1: matching local index
+        {
+            ResolvedIndices resolvedIndices = defaultIndicesResolver.resolveIndicesAndAliasesWithoutWildcards(
+                TransportSearchAction.TYPE.name() + "[s]",
+                createSingleIndexNoWildcardsRequest(new String[] { "index10" })
+            );
+            assertThat(resolvedIndices.getRemote().size(), equalTo(0));
+            assertThat(resolvedIndices.getLocal().size(), equalTo(1));
+            assertThat(resolvedIndices.getLocal().get(0), equalTo("index10"));
+        }
+
+        // test 2: matching remote index
+        {
+            ResolvedIndices resolvedIndices = defaultIndicesResolver.resolveIndicesAndAliasesWithoutWildcards(
+                TransportSearchAction.TYPE.name() + "[s]",
+                createSingleIndexNoWildcardsRequest(new String[] { "remote:indexName" })
+            );
+            assertThat(resolvedIndices.getRemote().size(), equalTo(1));
+            assertThat(resolvedIndices.getRemote().get(0), equalTo("remote:indexName"));
+            assertThat(resolvedIndices.getLocal().size(), equalTo(0));
+        }
+
+        // test 3: missing local index
+        {
+            ResolvedIndices resolvedIndices = defaultIndicesResolver.resolveIndicesAndAliasesWithoutWildcards(
+                TransportSearchAction.TYPE.name() + "[s]",
+                createSingleIndexNoWildcardsRequest(new String[] { "zzz_no_such_index_zzz" })
+            );
+            assertThat(resolvedIndices.getRemote().size(), equalTo(0));
+            assertThat(resolvedIndices.getLocal().size(), equalTo(1));
+            assertThat(resolvedIndices.getLocal().get(0), equalTo("zzz_no_such_index_zzz"));
+        }
+
+        // test 4: missing remote index
+        {
+            ResolvedIndices resolvedIndices = defaultIndicesResolver.resolveIndicesAndAliasesWithoutWildcards(
+                TransportSearchAction.TYPE.name() + "[s]",
+                createSingleIndexNoWildcardsRequest(new String[] { "remote:zzz_no_such_index_zzz" })
+            );
+            assertThat(resolvedIndices.getRemote().size(), equalTo(1));
+            assertThat(resolvedIndices.getRemote().get(0), equalTo("remote:zzz_no_such_index_zzz"));
+            assertThat(resolvedIndices.getLocal().size(), equalTo(0));
+        }
+
+        // test 5: both local and remote indexes
+        {
+            ResolvedIndices resolvedIndices = defaultIndicesResolver.resolveIndicesAndAliasesWithoutWildcards(
+                TransportSearchAction.TYPE.name() + "[s]",
+                createSingleIndexNoWildcardsRequest(new String[] { "index10", "remote:indexName" })
+            );
+            assertThat(resolvedIndices.getRemote().size(), equalTo(1));
+            assertThat(resolvedIndices.getRemote().get(0), equalTo("remote:indexName"));
+            assertThat(resolvedIndices.getLocal().size(), equalTo(1));
+            assertThat(resolvedIndices.getLocal().get(0), equalTo("index10"));
+        }
+
+        // test 6: remote cluster name with wildcards that does not match any configured remotes
+        {
+            NoSuchRemoteClusterException exception = expectThrows(
+                NoSuchRemoteClusterException.class,
+                () -> defaultIndicesResolver.resolveIndicesAndAliasesWithoutWildcards(
+                    TransportSearchAction.TYPE.name() + "[s]",
+                    createSingleIndexNoWildcardsRequest(new String[] { "x*x:test" })
+                )
+            );
+            assertThat(exception.getMessage(), containsString("no such remote cluster: [x*x]"));
+        }
+
+        // test 7: mix and test 2 and test 6 - should not result in exception (wildcard without matches has no effect)
+        {
+            ResolvedIndices resolvedIndices = defaultIndicesResolver.resolveIndicesAndAliasesWithoutWildcards(
+                TransportSearchAction.TYPE.name() + "[s]",
+                createSingleIndexNoWildcardsRequest(new String[] { "x*x:test", "remote:indexName" })
+            );
+            assertThat(resolvedIndices.getRemote().size(), equalTo(1));
+            assertThat(resolvedIndices.getRemote().get(0), equalTo("remote:indexName"));
+            assertThat(resolvedIndices.getLocal().size(), equalTo(0));
+        }
+    }
+
+    private static IndicesRequest.SingleIndexNoWildcards createSingleIndexNoWildcardsRequest(String[] indexExpression) {
+        IndicesRequest.SingleIndexNoWildcards singleIndexNoWildcardsRequest = new IndicesRequest.SingleIndexNoWildcards() {
+            @Override
+            public String[] indices() {
+                return indexExpression;
+            }
+
+            @Override
+            public IndicesOptions indicesOptions() {
+                return IndicesOptions.DEFAULT;
+            }
+        };
+        return singleIndexNoWildcardsRequest;
+    }
+
     public void testExplicitDashIndices() {
         SearchRequest request = new SearchRequest("-index10", "-index20");
         List<String> indices = resolveIndices(request, buildAuthorizedIndices(userDashIndices, TransportSearchAction.TYPE.name()))