Skip to content

Symlink resolution fails for non-existent subfolders #122

Description

@sandro-h

Our use case is whitelisting which host paths can be mounted into Docker containers. To avoid symlink workarounds, we use the Resolved field, as recommended in the README, but fall back to Source if Resolved is empty. This works fine, except when mounting a path that contains a symlink and a non-existent subfolder:

# Is blocked by opa-docker-authz because our policy file prevents mounting anything under /etc:
docker run --rm -it -v /etc:/work busybox

# Trying to work around it with a symlink. Is also blocked as expected:
ln -s /etc etc
docker run --rm -it -v $(pwd)/etc:/work busybox

# Is NOT blocked, letting me create a folder and other content under /etc/nonexistent:
docker run --rm -it -v $(pwd)/etc/nonexistent:/work busybox

While this perhaps has limited security implications, since I can only create a new folder and not read any existing content in /etc, it could still be problematic in some edge cases.

The underlying issue is that filepath.EvalSymlinks fails when it tries to run os.Lstat on /etc/nonexistent because it doesn't exist. By comparison, Python's pathlib.Path will simply return the resolved path /etc/nonexistent as expected.

A possible solution/workaround is to catch the PathError and return whatever it resolved (ugly code):

resolved, err := filepath.EvalSymlinks(bindMount.Source)
if err == nil {
	resolved = filepath.Clean(resolved)
	result[idx].Resolved = resolved
} else if pathErr, ok := err.(*os.PathError); ok && pathErr.Err == syscall.ENOENT {
	resolved = filepath.Clean(pathErr.Path)
	result[idx].Resolved = resolved
}

However, to me this relies a bit too much on the internals of EvalSymlinks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions