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.
Our use case is whitelisting which host paths can be mounted into Docker containers. To avoid symlink workarounds, we use the
Resolvedfield, as recommended in the README, but fall back toSourceifResolvedis empty. This works fine, except when mounting a path that contains a symlink and a non-existent subfolder: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.EvalSymlinksfails when it tries to runos.Lstaton/etc/nonexistentbecause it doesn't exist. By comparison, Python'spathlib.Pathwill simply return the resolved path/etc/nonexistentas expected.A possible solution/workaround is to catch the PathError and return whatever it resolved (ugly code):
However, to me this relies a bit too much on the internals of EvalSymlinks.