drop_symlink and the logs/ symlink sweep now unlink AS THE CUSTOMER, and
user_touch no longer falls back to a root chown -h (a parent dir swapped for a
symlink between check and use could still redirect a root path operation).
A root-owned leftover error_log is fixed by the existing chown -R, which never
follows symlinks. Every operation these helpers perform is now safe even if the
customer races it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/home/$user is the customer's bind mount; they can plant symlinks in it. The
entrypoints ran as root and wrote/chowned through customer paths, so e.g.
logs/php-fpm/error.log -> /etc/shadow (or a dangling crontab -> /etc/...)
handed the customer that file, i.e. container root. New scripts/safe-paths.sh,
used by entrypoint.sh and entrypoint-fpm.sh:
- secure_home: refuse to start if /home/$user is not a real dir; logs/ must be
a real dir and every symlink under logs/ is removed and logged (apache and
php-fpm open those files as root at start).
- dirs, the pool error_log and the default crontab are created AS THE CUSTOMER
(setpriv, runuser fallback); a pre-existing root-owned error_log is fixed
with chown -h (never dereferences).
- the user crontab is read as the customer and piped to `crontab -u $user -`
(root reading it could install any root-readable file where the customer
can `crontab -l` it). DEV-mode crontab/mysql_creds are written via as_user tee.
- the o+x directory pass (find -type d, no -L) runs as the customer, so a dir
swapped for a symlink before chmod can only reach what the customer owns.
- chown -R is unchanged: GNU default -P lchown()s symlinks, never follows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>