[ 
https://issues.apache.org/jira/browse/KAFKA-20700?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Mickael Maison resolved KAFKA-20700.
------------------------------------
    Fix Version/s: 4.4.0
       Resolution: Fixed

> AllowedPaths should resolve symlinks before validating paths against 
> allowed.paths
> ----------------------------------------------------------------------------------
>
>                 Key: KAFKA-20700
>                 URL: https://issues.apache.org/jira/browse/KAFKA-20700
>             Project: Kafka
>          Issue Type: Bug
>            Reporter: Nilesh Kumar
>            Assignee: Nilesh Kumar
>            Priority: Major
>             Fix For: 4.4.0
>
>
> h2. Problem
> `AllowedPaths.parseUntrustedPath()` validates user-supplied paths using 
> lexical `Path.normalize()` only. It does not resolve symbolic links before 
> checking whether a path is under a configured `allowed.paths` base directory.
> As a result, if a symlink exists inside an allowed directory (e.g. 
> `/opt/kafka/secrets/link -> /some/other/path`), validation passes because the 
> lexical path appears under the allowed base, but `FileConfigProvider` and 
> `DirectoryConfigProvider` later read the symlink target when accessing the 
> filesystem.
> h2. Affected components
> * `org.apache.kafka.common.config.internals.AllowedPaths`
> * `org.apache.kafka.common.config.provider.FileConfigProvider`
> * `org.apache.kafka.common.config.provider.DirectoryConfigProvider`
> h2. Example
> * `allowed.paths` configured as `/opt/kafka/secrets`
> * Symlink present: `/opt/kafka/secrets/test -> /etc/sensitive`
> * Config reference: `${file:/opt/kafka/secrets/test:key}`
> * Validation allows `/opt/kafka/secrets/test`
> * File read follows symlink and accesses `/etc/sensitive`
> h2. Current behavior
> {code:java}
> Path normalisedPath = parsedPath.normalize();
> long allowed = 
> allowedPaths.stream().filter(normalisedPath::startsWith).count();
> {code}
> `normalize()` collapses `.` and `..` but does not resolve symlinks.
> h2. Expected behavior
> Path validation should use the resolved filesystem path (e.g. `toRealPath()`) 
> so that a symlink inside an allowed directory pointing outside that directory 
> is rejected.
> h2. Proposed fix
> {code:java}
> try {
>     Path realPath = parsedPath.toRealPath();
>     long allowed = allowedPaths.stream().filter(realPath::startsWith).count();
>     if (allowed == 0) {
>         return null;
>     }
>     return realPath;
> } catch (IOException e) {
>     return null;
> }
> {code}
> Consider also resolving allowed base paths with `toRealPath()` at 
> configuration time for consistent `startsWith` comparisons.
> h2. Test coverage
> `AllowedPathsTest` covers `..` traversal but has no test for symlink 
> resolution. Add tests for:
> * Symlink inside allowed dir pointing outside → rejected
> * Symlink inside allowed dir pointing inside → allowed
> * Direct path (no symlink) → unchanged behavior
> h2. Context
> This was reported to the security team. They noted that exploiting this 
> requires write access to the Connect worker filesystem to create symlinks, 
> and therefore do not classify it as a security issue. They agreed the 
> behavior is not intuitive and welcomed a community PR to improve it.
> h2. References
> * 
> `clients/src/main/java/org/apache/kafka/common/config/internals/AllowedPaths.java`
> * 
> `clients/src/test/java/org/apache/kafka/common/config/provider/AllowedPathsTest.java`
> * Related hardening: CVE-2024-31141 (`allowed.paths`)



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to