Skip to content

Commit

Permalink
Fix geoip databases index access after system feature migration (agai…
Browse files Browse the repository at this point in the history
  • Loading branch information
joegallo authored Feb 21, 2025
1 parent 274be79 commit a895875
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 21 deletions.
5 changes: 5 additions & 0 deletions docs/changelog/122938.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 122938
summary: Fix geoip databases index access after system feature migration (again)
area: Ingest Node
type: bug
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,19 @@ public void testGeoIpSystemFeaturesMigration() throws Exception {

// as should a normal get *
assertBusy(() -> testGetStar(List.of("my-index-00001"), maybeSecurityIndex));

// and getting data streams
assertBusy(() -> testGetDatastreams());
} else {
// after the upgrade, but before the migration, Kibana should work
assertBusy(() -> testGetStarAsKibana(List.of("my-index-00001"), maybeSecurityIndex));

// as should a normal get *
assertBusy(() -> testGetStar(List.of("my-index-00001"), maybeSecurityIndex));

// and getting data streams
assertBusy(() -> testGetDatastreams());

// migrate the system features and give the cluster a moment to settle
Request migrateSystemFeatures = new Request("POST", "/_migration/system_features");
assertOK(client().performRequest(migrateSystemFeatures));
Expand All @@ -144,6 +150,9 @@ public void testGeoIpSystemFeaturesMigration() throws Exception {
// as should a normal get *
assertBusy(() -> testGetStar(List.of("my-index-00001"), maybeSecurityIndexReindexed));

// and getting data streams
assertBusy(() -> testGetDatastreams());

Request disableDownloader = new Request("PUT", "/_cluster/settings");
disableDownloader.setJsonEntity("""
{"persistent": {"ingest.geoip.downloader.enabled": false}}
Expand Down Expand Up @@ -257,4 +266,15 @@ private void testGetStarAsKibana(List<String> indexNames, @Nullable List<String>
Map<String, Object> map = responseAsMap(response);
assertThat(map.keySet(), is(new HashSet<>(indexNames)));
}

private void testGetDatastreams() throws IOException {
Request getStar = new Request("GET", "_data_stream");
getStar.setOptions(
RequestOptions.DEFAULT.toBuilder().setWarningsHandler(WarningsHandler.PERMISSIVE) // we don't care about warnings, just errors
);
Response response = client().performRequest(getStar);
assertOK(response);

// note: we don't actually care about the response, just that there was one and that it didn't error out on us
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.indices.SystemIndices.SystemIndexAccessLevel;

Expand Down Expand Up @@ -158,7 +159,26 @@ public static boolean isIndexVisible(
if (indexAbstraction.isSystem()) {
// check if it is net new
if (resolver.getNetNewSystemIndexPredicate().test(indexAbstraction.getName())) {
return isSystemIndexVisible(resolver, indexAbstraction);
// don't give this code any particular credit for being *correct*. it's just trying to resolve a combination of
// issues in a way that happens to *work*. there's probably a better way of writing things such that this won't
// be necessary, but for the moment, it happens to be expedient to write things this way.

// unwrap the alias and re-run the function on the write index of the alias -- that is, the alias is visible if
// the concrete index that it refers to is visible
Index writeIndex = indexAbstraction.getWriteIndex();
if (writeIndex == null) {
return false;
} else {
return isIndexVisible(
expression,
selectorString,
writeIndex.getName(),
indicesOptions,
metadata,
resolver,
includeDataStreams
);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.function.Supplier;

import static org.elasticsearch.index.mapper.MapperService.SINGLE_MAPPING_NAME;
import static org.elasticsearch.indices.SystemIndices.EXTERNAL_SYSTEM_INDEX_ACCESS_CONTROL_HEADER_KEY;
import static org.elasticsearch.indices.SystemIndices.SYSTEM_INDEX_ACCESS_CONTROL_HEADER_KEY;
import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.contains;
Expand Down Expand Up @@ -218,18 +219,6 @@ public void testIsIndexVisible() {
assertThat(isIndexVisible("data-stream1", "failures"), is(true));
}

private boolean isIndexVisible(String index, String selector) {
return IndexAbstractionResolver.isIndexVisible(
"*",
selector,
index,
IndicesOptions.strictExpandHidden(),
metadata,
indexNameExpressionResolver,
true
);
}

public void testIsNetNewSystemIndexVisible() {
final Settings settings = Settings.builder()
.put("index.number_of_replicas", 0)
Expand Down Expand Up @@ -269,16 +258,71 @@ public void testIsNetNewSystemIndexVisible() {
List.of(new SystemIndices.Feature("name", "description", List.of(fooDescriptor, barDescriptor)))
);

final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
threadContext.putHeader(SYSTEM_INDEX_ACCESS_CONTROL_HEADER_KEY, "false");
indexNameExpressionResolver = new IndexNameExpressionResolver(threadContext, systemIndices);
indexAbstractionResolver = new IndexAbstractionResolver(indexNameExpressionResolver);

metadata = Metadata.builder().put(foo, true).put(barReindexed, true).put(other, true).build();

assertThat(isIndexVisible("other", "*"), is(true));
assertThat(isIndexVisible(".foo", "*"), is(false));
assertThat(isIndexVisible(".bar", "*"), is(false));
// these indices options are for the GET _data_streams case
final IndicesOptions noHiddenNoAliases = IndicesOptions.builder()
.wildcardOptions(
IndicesOptions.WildcardOptions.builder()
.matchOpen(true)
.matchClosed(true)
.includeHidden(false)
.resolveAliases(false)
.build()
)
.build();

{
final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
threadContext.putHeader(SYSTEM_INDEX_ACCESS_CONTROL_HEADER_KEY, "true");
indexNameExpressionResolver = new IndexNameExpressionResolver(threadContext, systemIndices);
indexAbstractionResolver = new IndexAbstractionResolver(indexNameExpressionResolver);

// this covers the GET * case -- with system access, you can see everything
assertThat(isIndexVisible("other", "*"), is(true));
assertThat(isIndexVisible(".foo", "*"), is(true));
assertThat(isIndexVisible(".bar", "*"), is(true));

// but if you don't ask for hidden and aliases, you won't see hidden indices or aliases, naturally
assertThat(isIndexVisible("other", "*", noHiddenNoAliases), is(true));
assertThat(isIndexVisible(".foo", "*", noHiddenNoAliases), is(false));
assertThat(isIndexVisible(".bar", "*", noHiddenNoAliases), is(false));
}

{
final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
threadContext.putHeader(SYSTEM_INDEX_ACCESS_CONTROL_HEADER_KEY, "false");
indexNameExpressionResolver = new IndexNameExpressionResolver(threadContext, systemIndices);
indexAbstractionResolver = new IndexAbstractionResolver(indexNameExpressionResolver);

// this covers the GET * case -- without system access, you can't see everything
assertThat(isIndexVisible("other", "*"), is(true));
assertThat(isIndexVisible(".foo", "*"), is(false));
assertThat(isIndexVisible(".bar", "*"), is(false));

// no difference here in the datastream case, you can't see these then, either
assertThat(isIndexVisible("other", "*", noHiddenNoAliases), is(true));
assertThat(isIndexVisible(".foo", "*", noHiddenNoAliases), is(false));
assertThat(isIndexVisible(".bar", "*", noHiddenNoAliases), is(false));
}

{
final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
threadContext.putHeader(SYSTEM_INDEX_ACCESS_CONTROL_HEADER_KEY, "true");
threadContext.putHeader(EXTERNAL_SYSTEM_INDEX_ACCESS_CONTROL_HEADER_KEY, "some-elastic-product");
indexNameExpressionResolver = new IndexNameExpressionResolver(threadContext, systemIndices);
indexAbstractionResolver = new IndexAbstractionResolver(indexNameExpressionResolver);

// this covers the GET * case -- with product (only) access, you can't see everything
assertThat(isIndexVisible("other", "*"), is(true));
assertThat(isIndexVisible(".foo", "*"), is(false));
assertThat(isIndexVisible(".bar", "*"), is(false));

// no difference here in the datastream case, you can't see these then, either
assertThat(isIndexVisible("other", "*", noHiddenNoAliases), is(true));
assertThat(isIndexVisible(".foo", "*", noHiddenNoAliases), is(false));
assertThat(isIndexVisible(".bar", "*", noHiddenNoAliases), is(false));
}
}

private static XContentBuilder mappings() {
Expand Down Expand Up @@ -306,4 +350,12 @@ private List<String> resolveAbstractionsSelectorAllowed(List<String> expressions
private List<String> resolveAbstractions(List<String> expressions, IndicesOptions indicesOptions, Supplier<Set<String>> mask) {
return indexAbstractionResolver.resolveIndexAbstractions(expressions, indicesOptions, metadata, mask, (idx) -> true, true);
}

private boolean isIndexVisible(String index, String selector) {
return isIndexVisible(index, selector, IndicesOptions.strictExpandHidden());
}

private boolean isIndexVisible(String index, String selector, IndicesOptions indicesOptions) {
return IndexAbstractionResolver.isIndexVisible("*", selector, index, indicesOptions, metadata, indexNameExpressionResolver, true);
}
}

0 comments on commit a895875

Please sign in to comment.