fix(entrypoint): root never follows customer symlinks under /home/$user
/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>
This commit is contained in:
1 parent
eaf616eb30
commit
0ffccac4b8
3 files changed
+143
-54
No files matched your search
+20
-24
@@ -10,8 +10,11 @@ fi
|
||||
|
||||
adduser -u $uid $user
|
||||
|
||||
mkdir -p /home/$user/public_html
|
||||
mkdir -p /home/$user/logs/{apache,php-fpm}
|
||||
# Root never follows customer symlinks under /home/$user: see safe-paths.sh.
|
||||
source /scripts/safe-paths.sh
|
||||
secure_home
|
||||
|
||||
user_mkdir /home/$user/public_html /home/$user/logs/apache /home/$user/logs/php-fpm
|
||||
|
||||
mv /var/log/httpd /var/log/httpd.bak
|
||||
|
||||
@@ -36,6 +39,7 @@ fi
|
||||
|
||||
/usr/sbin/httpd -k start
|
||||
/usr/sbin/php-fpm -y /etc/php-fpm.conf
|
||||
# GNU chown -R defaults to -P: symlinks are lchown()ed, never followed.
|
||||
chown -R $user:$user /home/$user
|
||||
# Do NOT `chmod -R 755` the docroot here. /home/$user is the customer's
|
||||
# bind-mounted site directory; a recursive 755 on every start made every file
|
||||
@@ -45,13 +49,13 @@ chown -R $user:$user /home/$user
|
||||
# traverse directories to serve static files. Normalise that and nothing else:
|
||||
# open the docroot itself, and add o+x to directories that lack it. Regular
|
||||
# files are never touched, so execute is never added and wp-config.php is
|
||||
# never loosened.
|
||||
chmod 755 /home/$user
|
||||
find /home/$user -type d ! -perm -o=x -exec chmod o+x {} +
|
||||
# never loosened. Runs as the customer (safe-paths.sh) so a symlink can only
|
||||
# reach what the customer already owns.
|
||||
open_dir_traversal
|
||||
|
||||
if [[ $environment == 'DEV' ]]; then
|
||||
echo "Starting Dev Deployment"
|
||||
mkdir -p /home/$user/_db_backups
|
||||
user_mkdir /home/$user/_db_backups
|
||||
# Ensure microdnf is available for installing MariaDB and memcached in DEV mode
|
||||
if ! command -v microdnf &> /dev/null; then
|
||||
echo "microdnf not found, installing with dnf..."
|
||||
@@ -70,14 +74,13 @@ if [[ $environment == 'DEV' ]]; then
|
||||
mysql -e "CREATE USER '"$mysql_user"'@'localhost' IDENTIFIED BY '"$mysql_password"';"
|
||||
mysql -e "GRANT ALL PRIVILEGES ON *.* TO '"$mysql_user"'@'localhost' WITH GRANT OPTION;"
|
||||
mysql -e "FLUSH PRIVILEGES;"
|
||||
# Create user crontab with MySQL backup job
|
||||
echo "# User crontab for $user" > /home/$user/crontab
|
||||
echo "*/15 * * * * /scripts/mysql-backup.sh $user devdb_$mysql_db" >> /home/$user/crontab
|
||||
chown $user:$user /home/$user/crontab
|
||||
echo "MySQL User: "$mysql_user > /home/$user/mysql_creds
|
||||
echo "MySQL Password: "$mysql_password >> /home/$user/mysql_creds
|
||||
echo "MySQL Database: devdb_"$mysql_db >> /home/$user/mysql_creds
|
||||
cat /home/$user/mysql_creds
|
||||
# Create user crontab with MySQL backup job. Written AS THE CUSTOMER
|
||||
# (as_user tee), never by root through a customer path.
|
||||
drop_symlink /home/$user/crontab
|
||||
printf '%s\n' "# User crontab for $user" "*/15 * * * * /scripts/mysql-backup.sh $user devdb_$mysql_db" \
|
||||
| as_user tee /home/$user/crontab >/dev/null
|
||||
printf '%s\n' "MySQL User: $mysql_user" "MySQL Password: $mysql_password" "MySQL Database: devdb_$mysql_db" \
|
||||
| as_user tee /home/$user/mysql_creds
|
||||
|
||||
fi
|
||||
/usr/bin/memcached -d -u $user
|
||||
@@ -88,16 +91,9 @@ if [[ $environment == 'PROD' ]]; then
|
||||
sed -r -i 's/;session.save_path="localhost:11211/session.save_path="memcache:11211/' /etc/php.d/50-memcached.ini
|
||||
fi
|
||||
|
||||
# Set up user crontab
|
||||
if [ ! -f /home/$user/crontab ]; then
|
||||
echo "# User crontab for $user" > /home/$user/crontab
|
||||
echo "# Add your cron jobs here" >> /home/$user/crontab
|
||||
echo "# Example: */5 * * * * /home/$user/scripts/my-script.sh" >> /home/$user/crontab
|
||||
chown $user:$user /home/$user/crontab
|
||||
fi
|
||||
|
||||
# Load user crontab
|
||||
crontab -u $user /home/$user/crontab
|
||||
# Set up + load the user crontab, created and read as the customer
|
||||
# (safe-paths.sh). Root reading/writing/chowning it followed symlinks.
|
||||
load_user_crontab
|
||||
|
||||
/usr/sbin/crond
|
||||
|
||||
|
||||
Reference in new issue
Block a user