ES|QL: Fix queries with document level security on lookup indexes#120617
ES|QL: Fix queries with document level security on lookup indexes#120617luigidellaquila merged 25 commits intoelastic:mainfrom
Conversation
|
Hi @luigidellaquila, I've created a changelog YAML for you. |
|
Pinging @elastic/es-analytical-engine (Team:Analytics) |
…to esql/fix_lookup_dls
|
Failures due to #120637 |
| return failures.failures(); | ||
| } | ||
|
|
||
| private void checkLookupJoin(LogicalPlan p, Failures failures) { |
There was a problem hiding this comment.
Drive by comment: LookupJoin could implement PostAnalysisVerificationAware and perform the check there.
There was a problem hiding this comment.
Thanks @bpintea, good point, doing it now
Muting a yaml bwc test called "alias" apparently is not possible:
```
task.skipTest("esql/190_lookup_join/alias", "LOOKUP JOIN does not support index aliases for now")
```
```
* What went wrong:
Execution failed for task ':x-pack:plugin:yamlRestCompatTestTransform'.
> class com.fasterxml.jackson.databind.node.TextNode cannot be cast to class com.fasterxml.jackson.databind.node.ArrayNode (com.fasterxml.jackson.databind.node.TextNode and com.fasterxml.jackson.databind.node.ArrayNode are in unnamed module of loader org.gradle.internal.classloader.VisitableURLClassLoader$InstrumentingVisitableURLClassLoader @725f7fdd)
```
I need to mute this test in
#120617
Muting a yaml bwc test called "alias" apparently is not possible:
```
task.skipTest("esql/190_lookup_join/alias", "LOOKUP JOIN does not support index aliases for now")
```
```
* What went wrong:
Execution failed for task ':x-pack:plugin:yamlRestCompatTestTransform'.
> class com.fasterxml.jackson.databind.node.TextNode cannot be cast to class com.fasterxml.jackson.databind.node.ArrayNode (com.fasterxml.jackson.databind.node.TextNode and com.fasterxml.jackson.databind.node.ArrayNode are in unnamed module of loader org.gradle.internal.classloader.VisitableURLClassLoader$InstrumentingVisitableURLClassLoader @725f7fdd)
```
I need to mute this test in
elastic#120617
dnhatn
left a comment
There was a problem hiding this comment.
The LookupService looks good to me. Thanks @luigidellaquila
Muting a yaml bwc test called "alias" apparently is not possible:
```
task.skipTest("esql/190_lookup_join/alias", "LOOKUP JOIN does not support index aliases for now")
```
```
* What went wrong:
Execution failed for task ':x-pack:plugin:yamlRestCompatTestTransform'.
> class com.fasterxml.jackson.databind.node.TextNode cannot be cast to class com.fasterxml.jackson.databind.node.ArrayNode (com.fasterxml.jackson.databind.node.TextNode and com.fasterxml.jackson.databind.node.ArrayNode are in unnamed module of loader org.gradle.internal.classloader.VisitableURLClassLoader$InstrumentingVisitableURLClassLoader @725f7fdd)
```
I need to mute this test in
#120617
alex-spies
left a comment
There was a problem hiding this comment.
Had a first look, will continue tomorrow. Looking good so far, thanks @luigidellaquila !
x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/190_lookup_join.yml
Show resolved
Hide resolved
...ugin/esql/qa/security/src/javaRestTest/java/org/elasticsearch/xpack/esql/EsqlSecurityIT.java
Outdated
Show resolved
Hide resolved
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java
Outdated
Show resolved
Hide resolved
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java
Show resolved
Hide resolved
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java
Show resolved
Hide resolved
| public void postAnalysisVerification(Failures failures) { | ||
| super.postAnalysisVerification(failures); | ||
| if (right() instanceof EsRelation esr) { | ||
| if (esr.concreteIndices().contains(esr.indexPattern()) == false) { |
There was a problem hiding this comment.
Also: Could there be weird edge cases where the name of the index contains e.g. an escaped wildcard character, so that an index pattern is being used, but coincidentally, it also matches a concrete index? There's no escaping in index names, right? Per the docs it doesn't seem so: https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html
I understand this is as edge case-y as it gets; but since avoiding aliases is required to ensure permissions are respected correctly, we better try to think about every possible scenario.
to make them easier to run on Serverless
alex-spies
left a comment
There was a problem hiding this comment.
Thanks a lot for the iterations @luigidellaquila !
I have a final test suggestion, but otherwise LGTM.
| assertThat(respMap.get("values"), equalTo(List.of(Arrays.asList(40.0, "sales")))); | ||
|
|
||
| resp = runESQLCommand("dls_user2", "ROW x = 32.0 | EVAL value = x | LOOKUP JOIN `lookup-user2` ON value | KEEP x, org"); | ||
| assertOK(resp); | ||
| respMap = entityAsMap(resp); | ||
| assertThat( | ||
| respMap.get("columns"), | ||
| equalTo(List.of(Map.of("name", "x", "type", "double"), Map.of("name", "org", "type", "keyword"))) | ||
| ); | ||
| assertThat(respMap.get("values"), equalTo(List.of(List.of(32.0, "marketing")))); | ||
|
|
There was a problem hiding this comment.
Nice, these are very helpful. Thanks for adding!
| failures.add( | ||
| fail( | ||
| esr, | ||
| "invalid [{}] resolution in lookup mode to an index in [{}] indices", |
There was a problem hiding this comment.
| "invalid [{}] resolution in lookup mode to an index in [{}] indices", | |
| "invalid [{}] resolution in lookup mode to [{}] indices", |
Previously it was a slightly different message, not sure if it was changed intentionally.
alex-spies
left a comment
There was a problem hiding this comment.
Thanks a lot for the iterations @luigidellaquila ! I'm feeling much more confident about the security of LOOKUP JOIN now :)
| TransportRequest transportRequest | ||
| ) { | ||
| ThreadContext threadContext = transportService.getThreadPool().getThreadContext(); | ||
| try (ThreadContext.StoredContext unused = threadContext.stashWithOrigin(ClientHelper.ENRICH_ORIGIN)) { |
There was a problem hiding this comment.
Agree, with @dnhatn 's suggestion the whole situation is more localized and more clear.
Personally, I'd still leave a comment in place because for people unfamiliar with this code, it can be unexpected that the call to ThreadContext.stashWithOrigin will abandon the current user's authorization. (It's explained in stashWithOrigin's javadoc, but that requires an extra hop.)
There was a problem hiding this comment.
Sorry, one last test suggestion - we can also add this in a separate PR if you're already about to make this ready to merge.
I just noticed: all of the fls test cases have permission to read the value field, which is the join key.
I think it'd be interesting to test if forbidding this field for a user makes it so all look ups are null. And, if we wanna be extra paranoid, consider the case where the lookup index has duplicates for a given join key, so the LOOKUP JOIN would lead to extra rows - but without permissions granted on the join key, no extra rows should be emitted. (Otherwise, users could exfiltrate statistics about fields they have no permissions on).
There was a problem hiding this comment.
Thanks Alex, I still have a couple of things to push, I'll add this one as well.
I think the whole query will fail because the user doesn't see the join key in the schema, but I'll double-check
There was a problem hiding this comment.
The new tests confirm the above: if a role cannot see the join key in the lookup index, the query will fail with "Unknown column"
|
Thanks for the reviews folks! |
💔 Backport failed
You can use sqren/backport to manually backport by running |
There are some analyser tests for this, but I thought it useful to also have a yaml test for more coverage, since there was temporarily an issue elastic#120189 with this, later fixed in elastic#120617.
There are some analyser tests for this, but I thought it useful to also have a yaml test for more coverage, since there was temporarily an issue elastic#120189 with this, later fixed in elastic#120617.
There are some analyser tests for this, but I thought it useful to also have a yaml test for more coverage, since there was temporarily an issue elastic#120189 with this, later fixed in elastic#120617.
Fixes: #120509
Make ES|QL respect document level permissions on lookup indexes.
This fix exposes a problem with aliases as lookup indexes: if a user has permissions on an alias, but not on the underlying index, the query fails with an authorization error. This needs to be investigated further and fixed. For now we'll just disable support for aliases in LOOKUP JOIN command