#!/bin/bash # 移除 set -e 以便更好的错误控制 # 显示帮助信息 show_help() { cat << EOF === Web应用权限初始化脚本 === 用法: $0 [--help|-h] 参数: --help, -h 显示此帮助信息 优先级: 1. 环境变量 PHP_APP_ROOT 2. 默认值 /var/www/html 示例: $0 # 使用环境变量或默认值 PHP_APP_ROOT=/custom $0 /var/www # 参数优先,忽略环境变量 EOF } echo "=== Web应用权限初始化 ===" # 默认值 APP_ROOT="/var/www/html" SOURCE="默认值" if [ -n "${PHP_APP_ROOT}" ]; then # 其次使用环境变量 APP_ROOT="${PHP_APP_ROOT}" SOURCE="环境变量 PHP_APP_ROOT" fi echo "使用应用根目录: $APP_ROOT (来源: $SOURCE)" # 验证应用根目录是否有效 if [ ! -d "$APP_ROOT" ]; then echo "⚠️ 警告: 应用根目录 '$APP_ROOT' 不存在, 程序退出" exit 1 fi # 创建统一的Web组并配置所有用户(最高效的权限管理) configure_web_users() { # 常见Web服务器用户列表 WEB_USERS=("www-data" "www" "apache" "nginx") # 获取环境变量中的用户ID TARGET_UID=${USER_ID:-33} TARGET_GID=${GROUP_ID:-33} echo "配置统一Web组权限,目标UID:GID = $TARGET_UID:$TARGET_GID" # 创建统一的Web组(增强错误处理) WEB_GROUP="webaccess" if ! getent group "$WEB_GROUP" &>/dev/null; then echo "创建统一Web组: $WEB_GROUP" # 尝试使用指定GID创建组 if groupadd -g $TARGET_GID "$WEB_GROUP" 2>/dev/null; then echo "✅ 统一Web组创建成功,GID: $TARGET_GID" else echo "⚠️ GID $TARGET_GID 已被占用,尝试自动分配GID" # 尝试不指定GID创建组 if groupadd "$WEB_GROUP" 2>/dev/null; then ACTUAL_GID=$(getent group "$WEB_GROUP" | cut -d: -f3) echo "✅ 统一Web组创建成功,自动分配GID: $ACTUAL_GID" else echo "❌ 创建 $WEB_GROUP 组失败,尝试使用备用方案" # 备用方案:使用现有的www-data组 if getent group "www-data" &>/dev/null; then WEB_GROUP="www-data" echo "🔄 使用现有的www-data组作为统一组" else echo "❌ 备用方案也失败,权限配置可能不完整" WEB_GROUP="" fi fi fi else ACTUAL_GID=$(getent group "$WEB_GROUP" | cut -d: -f3) echo "✅ 统一Web组 $WEB_GROUP 已存在,GID: $ACTUAL_GID" fi # 最终验证组是否存在 if [ -z "$WEB_GROUP" ] || ! getent group "$WEB_GROUP" &>/dev/null; then echo "❌ 无法创建或找到可用的Web组,权限配置将受限" return 1 fi # 只将已存在的Web用户加入统一组(增强错误处理) success_count=0 total_users=0 for web_user in "${WEB_USERS[@]}"; do total_users=$((total_users + 1)) if id "$web_user" &>/dev/null; then echo "📝 处理Web用户: $web_user" # 获取用户当前组信息(安全的变量处理) current_groups=$(id -Gn "$web_user" 2>/dev/null | tr '\n' ' ' | sed 's/ *$//') echo " 当前所属组: ${current_groups:-无}" # 尝试将用户加入统一组(使用-a参数保留现有组,只添加新组) if usermod -a -G "$WEB_GROUP" "$web_user" 2>/dev/null; then echo " ✅ 成功将 $web_user 添加到统一组 $WEB_GROUP" success_count=$((success_count + 1)) else echo " ⚠️ 无法将 $web_user 添加到统一组,尝试设置主组" # 备用方案:设置主组 if usermod -g "$WEB_GROUP" "$web_user" 2>/dev/null; then echo " ✅ 成功将 $web_user 主组设置为 $WEB_GROUP" success_count=$((success_count + 1)) else echo " ❌ 无法配置 $web_user 的组权限" fi fi else echo "⚭ Web用户 $web_user 不存在,跳过" fi done echo "📊 用户配置汇总: $success_count/$total_users 个Web用户配置成功" # 至少要有一个用户配置成功 if [ $success_count -eq 0 ]; then echo "⚠️ 没有Web用户被成功配置,但继续执行" fi echo "统一Web组配置完成" } # 错误处理:如果配置失败,不要终止整个脚本 configure_web_users || echo "⚠️ Web用户配置出现问题,但继续执行权限设置" echo "当前用户: $(whoami)" echo "UID: $(id -u), GID: $(id -g)" # 修复所有目录权限(使用统一Web组,最高效的权限管理) if [ -d "$APP_ROOT" ]; then # 重新获取最终的WEB_GROUP(可能已被修改) FINAL_WEB_GROUP="" # 首选:使用创建的webaccess组 if getent group "webaccess" &>/dev/null; then FINAL_WEB_GROUP="webaccess" echo "🎯 使用创建的统一Web组: $FINAL_WEB_GROUP" # 备选:使用www-data组 elif getent group "www-data" &>/dev/null; then FINAL_WEB_GROUP="www-data" echo "🔄 回退到www-data组: $FINAL_WEB_GROUP" # 最后:使用当前用户的组 else CURRENT_USER=$(whoami) CURRENT_GROUP=$(id -gn "$CURRENT_USER") FINAL_WEB_GROUP="$CURRENT_GROUP" echo "🔧 使用当前用户组: $FINAL_WEB_GROUP" fi # 最终验证 if [ -z "$FINAL_WEB_GROUP" ]; then echo "❌ 无法确定有效的Web组,跳过权限设置" echo "=== 启动应用 ===" exec "$@" fi WEB_GROUP="$FINAL_WEB_GROUP" WEB_GROUP_GID=$(getent group "$WEB_GROUP" | cut -d: -f3) echo "✅ 最终使用Web组: $WEB_GROUP (GID: $WEB_GROUP_GID)" echo "🔒 统一组权限模式:所有Web用户通过组继承权限" # 设置所有权为统一Web组(增强错误处理) echo "📁 设置应用目录所有权为统一Web组" CURRENT_USER=$(whoami) if chown -R $CURRENT_USER:$WEB_GROUP "$APP_ROOT" 2>/dev/null; then echo "✅ 所有权设置成功: $CURRENT_USER:$WEB_GROUP" else echo "⚠️ 所有权设置失败,尝试只设置组权限" chgrp -R "$WEB_GROUP" "$APP_ROOT" 2>/dev/null || echo "❌ 组权限设置也失败" fi # 设置目录权限为775(组权限为rwx,所有组内用户都有完整权限) echo "🔐 设置目录权限775,文件权限664" # 使用更安全的权限设置方式,避免权限被拒绝 dir_count=0 file_count=0 # 设置目录权限(兼容性更好的方式) if command -v find >/dev/null 2>&1; then dir_count=$(find "$APP_ROOT" -type d -exec chmod 775 {} \; 2>/dev/null | wc -l) file_count=$(find "$APP_ROOT" -type f -exec chmod 664 {} \; 2>/dev/null | wc -l) find "$APP_ROOT" -type d -exec chmod g+s {} \; 2>/dev/null else # 备用方案:使用简单的循环 echo "find命令不可用,跳过批量权限设置" dir_count=0 file_count=0 fi echo "📊 权限设置完成: $dir_count个目录, $file_count个文件" echo "✅ 统一组权限设置完成,所有Web用户通过组获得权限" # 设置ACL(如果支持,只需设置统一组) if command -v setfacl >/dev/null 2>&1; then echo "🔒 设置ACL权限(只需设置统一Web组)" acl_success=0 # 只为统一Web组设置ACL权限(限制处理深度) if setfacl -R -m g:$WEB_GROUP:rwx "$APP_ROOT" 2>/dev/null; then echo " ✅ 设置组ACL权限成功" acl_success=$((acl_success + 1)) else echo " ❌ 设置组ACL权限失败" fi # 设置默认ACL权限(新创建的文件自动继承权限) if setfacl -dR -m g:$WEB_GROUP:rwx "$APP_ROOT" 2>/dev/null; then echo " ✅ 设置默认ACL权限成功" acl_success=$((acl_success + 1)) else echo " ❌ 设置默认ACL权限失败" fi if [ $acl_success -eq 2 ]; then echo "🎉 统一组ACL设置完成,所有组内用户自动获得权限" elif [ $acl_success -eq 1 ]; then echo "⚠️ ACL部分设置成功,建议检查文件系统ACL支持" else echo "❌ ACL设置完全失败,文件系统可能不支持ACL" fi else echo "ℹ️ ACL不支持,依赖传统权限模式" echo "✅ 775权限已足够,所有组内用户都有rwx权限" fi # 设置umask umask 0002 echo "✅ 应用目录权限修复完成" # 验证文件权限是否足够(测试统一组权限效果) echo "=== 验证统一组权限效果 ===" # 查找测试文件的更可靠方法 test_file="" # 方法1: 查找index.html if [ -f "$APP_ROOT/index.html" ]; then test_file="$APP_ROOT/index.html" fi # 方法2: 查找任意HTML文件(更安全的方式) if [ -z "$test_file" ]; then first_html=$(find "$APP_ROOT" -maxdepth 2 -name "*.html" -type f 2>/dev/null | head -1) if [ -n "$first_html" ] && [ -f "$first_html" ]; then test_file="$first_html" fi fi # 方法3: 查找index.php if [ -z "$test_file" ] && [ -f "$APP_ROOT/index.php" ]; then test_file="$APP_ROOT/index.php" fi # 方法4: 创建专用测试文件(最安全的选择) if [ -z "$test_file" ]; then test_file="$APP_ROOT/.permission_test.html" echo "创建专用权限测试文件" cat > "$test_file" << 'EOF'