在 Linux 生产环境中,一个非常经典但容易被忽略的问题是:
同一份资源(文件 / 目录 / 脚本),在 root 定时任务中运行正常,但 Web 用户(www)访问或写入失败。
这种问题通常表现为:
-
crontab 执行成功,但 Web 页面报 403 / 500
-
定时任务能生成文件,但 Web 无法读取
-
Web 上传文件成功,但定时任务无法处理
-
日志文件权限混乱、偶发性失败
本质原因只有一个:
root 与 www 之间的权限边界 + umask + ACL + 继承机制不一致
一、问题本质:root ≠ www
Linux 权限模型是:
用户(user) + 组(group) + other + ACL
但 root 有一个特殊点:
root 可以绕过权限检查(DAC bypass)
因此出现一个典型错觉:
| 场景 | 是否成功 | 原因 |
|---|---|---|
| root 创建文件 | 成功 | root 不受权限限制 |
| www 访问文件 | 失败 | 权限不匹配 |
| root cron 写日志 | 成功 | root 写入 |
| nginx/php-fpm 读取 | 失败 | www 无权限 |
二、最常见冲突场景
1. root crontab 创建文件
* * * * * /root/script.sh
脚本内容:
echo "hello" > /data/log/app.log
结果:
-rw-r--r-- 1 root root 10 /data/log/app.log
Web 用户 www 无法写入或追加。
2. Web 进程写文件失败
典型报错:
Permission denied: /data/upload/file.tmp
原因:
-
文件属主 root
-
www 没写权限
3. 混合场景(最隐蔽)
root cron:
生成缓存文件
Web:
读取缓存文件
结果:
-
偶发 403
-
或 PHP Warning / Java IOException
三、核心根因分析
1. umask 差异(最常见)
root crontab 默认:
umask 022
www(nginx/php-fpm)可能:
umask 002 或 007
结果差异:
| umask | 文件权限 |
|---|---|
| 022 | 644 |
| 002 | 664 |
| 007 | 660 |
👉 root 创建文件通常 不够"共享友好"
2. 属主不一致
root 创建:
root:root
www 需要:
www:www
3. 目录权限缺失 x 位
典型问题:
drwxrw---- root:www /data
www 无法进入目录(缺少 x)
4. ACL 未统一
你可能看到:
getfacl /data
类似:
user:www:rwx
mask::rwx
但子目录没有继承 ACL。
5. systemd / crontab 环境差异
cron:
PATH 很短
HOME=/root
Web:
PATH 不同
SELinux/AppArmor 约束
四、典型问题复现(真实生产案例)
案例:日志写入失败
root cron:
echo "$(date)" >> /data/log/app.log
Web:
tail -f /data/log/app.log
Permission denied
排查:
ls -l /data/log
-rw-r--r-- 1 root root app.log
五、系统级解决方案(推荐优先级排序)
方案一:统一用户(最佳实践)
做法
让 cron 不再使用 root:
crontab -u www -e
或者:
www 用户执行定时任务
优点:
-
权限一致
-
最干净
方案二:统一属主 + 组
chown -R www:www /data
并确保目录:
chmod -R 775 /data
方案三:强制 umask(关键)
在 cron 中:
umask 002
或者脚本开头:
#!/bin/bash
umask 002
方案四:ACL 精细控制(生产推荐)
setfacl -m u:www:rwx /data
setfacl -R -m u:www:rwx /data
setfacl -d -m u:www:rwx /data
检查:
getfacl /data
方案五:group 共享模型(推荐架构)
创建统一组:
groupadd app
usermod -aG app www
usermod -aG app root
目录:
chown -R root:app /data
chmod -R 2775 /data
重点:
2 = setgid(继承组)
方案六:setgid 目录(非常关键)
chmod g+s /data/log
效果:
新文件自动继承 group = app
六、推荐生产级标准方案(组合拳)
推荐结构
用户:www(Web)
定时任务:root(仅调度)
共享目录:/data
组:app
标准配置
1. 目录权限
chown -R root:app /data
chmod -R 2775 /data
2. cron 设置
umask 002
3. ACL 兜底
setfacl -R -m u:www:rwx /data
setfacl -R -m u:root:rwx /data
4. setgid 继承
chmod g+s /data
七、排查命令清单(非常实用)
1. 权限链检查
namei -l /data/log/app.log
2. ACL 检查
getfacl -R /data
3. umask 检查
umask
4. 运行用户确认
ps -ef | grep nginx
ps -ef | grep php-fpm
5. cron 环境
cat /etc/crontab
八、总结(核心原则)
权限冲突的本质不是 bug,而是设计问题:
不同执行上下文(root / www)必须共享"统一权限模型"
关键抓手:
-
✔ 统一属组(app group)
-
✔ setgid 目录继承
-
✔ umask 标准化
-
✔ ACL 兜底
-
✔ 避免 root 直接生产写文件