#!/usr/bin/env bash # setperms.sh # Run from anywhere — WEB_ROOT is absolute # Handles both WordPress sites and custom PHP apps set -euo pipefail # ====== EDIT THESE ====== WEB_ROOT="/mnt/data/www" ADMIN_USER="ubuntu" ADMIN_GROUP="ubuntu" WEB_USER="www-data" WEB_GROUP="www-data" DRY_RUN=false # set true to preview without changes # ======================== run() { $DRY_RUN && echo "[DRY] $*" || eval "$*"; } # ───────────────────────────────────────────── # Custom PHP apps and their writable dirs # Each value is a space-separated list of dirs # relative to the app root # ───────────────────────────────────────────── declare -A CUSTOM_APP_WRITABLE CUSTOM_APP_WRITABLE["grailheart.com"]="CA IM __ de en es fr la pt public/uploads public/cache" CUSTOM_APP_WRITABLE["xyzzy31416.com"]="public/uploads public/cache storage tmp" # ───────────────────────────────────────────── # SECTION 1: WordPress sites # ───────────────────────────────────────────── echo "========================================" echo "Processing WordPress sites..." echo "========================================" mapfile -t WP_SITES < <(find "$WEB_ROOT" -maxdepth 3 -type f -name wp-config.php -printf '%h\n' | sort -u) if [[ ${#WP_SITES[@]} -eq 0 ]]; then echo "No WordPress sites found." else for WP in "${WP_SITES[@]}"; do echo "----" echo "WordPress: $WP" CONTENT="$WP/wp-content" UPLOADS="$CONTENT/uploads" CACHE="$CONTENT/cache" UPGRADE="$CONTENT/upgrade" PLUGINS="$CONTENT/plugins" THEMES="$CONTENT/themes" WC_LOGS="$UPLOADS/wc-logs" WC_DOWNLOADS="$UPLOADS/wc-downloads" WC_UPLOADS="$UPLOADS/woocommerce_uploads" run "mkdir -p '$UPLOADS' '$CACHE' '$UPGRADE' '$PLUGINS' '$THEMES' '$WC_LOGS' '$WC_DOWNLOADS' '$WC_UPLOADS'" # Baseline: ubuntu owns code, dirs 755, files 644 run "chown -R '$ADMIN_USER:$ADMIN_GROUP' '$WP'" run "find '$WP' -type d -exec chmod 755 {} +" run "find '$WP' -type f -exec chmod 644 {} +" # Sensitive files [[ -f "$WP/wp-config.php" ]] && run "chmod 640 '$WP/wp-config.php'" [[ -f "$WP/.htaccess" ]] && run "chmod 644 '$WP/.htaccess'" # wp-content: web-owned, writable run "chown '$WEB_USER:$WEB_GROUP' '$CONTENT' || true" run "chmod 775 '$CONTENT' || true" # Core code dirs for CODE in "$WP/wp-admin" "$WP/wp-includes"; do [[ -d "$CODE" ]] && run "chown -R '$WEB_USER:$WEB_GROUP' '$CODE'" done # Writable runtime areas for WDIR in "$UPLOADS" "$CACHE" "$UPGRADE" "$PLUGINS" "$THEMES" \ "$WC_LOGS" "$WC_DOWNLOADS" "$WC_UPLOADS"; do run "chown -R '$WEB_USER:$WEB_GROUP' '$WDIR'" run "find '$WDIR' -type d -exec chmod 775 {} +" run "find '$WDIR' -type f -exec chmod 664 {} +" done # Setgid on writable dirs run "chmod g+s '$CONTENT' '$UPLOADS' '$CACHE' '$UPGRADE' '$PLUGINS' '$THEMES' || true" # PHP files never executable run "find '$WP' -type f -name '*.php' -exec chmod 644 {} +" echo "Done: $WP" done fi # ───────────────────────────────────────────── # SECTION 2: Custom PHP apps # ───────────────────────────────────────────── echo "" echo "========================================" echo "Processing custom PHP apps..." echo "========================================" for APP in "${!CUSTOM_APP_WRITABLE[@]}"; do APP_PATH="$WEB_ROOT/$APP" if [[ ! -d "$APP_PATH" ]]; then echo "Skipping $APP — directory not found" continue fi echo "----" echo "Custom app: $APP_PATH" # Baseline: ubuntu owns everything, dirs 755, files 644 run "chown -R '$ADMIN_USER:$ADMIN_GROUP' '$APP_PATH'" run "find '$APP_PATH' -type d -exec chmod 755 {} +" run "find '$APP_PATH' -type f -exec chmod 644 {} +" # PHP files never executable run "find '$APP_PATH' -type f -name '*.php' -exec chmod 644 {} +" # Writable dirs for www-data (only if they exist) for WDIR in ${CUSTOM_APP_WRITABLE[$APP]}; do FULL="$APP_PATH/$WDIR" if [[ -d "$FULL" ]]; then run "chown -R '$WEB_USER:$WEB_GROUP' '$FULL'" run "find '$FULL' -type d -exec chmod 775 {} +" run "find '$FULL' -type f -exec chmod 664 {} +" run "chmod g+s '$FULL'" echo " Writable: $WDIR" fi done echo "Done: $APP" done # ───────────────────────────────────────────── # SECTION 3: Shared .env # ───────────────────────────────────────────── echo "" echo "========================================" echo "Securing shared .env..." echo "========================================" SHARED_ENV="$WEB_ROOT/.env" if [[ -f "$SHARED_ENV" ]]; then run "chown '$ADMIN_USER:$WEB_GROUP' '$SHARED_ENV'" run "chmod 640 '$SHARED_ENV'" echo "Done: $SHARED_ENV" else echo "No shared .env found at $SHARED_ENV — skipping" fi echo "" echo "========================================" echo "setperms complete." echo "========================================"