381 lines
14 KiB
Bash
381 lines
14 KiB
Bash
#!/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'
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head><title>Permission Test</title></head>
|
||
<body><h1>Web Server Permission Test File</h1></body>
|
||
</html>
|
||
EOF
|
||
# 设置正确的权限
|
||
chown $(whoami):"$WEB_GROUP" "$test_file" 2>/dev/null || true
|
||
chmod 664 "$test_file"
|
||
fi
|
||
|
||
# 执行权限测试
|
||
if [ -f "$test_file" ]; then
|
||
echo "使用测试文件: $test_file"
|
||
echo "文件权限: $(stat -c '%a %n' \"$test_file\")"
|
||
echo "文件所有者: $(stat -c '%U:%G' \"$test_file\")"
|
||
|
||
# 测试所有Web用户的权限(通过组权限)
|
||
for test_user in "www-data" "www" "apache" "nginx"; do
|
||
if id "$test_user" &>/dev/null; then
|
||
echo "🔍 测试 $test_user 用户权限(通过组权限):"
|
||
|
||
# 显示用户组信息(安全的变量处理)
|
||
user_groups=$(id -Gn "$test_user" 2>/dev/null | tr '\n' ' ' | sed 's/ *$//')
|
||
echo " 📋 所属组: ${user_groups:-无}"
|
||
|
||
# 测试读权限(安全:只读不修改)
|
||
if su -s /bin/sh -c "cat '$test_file' >/dev/null 2>&1" "$test_user" 2>/dev/null; then
|
||
echo " ✅ 读权限: 通过组权限可读"
|
||
else
|
||
echo " ❌ 读权限: 不可读"
|
||
fi
|
||
|
||
# 测试写权限(使用临时文件,避免污染原文件)
|
||
temp_test_file="${test_file}.write_test_${test_user}"
|
||
if su -s /bin/sh -c "echo 'permission_test' > '$temp_test_file' 2>/dev/null" "$test_user" 2>/dev/null; then
|
||
echo " ✅ 写权限: 通过组权限可写"
|
||
rm -f "$temp_test_file" 2>/dev/null
|
||
else
|
||
echo " ❌ 写权限: 不可写"
|
||
fi
|
||
|
||
# 测试目录创建权限(使用临时目录)
|
||
temp_test_dir="${APP_ROOT}/.perm_test_${test_user}"
|
||
if su -s /bin/sh -c "mkdir -p '$temp_test_dir' 2>/dev/null" "$test_user" 2>/dev/null; then
|
||
echo " ✅ 创建目录: 通过组权限可创建"
|
||
rm -rf "$temp_test_dir" 2>/dev/null
|
||
else
|
||
echo " ❌ 创建目录: 不可创建"
|
||
fi
|
||
|
||
echo " 🔗 权限来源: 统一Web组 ($WEB_GROUP) 775权限"
|
||
break # 只测试第一个可用的用户即可验证效果
|
||
fi
|
||
done
|
||
|
||
# 清理专用测试文件(如果是创建的)
|
||
if echo "$test_file" | grep -q "\.permission_test\.html$"; then
|
||
rm -f "$test_file" 2>/dev/null
|
||
echo "🧹 已清理临时测试文件"
|
||
fi
|
||
else
|
||
echo "❌ 无法找到或创建测试文件,跳过权限验证"
|
||
fi
|
||
|
||
# 显示统一组和用户状态
|
||
echo "=== 统一Web组状态检查 ==="
|
||
if getent group "$WEB_GROUP" &>/dev/null; then
|
||
echo "✅ 统一Web组 '$WEB_GROUP' 存在"
|
||
echo "组信息: $(getent group "$WEB_GROUP" 2>/dev/null || echo '获取失败')"
|
||
|
||
# 检查哪些用户在统一组中
|
||
echo "统一组成员检查:"
|
||
for web_user in "www-data" "www" "apache" "nginx"; do
|
||
if id "$web_user" &>/dev/null; then
|
||
if id -Gn "$web_user" | grep -q "$WEB_GROUP"; then
|
||
echo "✅ $web_user 在统一组 '$WEB_GROUP' 中"
|
||
else
|
||
echo "❌ $web_user 不在统一组 '$WEB_GROUP' 中"
|
||
fi
|
||
else
|
||
echo "❌ $web_user 用户不存在"
|
||
fi
|
||
done
|
||
else
|
||
echo "❌ 统一Web组 '$WEB_GROUP' 不存在"
|
||
fi
|
||
fi
|
||
|
||
echo "=== 启动应用 ==="
|
||
|
||
# 执行原有的启动命令
|
||
# 移除第一个参数(应用根目录),只传递后续参数给应用
|
||
if [ $# -gt 0 ]; then
|
||
# 如果有应用根目录参数,传递剩余参数
|
||
shift 1
|
||
exec "$@"
|
||
else
|
||
# 没有额外参数,正常执行
|
||
exec "$@"
|
||
fi |