Skip to content

Conversation

@mosche
Copy link
Contributor

@mosche mosche commented Mar 5, 2025

Entitlement checks are handling relative (symbolic) links incorrectly, such a path is resolved against the current work dir rather than the link's parent.

In Serverless this caused NotEntitledExceptions as the following, looking at it retrospectively the error is fairly obvious :/

Not entitled: component [(server)], module [org.elasticsearch.server], class [class org.elasticsearch.common.file.AbstractFileWatchingService], entitlement [file], operation [read], path [..data/settings.json]

This PR changes the checks for createSymbolicLink and createLink to resolve a relative link target correctly.

The check for toRealPath is changed from using readSymbolicLink to toRealPath. Besides above issue, readSymbolicLink does not work correctly in this case as it does not follow links. This is necessary with K8s mounts due to usage of a chain of symbolic links, e.g. mount-0.tmp -> ..data/mount-0.tmp -> ..version/mount-0.tmp where ..data is a symbolic link to ..version.

Manually following links is also not trivially possible, e.g. in above example ..data/mount-0.tmp is not a symbolic link (and readSymbolicLink would throw) making it an expensive task as each segment of a path needs to be checked.

Because of that I decided to switch to using toRealPath. As previously discussed in #122507, toRealPath has the disadvantage that the check on the target is skipped if it doesn't exist (throws a FileNotFoundException). Comparing the two options, this seems to be the lesser evil.

Relates to ES-11019

