定时任务(默认用户root)与 Web(用户www)权限冲突问题

在 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 直接生产写文件