The trad solution is to have user writeable areas (home, vartmp, tmp) on different volumes. Some tools have options to not traverse symlinks across volumes for this and other reasons. But on modern systems you are protected by the fs.protected_hardlinks setting.
> POSIX guarantees that hard links can exist. It follows that, on POSIX systems without any non-standard protections, it's unsafe for anyone (but in particular, root) to do anything sensitive in a directory that is writable by another user. Cross-platform programs designed to do so are simply flawed.
I feel like something had to have been lost in translation from Unix to POSIX here. In the original implementation, were users able to hardlink (well, "link") files owned by others? If so, this is a foundational flaw that should probably be fixed across the board with something similar to Linux's non-standard sysctl option.
[1]: http://michael.orlitzky.com/articles/posix_hardlink_heartach...
Hard links are particularly useful for having multiple copies of a directory tree without consuming space for the files. Eg to create an overlay of a build tree, with your changes on top. Unfortunately CoW file systems still have many issues that make this difficult.
https://sysctl-explorer.net/fs/protected_hardlinks/
http://michael.orlitzky.com/articles/posix_hardlink_heartach...
https://github.com/torvalds/linux/commit/800179c9b8a1e796e44...
Without this, a whole lot of attacks are possible with hardlinks.
Hint: it's stat(2) (or equivalent in your language library)
Additional hint: if using pythons os.stat set follow_symlinks to false. It recently took me an embarrassing long time to figure out why my script was failing to find symlinks.
to expand on that:
"In computing, a symbolic link (also symlink or soft link) is a file whose purpose is to point to a file or directory (called the "target") by specifying a path thereto."
I hope somebody is working on it because as things are going in the last years, I'd be retired before I have the time for it.
The problem here is trying to cross a security boundary where your only tool is shell scripting. That’s just basically impossible to do securely.
Use a real programming language, follow the rules required to make it secure and do all the checks you need to.
Though that would require much more than just ACID semantics but also proper user / jail isolation.
(cd "$path" && [ "$(pwd -P)" = "$path" ] && chown -R buildkite-agent:buildkite-agent .)
the real question though is why they're trusting just Docker alone to isolate customers; if they want the jobs to effectively be a single user to the system, they can even use unprivileged user namespaces?There are radically better isolation strategies now. Firecracker and/or Sysbox hardened docker containers is one I’ve recently implemented.
https://www.kylheku.com/cgit/safepath/about/
safepath is a function which tries to analyze whether a path is safe to use. Roughly that means that it doesn't resolve in some way that can be controlled by another (non-root) user.
A something similar to this is in TXR Lisp under the name path-components-safe:
https://www.kylheku.com/cgit/txr/tree/stdlib/path-test.tl?hl...
Tho, to be fair, it's probably an improvement by narrowing how simple it is to exploit relative to doing nothing.
But, assuming it does what it's supposed to, the inherent semantics of the check is supposed to eliminate TOCtoTOU.
The reason is that if we validate, from left to right, that no part of the path is under the control of adversaries, then nothing can happen to that path between the time of check and time of use.
(At least nothing that isn't self-inflicted, which would be outside of the scope. If root checks a path for safety, but root itself has a parallel task going on which changes the permissions or symbolic links in ways that affect the security status of that path, we can regard that as root's self-inflicted problem. That's nearly the same as the ever-present threat that a careless sysadmin might "chmod 777" an sensitive file.)
In fact, development of this function was motivated by exploring the question: can we do security checks on a path in such a way that if the answer is affirmative, it continues to tell the truth moments later, when the same path is accessed?
Username space remapping wasn’t adequate, for reasons I’m a bit blurry on. I think recent kernels have some better options on remapping permissions across file systems.
should probably setuid to the correct user and do the thing there instead