@mosche mosche added auto-backport Automatically create backport pull requests when merged v8.18.1 v8.19.0 v9.0.1 v9.1.0 :Core/Infra/Entitlements Entitlements infrastructure labels Mar 5, 2025
@mosche mosche force-pushed the entitlements/fix-relative-links branch from fa92815 to eacba7c Compare March 5, 2025 19:05
"files",
List.of(
Map.of("path", tempDir.resolve("read_dir"), "mode", "read_write"),
Map.of("path", tempDir.resolve("read_dir").resolve("k8s").resolve("..data"), "mode", "read", "exclusive", true),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

..data is marked as exclusive here, it shouldn't ever be visible when testing K8s like file mounts (mount-0.tmp-> ..data/mount-0.tmp-> ..version/mount-0.tmp)

boolean canRead = entitlements.fileAccess().canRead(path);
if (canRead && followLinks) {
try {
realPath = path.toRealPath();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See PR description why using toRealPath

}
}
policyManager.checkFileRead(callerClass, that);
policyManager.checkFileRead(callerClass, that, followLinks);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the check into the PolicyManager, this allows to use toRealPath (without escaping the recursion here). See the PR description for why toRealPath instead of readSymbolicLink...

@mosche mosche marked this pull request as ready for review March 5, 2025 20:00
@mosche mosche requested a review from a team as a code owner March 5, 2025 20:00
@elasticsearchmachine elasticsearchmachine added the Team:Core/Infra Meta label for core/infra team label Mar 5, 2025
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-core-infra (Team:Core/Infra)

Copy link
Member

@rjernst rjernst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, a couple minor questions


private static Path resolveLinkTarget(Path path, Path target) {
var parent = path.getParent();
return parent == null ? target : parent.resolve(target);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If target is absolute shouldn't we always return it? ie, should we only resolve relative to the parent when target is relative?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what parent.resolve(target) does. So didn't seem necessary to handle this case specially, but agreed it could make it easier to understand. Can do next week unless someone can pick this up

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct, it should not be strictly needed. I think this could be a follow-up if we feel we need it.

try {
realPath = path.toRealPath();
} catch (IOException e) {
// target not found or other IO error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we set canRead = false in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the ideal case we would throw a FileNotFoundException, as done by toRealPath in this case...
Not sure there's a good solution in this case, don't have a strong opinion...

In the worst case we leaking the information that a file does not exist in a location the caller shouldn't be able to access.

The opposite way the location might be OK, but the link broken... throwing a runtime exception the caller doesn't expect / handle in this case seems problematic as well 😔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the ongoing discussion about tweaking exceptions we throw from instrumentation, I would lean towards throwing FileNotFoundException, but that would require changing the signature of our functions in the chain, right?
I don't think it should be too difficult, but it is additional work.
My 2c: I am ok with anything you decide is best for now, (leaving this as-is or setting canRead = false -- which would mean throwing NotEntitledException in the end), but I'd consider a follow-up that let us propagate the IOException / throw a FileNotFoundException.
Does it make sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll leave this as is and will follow up with a separate PR to throw a NoSuchFileException, it might spark additional discussions ... In the case of toRealPath it doesn't make a difference, the same exception would be thrown if calling toRealPath for real once the permission check passed.
That's also why keeping canRead = true is fine, the same would be thrown if attempting to read the content of the link.

Copy link
Contributor

@ldematte ldematte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM2, but I ++ both Ryan's questions

@mosche mosche merged commit 7cec948 into elastic:main Mar 10, 2025
17 checks passed
@mosche mosche deleted the entitlements/fix-relative-links branch March 10, 2025 12:45
mosche added a commit to mosche/elasticsearch that referenced this pull request Mar 10, 2025
mosche added a commit to mosche/elasticsearch that referenced this pull request Mar 10, 2025
mosche added a commit to mosche/elasticsearch that referenced this pull request Mar 10, 2025
georgewallace pushed a commit to georgewallace/elasticsearch that referenced this pull request Mar 11, 2025
elasticsearchmachine pushed a commit that referenced this pull request Mar 11, 2025
…hecking file entitlements (#124483)

This will rethrow the `NoSuchFileException` when encountering an invalid
symbolic link when following links during file (read) entitlement
checks.

Relates to #124133
(ES-11019)
mosche added a commit to mosche/elasticsearch that referenced this pull request Mar 11, 2025
…hecking file entitlements (elastic#124483)

This will rethrow the `NoSuchFileException` when encountering an invalid
symbolic link when following links during file (read) entitlement
checks.

Relates to elastic#124133
(ES-11019)
mosche added a commit to mosche/elasticsearch that referenced this pull request Mar 11, 2025
…hecking file entitlements (elastic#124483)

This will rethrow the `NoSuchFileException` when encountering an invalid
symbolic link when following links during file (read) entitlement
checks.

Relates to elastic#124133
(ES-11019)
mosche added a commit to mosche/elasticsearch that referenced this pull request Mar 11, 2025
…hecking file entitlements (elastic#124483)

This will rethrow the `NoSuchFileException` when encountering an invalid
symbolic link when following links during file (read) entitlement
checks.

Relates to elastic#124133
(ES-11019)
elasticsearchmachine pushed a commit that referenced this pull request Mar 11, 2025
…hecking file entitlements (#124483) (#124543)

This will rethrow the `NoSuchFileException` when encountering an invalid
symbolic link when following links during file (read) entitlement
checks.

Relates to #124133
(ES-11019)
elasticsearchmachine pushed a commit that referenced this pull request Mar 11, 2025
…hecking file entitlements (#124483) (#124541)

This will rethrow the `NoSuchFileException` when encountering an invalid
symbolic link when following links during file (read) entitlement
checks.

Relates to #124133
(ES-11019)
elasticsearchmachine pushed a commit that referenced this pull request Mar 11, 2025
…hecking file entitlements (#124483) (#124542)

This will rethrow the `NoSuchFileException` when encountering an invalid
symbolic link when following links during file (read) entitlement
checks.

Relates to #124133
(ES-11019)
albertzaharovits pushed a commit to albertzaharovits/elasticsearch that referenced this pull request Mar 13, 2025
…hecking file entitlements (elastic#124483)

This will rethrow the `NoSuchFileException` when encountering an invalid
symbolic link when following links during file (read) entitlement
checks.

Relates to elastic#124133
(ES-11019)
jfreden pushed a commit to jfreden/elasticsearch that referenced this pull request Mar 13, 2025
jfreden pushed a commit to jfreden/elasticsearch that referenced this pull request Mar 13, 2025
…hecking file entitlements (elastic#124483)

This will rethrow the `NoSuchFileException` when encountering an invalid
symbolic link when following links during file (read) entitlement
checks.

Relates to elastic#124133
(ES-11019)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto-backport Automatically create backport pull requests when merged :Core/Infra/Entitlements Entitlements infrastructure >refactoring Team:Core/Infra Meta label for core/infra team v8.18.1 v8.19.0 v9.0.1 v9.1.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants