文章总结: 本文深入分析Linux文件权限管理,重点揭示chmod777命令的安全风险,包括权限失控、审计失效和攻击面扩大等问题。文档系统讲解权限位三元组、特殊位、ACL、文件属性、umask及SELinux等核心机制,提供从基础权限到高级控制的完整知识体系。针对运维实际场景,给出精细化权限配置方案和排查流程,强调最小权限原则与安全替代方案。 综合评分: 85 文章分类: 安全建设,解决方案,技术标准,安全意识,运维经验
Linux 文件权限详解:为什么 chmod 777 是运维大忌?
点击关注 👉 点击关注 👉
马哥Linux运维
2026年6月26日 18:07 广东
在小说阅读器读本章
去阅读
Linux 文件权限详解:为什么 chmod 777 是运维大忌?
一、问题背景
线上排查时经常碰到这种情况:刚入职的小同事上来就一句 chmod 777 /data/app -R,直接把整个应用目录放开;或者日志目录权限混乱、文件莫名丢失、某个脚本突然不能执行。事后排查日志,所有用户都能写,自然找不到”是谁干的”。这就是典型的”用 777 治百病,最后百病都治不了”。
权限管理是 Linux 运维最基本的内功之一,但大多数初中级运维只记住了 chmod 777 和 chown -R,对于 setuid、setgid、sticky bit、ACL、SELinux/AppArmor、umask、文件属性这些机制,要么没用过,要么不会用。导致排查一个权限问题,要从最粗的 chmod 777 试到最细的 SELinux 策略,中间走了无数弯路。
为什么会出现 777?大致是这几类场景:
- 新人不懂权限,看到 Permission denied 就 777
- “临时给一下”,结果忘了收回来
- 共享目录没想清楚授权模型,先 777 跑通再说
- 容器、CI 流水线里固化写死 777 的脚本
- 调试某段代码时放开权限,发布时忘了改回去
但 777 真的会”治好”问题吗?事实上:
- 它把”权限错误”变成了”任何人可改”,看起来不报错,但是潜在的事故
- 它让日志审计、文件溯源彻底失效
- 它给攻击者横向越权的机会
- 它容易触发合规检查失败
这一篇不讲空话,全部围绕”现象 → 命令 → 输出 → 判断 → 修复 → 验证 → 回滚”展开,把 chmod 777 真正的问题讲透,并把替代方案落到命令上。
二、适用场景
- 服务器上某目录需要给多个用户/服务共同读写,但被错配成 777
- 排查”为什么我的文件被别人误删了”
- 排查”为什么某个脚本不能执行”
- 排查”为什么服务进程读不到配置文件”
- 排查”为什么 auditd 抓到大量 unauthorized access”
- 在合规要求下做权限基线检查(等保、CIS Benchmark)
- 共享目录(如
/srv/upload、/var/log/app)需要精细授权 - 容器、CI、Ansible 批量部署时的权限一致性管理
- 数据库、应用、消息队列的数据目录权限管理
- 用户上传目录、临时目录、构建产物目录的授权
- SSH 密钥、API 凭据、Token 文件的最小化保护
- 在多租户、多团队开发服务器上的目录隔离
- 跨主机 NFS、SMB、GlusterFS 共享存储的权限对齐
- 自动化脚本运行时需要的临时提权
- 内核、文件系统级别的高级权限(capability、ACL mask)
三、核心知识点
3.1 权限位三元组
每一个文件或目录都有三组权限:文件属主(owner)、属组(group)、其他用户(other)。
每一组用三个 bit 表示:
-
r(read)= 4,可读 / 列出目录
-
w(write)= 2,可写 / 创建删除目录内文件
-
x(execute)= 1,可执行 / 进入目录
- r w x r - x r - -
↑ ↑ ↑
owner group other
例如 -rwxr-xr-- 翻译成数字就是 754。三组权限可以用数字法(7/5/4)也可以用符号法(u/g/o + r/w/x)。
对于目录而言:
-
r表示可以列出目录内的文件列表(
ls) -
w表示可以在目录内创建、删除、重命名文件
-
x表示可以进入目录(
cd),访问元数据(stat)
很多人会忽略”目录必须有 x 才能进入”这一条,目录没 x 时即便有 r 也只是能 ls,不能 cd,更不能访问文件内容。
3.2 数字法的来源
数字法本质是 bitmask:
- r = 4 (100)
- w = 2 (010)
- x = 1 (001)
- 4+2+1=7 (rwx)
- 4+1=5 (r-x)
- 4+2=6 (rw-)
七种组合:0、1、2、3、4、5、6、7,对应:—、–x、-w-、-wx、r–、r-x、rw-、rwx。
生产环境最常用组合:
- 644:文件默认权限,rw-r–r–
- 755:目录默认权限或可执行文件,rwxr-xr-x
- 600:私钥或敏感配置,rw——-
- 640:组内可读的配置文件,rw-r—–
- 750:仅 owner + group 可进入的目录,rwxr-x—
- 700:仅 owner 可访问的目录(如 .ssh),rwx——
- 660:组内共享读写,rw-rw—-
- 770:组内共享读写执行,rwxrwx—
- 711:目录对 other 仅 x(可访问但不能列),常见于 Web 服务静态目录
3.3 特殊位:setuid / setgid / sticky
-
setuid(4xxx):文件被执行时,以属主身份运行。
/usr/bin/passwd就是典型例子。 -
setgid(2xxx):文件被执行时,以属组身份运行;目录设置后,新建文件自动继承属组。
-
sticky(1xxx):典型场景
/tmp,目录下文件只有属主能删,即便目录是 777。
详细解释:
setuid 触发条件:二进制文件 + 有 x 位 + 属主可执行位为 s。表现为:
-rwsr-xr-x 1 root root 59640 Mar 14 2025 /usr/bin/passwd
passwd 需要修改 /etc/shadow,普通用户没有写权限,但通过 setuid 后进程临时获得 root 身份,可以改 shadow。
注意:如果原属主权限没有 x,setuid 位会显示为大写 S,意思是”set 上了但无效”。
setgid 在目录上的作用特别重要:组内多个用户共同维护一个目录时,给目录加 setgid,所有新建文件都自动继承属组,无需手工 chown。
sticky 的本质是”在共享目录中保护用户自己的文件不被别人删除”。/tmp 是经典例子:
drwxrwxrwt 10 root root 4096 May 10 09:00 /tmp
最后的 t 就是 sticky bit。任何用户可以在 /tmp 下创建文件,但只有 root、目录属主、文件属主才能删除文件。
3.4 ACL:超出三元组的精细控制
POSIX ACL 用于给单个用户或单个组单独授权,弥补 owner/group/other 三元组的不足。常用命令 getfacl 与 setfacl。
例如:
bash
setfacl -m u:myapp:rw /etc/myapp/app.conf
setfacl -m g:devs:r /opt/data
setfacl -d -m u:deploy:rwx /srv/share
最后一条是为目录设置 default ACL,未来新建的文件会自动继承。
ACL 的 mask 字段是另一个容易忽略的点:除了 owner 之外,其他所有 user/group 条目的有效权限都要和 mask 做与运算。一旦误改 mask,原本能读的文件也会失效。
3.5 文件属性与扩展属性
lsattr 与 chattr 可以设置不可修改位 +i、只追加位 +a,对防护误删和审计非常有帮助。
常用属性:
-
a:append only,只能追加不能修改
-
i:immutable,不能修改、删除、重命名、创建链接
-
e:extent format,几乎所有 ext4 文件默认带
-
A:no atime updates
-
c:compressed
-
s:secure deletion
-
u:undeletable
应用场景举例:
-
/etc/passwd、
/etc/shadow:可以加 +i 锁定,防止被改 -
/var/log/important.log:可以加 +a,只能追加
-
SSH 私钥:可以加 +i,防止误改
注意:root 在大多数情况下也能被 chattr +i 阻挡,必须先 -i 才能改。
3.6 umask:默认权限的源头
umask 决定新文件/目录的默认权限。root 用户默认 umask 通常是 0022,普通用户 0002。文件实际权限 = 666 – umask,目录实际权限 = 777 – umask。
为什么文件是 666 而不是 777?因为 Linux 默认不允许新文件带可执行位,必须手工 chmod +x。这一设计是为了减少”无意中创建可执行文件”的安全风险。
umask 优先级:
- 内核默认 umask(通常 022)
- /etc/profile、/etc/bashrc、/etc/zshrc 里的 umask
- ~/.bashrc、~/.profile 里的 umask
- pam_umask 模块(在 /etc/pam.d/ 里)
- 程序内调用 umask() 系统调用
- systemd service 的 UMask= 指令
生产环境中常被忽略的是 pam_umask。如果 PAM 没启用 umask 模块,单改 profile 是没用的,systemd 启动的服务会绕开。
3.7 SELinux / AppArmor:第三道墙
即便文件权限正确,SELinux 或 AppArmor 也会基于策略拒绝访问。这是”权限全开却还是不能访问”最常见的根因。
SELinux 工作在三步:
- 进程标签(domain)
- 文件标签(type)
- 策略规则(allow domain type:permission)
当进程标签对文件类型没有允许规则时,即便文件 777,也会被 deny。
AppArmor 工作模式类似,以”路径”或”帽子”做匹配,配置文件通常在 /etc/apparmor.d/。
3.8 文件能力(Capabilities)
Linux Capabilities 是比 setuid 更细粒度的提权机制。可以只给进程部分 root 权限,比如 CAP_NET_BIND_SERVICE(绑定 1024 以下端口)、CAP_DAC_OVERRIDE(绕过文件权限检查)、CAP_CHOWN、CAP_NET_RAW。
bash
setcap cap_net_bind_service=+ep /usr/bin/nginx
getcap /usr/bin/nginx
判断:用 getcap 可以看到具体授予的能力。生产环境可以用能力替代 setuid nginx 的旧做法。
3.9 挂载选项对权限的影响
mount 时的几个常见选项:
-
noexec:禁止执行挂载点上的二进制
-
nosuid:禁止 setuid 生效
-
nodev:禁止设备文件
-
noatime/
relatime:是否更新访问时间 -
ro:只读挂载
对于临时目录、可写共享目录,加 nosuid、noexec 是基本安全要求。
四、整体排查或实施思路
权限问题排查的逻辑顺序如下,不要一上来就 chmod 777:
- 先看属主属组:
ls -ln或stat - 再看传统权限:
ls -l - 看是否有 ACL:
getfacl - 看是否有 setuid/setgid/sticky:
ls -l开头字符是否为s、t、S、T - 看是否有 chattr 属性:
lsattr - 看 umask:
umask - 看进程身份:
ps -ef、id - 看 SELinux/AppArmor 拒绝日志:
ausearch、audit2allow、journalctl -t audit - 看挂载选项:
mount | grep noexec、nosuid - 看权限继承:父目录权限、ACL mask
- 看日志:auditd、/var/log/secure、journalctl
- 看进程能力:getcap、ps -ef 里进程的 cap 位
- 看 namespaces:容器场景下 PID/UID 命名空间映射
修复原则:
- 能用最小权限解决就用最小权限
- 能用 ACL 就不用 777
- 能用 chattr +i 锁就不要靠”约定”
- 能用 setgid 解决组内共享就不要全开
- 能用能力替代 setuid 就不要全开
- SELinux/AppArmor 永远不要全局关掉
五、实战步骤
下面是一组完整的”目录权限事故复盘”步骤,包含权限审计、修复、验证和回滚。
5.1 现象描述
应用日志报错:
Permission denied: cannot open configuration file /etc/myapp/app.conf
但 ls 看上去权限正常:
-rw------- 1 root root 1024 May 10 09:00 /etc/myapp/app.conf
应用以 myapp 用户运行,文件属主是 root。看上去权限对,但实际是 600,myapp 用户读不到。
如果这时有人偷懒执行 chmod 777 /etc/myapp -R,问题虽然解决,但留下了一堆隐患。
5.2 第一步:识别文件真实属主和权限
bash
ls -ln /etc/myapp/app.conf
stat /etc/myapp/app.conf
预期输出(stat):
File: /etc/myapp/app.conf
Size: 1024 Blocks: 8 IO Block: 4096 regular file
Device: 803h/2051d Inode: 524311 Links: 1
Access: (0600/-rw-------) Uid: ( 0/ root) Gid: ( 0/ root)
Context: system_u:object_r:etc_t:s0
Access: 2026-05-10 09:00:00.000000000 +0800
Modify: 2026-05-10 09:00:00.000000000 +0800
Change: 2026-05-10 09:00:00.000000000 +0800
判断:
- Access 字段括号中 0600 表示权限三元组
- Uid/Gid 都是 root/0
- Context 是 SELinux 标签
- Change time 表示元数据最近改动时间
下一步:不要直接 chmod 777,先确认配置文件是否必须 root 拥有。
5.3 第二步:确认运行用户
bash
ps -ef | grep myapp | grep -v grep
id myapp
预期输出:
myapp 12345 1 0 May10 ? 00:00:01 /usr/local/bin/myapp
uid=1001(myapp) gid=1001(myapp) groups=1001(myapp)
判断:进程属主是 myapp,配置文件当前只有 root 能读。
5.4 第三步:选择正确的修复方案
推荐三条路径,按优先级排序:
- 把配置文件属主改为 myapp:
chown myapp:myapp /etc/myapp/app.conf - 加入 myapp 组并设 640:
chgrp myapp /etc/myapp/app.conf && chmod 640 /etc/myapp/app.conf - 用 ACL 仅授权 myapp:
setfacl -m u:myapp:r /etc/myapp/app.conf
第三种方案最精细但 ACL 多了不好审计,第一种和第二种是生产环境最常用。
5.5 第四步:执行修复(推荐方案二)
bash
chgrp myapp /etc/myapp/app.conf
chmod 640 /etc/myapp/app.conf
ls -ln /etc/myapp/app.conf
预期输出:
-rw-r----- 1 root myapp 1024 May 10 09:00 /etc/myapp/app.conf
判断:属组已改为 myapp,权限 640,myapp 组可读,others 无权限。
5.6 第五步:验证
bash
sudo -u myapp cat /etc/myapp/app.conf | head
预期输出:能正常读取到配置内容,无 Permission denied。
也可以用 runuser:
bash
runuser -u myapp -- cat /etc/myapp/app.conf
5.7 第六步:扩展到目录
如果整个配置目录都要交给 myapp 组:
bash
chown -R root:myapp /etc/myapp
chmod 750 /etc/myapp
find /etc/myapp -type f -exec chmod 640 {} \;
find /etc/myapp -type d -exec chmod 750 {} \;
判断:目录 750、文件 640,root 完全控制,myapp 组可读可进入,others 完全隔离。
注意:先改目录,再分别处理文件和子目录,比 chmod -R 750 /etc/myapp 更精细——后者会把所有文件也设成 750,可执行位会被错误打开。
5.8 第七步:用 ACL 处理需要写权限的特殊文件
如果有几个文件需要 myapp 自己写:
bash
setfacl -m u:myapp:rw /etc/myapp/app.conf
getfacl /etc/myapp/app.conf
预期输出(关键字段):
user::rw-
user:myapp:rw-
group::r--
mask::rw-
other::---
判断:myapp 通过 ACL 拿到了 rw,但 other 仍然 0,文件更安全。
5.9 第八步:用 chattr 锁定关键文件
对特别敏感的文件加不可修改位:
bash
chattr +i /etc/myapp/app.conf
lsattr /etc/myapp/app.conf
预期输出:
----i---------e------- /etc/myapp/app.conf
判断:文件被锁定,root 也无法直接修改,必须先 chattr -i。
chattr 在生产环境中也常被应急使用:
-
chattr +a /var/log/messages:只允许日志追加
-
chattr +i /etc/resolv.conf:防止 DHCP 客户端被覆盖
-
chattr +i /usr/bin/*:防止关键二进制被替换
5.10 第九步:检查应用进程能否读取并写入运行日志
权限不只是配置文件,也包括运行时目录:
bash
ls -ld /var/log/myapp /var/run/myapp
chown myapp:myapp /var/log/myapp /var/run/myapp
chmod 750 /var/log/myapp /var/run/myapp
5.11 第十步:建立权限基线
使用 auditd 或文件完整性工具做权限审计:
bash
auditctl -w /etc/myapp -p wa -k myapp-config
ausearch -k myapp-config
或用 aide/tripwire 做基线快照。
5.12 第十一步:建立 SUID/SGID 基线
生产环境的 SUID 文件应有一个固定基线:
bash
find / -perm /6000 -type f 2>/dev/null | sort > /var/lib/baseline/suid.txt
diff /var/lib/baseline/suid.txt /var/lib/baseline/suid.txt.bak
判断:基线出现新增 SUID 文件,说明可能被植入后门或被误操作。
5.13 第十二步:批量修复时的 Ansible 写法
yaml
- name: 配置文件属主和权限
file:
path: /etc/myapp/app.conf
owner: root
group: myapp
mode: '0640'
- name: 锁定关键文件
file:
path: /etc/myapp/app.conf
attr: +i
判断:file 模块同时支持 owner/group/mode 和 attributes,比手工 chmod + chown 更原子。
5.14 第十三步:在 CI 流水线中检查权限
bash
#!/bin/bash
# ci-perm-check.sh
set -euo pipefail
for f in $(find /etc/myapp -type f); do
perm=$(stat -c '%a' "$f")
if [[ "$perm" == "777" ]]; then
echo "FAIL: $f is 777"
exit 1
fi
done
echo "OK: no 777 found"
判断:把”没有 777 文件”作为 CI 闸口,新代码一旦引入 777 直接 fail。
5.15 第十四步:用 rpm/ostree 等做完整性校验
基于包管理的系统,可以用 rpm 自带的 verify:
bash
rpm -Va | grep '^M'
判断:M 开头表示 mode 变化,可以用来发现被改动的权限。
六、常用命令
| 命令 | 用途 | 关键选项 |
| — | — | — |
| ls -l | 看权限三元组 | -h 、-d、-n |
| stat | 看文件全部属性 | -c 自定义格式 |
| chmod | 改权限 | u/g/o/a 、+/-/=、r/w/x、数字法 |
| chown | 改属主/属组 | -R 、--from、--reference |
| chgrp | 改属组 | -R |
| umask | 看/设默认权限 | -S 符号显示 |
| getfacl | 读 ACL | -n 显示数字 UID/GID |
| setfacl | 设 ACL | -m 、-x、-b、-R、-d 默认 ACL |
| lsattr | 看文件属性 | -R 、-a |
| chattr | 改文件属性 | +i 、+a、-i、-R |
| find -perm | 按权限找文件 | find / -perm /6000 -type f |
| id | 看当前用户身份 | -u 、-g、-G |
| sudo -u | 切换身份执行 | -i 、-H |
| runuser | 不经 PAM 切换身份 | -u |
| auditctl/ausearch | 看权限审计 | -w 、-p、-k |
| getcap | 看文件能力 | -r 递归 |
| setcap | 设文件能力 | -n 、--reuid |
| strace | 跟踪系统调用 | -e trace=openat,chmod |
| inotifywait | 实时监控文件改动 | -m 、-e |
| mount | 查看挂载选项 | 无 |
| rpm -Va | 校验包文件属性 | 无 |
6.1 权限位数字法
bash
chmod 755 /opt/app
读法:7=rwx(owner)、5=r-x(group)、5=r-x(other)。
6.2 权限位符号法
bash
chmod u=rw,g=r,o= /opt/app
chmod g+w /opt/app
chmod o-x /opt/app
适合做”对现有权限做加减”。
6.3 递归改权限的常见错误
bash
chmod 755 /opt/app
chmod -R 755 /opt/app
chmod -R 755 会把目录下所有文件都设成 755,包括二进制、密钥、配置。生产环境中极容易把密钥文件也设成 755,导致 ssh 直接拒绝。
更安全的做法:
bash
find /opt/app -type d -exec chmod 755 {} \;
find /opt/app -type f -exec chmod 644 {} \;
如果希望对特定可执行文件加 +x:
bash
find /opt/app/bin -type f -exec chmod 755 {} \;
find /opt/app/lib -type f -exec chmod 644 {} \;
6.4 ACL 的备份与恢复
bash
getfacl -R /opt/app > /backup/app.acl
setfacl --restore=/backup/app.acl
迁移权限时尤其有用。
6.5 找 SUID/SGID 文件
bash
find / -perm /6000 -type f -ls 2>/dev/null
说明:/6000 表示匹配只要 setuid 或 setgid 任一即可,用于排错和审计。
更精细的查询:
bash
find /usr/bin -perm -4000 -type f -ls
find /usr/bin -perm -2000 -type f -ls
-perm -4000 表示必须同时有 suid 位(用于单独查 suid)。
6.6 找没有属主的文件
bash
find / -nouser -o -nogroup -ls 2>/dev/null
判断:账户被删除但文件仍在。多发生在离职流程没走完时。
6.7 批量设置粘滞位(共享目录)
bash
chmod 1770 /srv/share
数字 1 在最前面表示 sticky bit。目录内文件只有属主、root 或目录属主能删。
6.8 用 ACL 设置默认权限(目录继承)
bash
setfacl -d -m u:myapp:rwx /srv/share
-d 表示 default,未来在 /srv/share 下创建的文件/目录会继承这一条 ACL。
6.9 用 ACL 移除单个授权
bash
setfacl -x u:myapp /etc/myapp/app.conf
-x 表示移除指定条目,保留其他。
6.10 递归删除所有 ACL
bash
setfacl -R -b /opt/app
注意:setfacl -b 会清空所有 ACL 条目,但属主属组和传统三元组不变。这条命令是清理 ACL 的标准动作,但要确认业务没有依赖 ACL。
6.11 看进程能力
bash
getcap /usr/bin/nginx
ps -ef | grep nginx
cat /proc/$(pidof nginx)/status | grep Cap
判断:通过能力可以让 nginx 不需要 root 就能绑 80 端口。
6.12 用 strace 跟踪拒绝
bash
strace -f -e trace=openat,access,chmod,fchmod -p $(pidof myapp) 2>&1 | grep -E 'ENOENT|EACCES'
判断:可以实时看到某个进程在打开文件时被拒绝的具体路径和系统调用。
6.13 用 inotifywait 监控目录
bash
inotifywait -m -r -e create,modify,delete,attrib /etc/myapp
判断:可以快速定位谁动了文件。注意权限不足时会无法 attach。
6.14 永久修改 umask
bash
echo "umask 027" >> /etc/profile.d/myapp.sh
chmod 644 /etc/profile.d/myapp.sh
注意:图形登录、SSH 登录、su – 切换会触发不同的 shell 启动文件,要确保都覆盖。
6.15 看 SELinux 拒绝原因
bash
ausearch -m avc -ts today | audit2why
ausearch -m avc -ts today | audit2allow
判断:audit2why 解释原因,audit2allow 生成可用的策略建议。
6.16 看当前登录用户
bash
who
w
last
判断:用于排查”为什么这个用户在我不知情时登录过”。
6.17 看 su/sudo 历史
bash
journalctl _COMM=sudo
journalctl _COMM=su
cat /var/log/secure | grep -E 'sudo|session'
判断:可定位权限使用记录。
6.18 用 rsync 同步权限
bash
rsync -a --no-perms /source/ /dest/
说明:默认 rsync 会同步权限,用 --no-perms 可保持目标权限不变。
6.19 用 cp 保留权限
bash
cp -a /etc/myapp /backup/myapp.bak
cp --preserve=mode,ownership,timestamps /etc/myapp/app.conf /tmp/
-a 等价于 -dR --preserve=all,是备份权限和元数据的标准动作。
6.20 看 mount 与权限相关的选项
bash
mount | grep -E 'nosuid|noexec|nodev|ro,'
findmnt /data
findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS /data
判断:挂载选项会被权限检查叠加,不要只看到 755 就以为一定能进。
七、配置示例
7.1 /etc/profile 中的 umask 配置
bash
# /etc/profile
if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then
umask 002
else
umask 022
fi
判断:UID 大于 199 且用户名与组名一致(即普通用户),umask 为 002;其他为 022。系统服务账户通常落在 022。
7.2 /etc/login.defs 中的 umask
bash
UMASK 077
USERGROUPS_ENAB yes
说明:USERGROUPS_ENAB=yes 时,userdel 会同时删除同名组。
7.3 sudoers 中限定命令权限
bash
# /etc/sudoers.d/myapp
myapp ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp.service, /usr/local/bin/myapp-reload
判断:myapp 用户只能 sudo 重启特定服务,而不是 ALL。
更严格版本:
bash
myapp ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp.service
myapp ALL=(root) NOPASSWD: /usr/local/bin/myapp-reload
Defaults!/usr/bin/systemctl !logfile, !syslog
Defaults env_keep -= "PATH"
7.4 /etc/pam.d/su 限制 su 切换
bash
auth required pam_wheel.so use_uid
判断:只有 wheel 组成员才能 su -,普通用户被禁止。
7.5 pam_umask 模块
bash
# /etc/pam.d/system-auth
session optional pam_umask.so umask=0077
说明:会话建立后强制 umask=077,新文件默认 600、目录 700。
7.6 /etc/fstab 与挂载选项
bash
UUID=xxx /srv/data ext4 defaults,noexec,nosuid,nodev 0 2
判断:给数据盘加 noexec,禁止直接执行盘内脚本;nosuid 屏蔽 setuid。
7.7 auditd 规则示例
bash
# /etc/audit/rules.d/myapp.rules
-w /etc/myapp -p wa -k myapp-config
-w /usr/local/bin/myapp -p x -k myapp-exec
-w /var/log/myapp -p wa -k myapp-log
判断:所有对配置目录的写、属性变更、对二进制的执行、对日志目录的写都会被记录。
7.8 /etc/audit/rules.d/perm.rules(权限审计)
bash
-a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S chown -S fchown -S fchownat -S lchown -F auid>=1000 -F auid!=4294967295 -k perm_mod
判断:所有非系统用户的 chmod/chown 都会被审计。
7.9 /etc/skel/.bashrc 中的 umask 提示
bash
umask 022
echo "your umask is $(umask)"
7.10 systemd unit 中的 UMask 与权限
ini
[Service]
ExecStart=/usr/local/bin/myapp
UMask=0027
判断:服务启动后进程内 umask=0027,新建文件默认 640、目录 750。
也可以加上:
ini
[Service]
ExecStart=/usr/local/bin/myapp
UMask=0027
User=myapp
Group=myapp
RuntimeDirectory=myapp
RuntimeDirectoryMode=0750
StateDirectory=myapp
StateDirectoryMode=0750
ConfigurationDirectory=myapp
ConfigurationDirectoryMode=0750
判断:让 systemd 自动管理运行时、状态、配置目录的权限。
7.11 /etc/security/limits.conf
bash
myapp hard nofile 65535
myapp soft nofile 32768
说明:虽然和文件权限无关,但运维经常混在一起排查,作为知识补充。
7.12 /etc/ssh/sshd_config 权限相关
bash
StrictModes yes
AuthorizedKeysFile .ssh/authorized_keys
判断:StrictModes 会强制 .ssh 目录和 authorized_keys 文件权限必须符合预期。
7.13 /etc/cron.d/ 中的脚本权限
bash
-rwx------ root root /etc/cron.daily/backup.sh
判断:cron 脚本不应该有 755,敏感信息可能泄露。
7.14 ACL 的最小授权示例
bash
setfacl -m u:deploy:rx /opt/app/bin
setfacl -m u:deploy:r /opt/app/conf
setfacl -m g:deployers:rw /opt/app/upload
setfacl -d -m g:deployers:rw /opt/app/upload
判断:deploy 用户对 bin 可读可执行、对 conf 仅读、对 upload 可写;新建文件自动继承 rw。
7.15 Ansible 中批量修复 ACL
yaml
- name: 设置 ACL
acl:
path: /opt/app/conf
entity: myapp
etype: user
permissions: r
state: present
判断:Ansible 的 acl 模块会幂等地设置 ACL。
7.16 用 policykit 限制服务控制
bash
# /etc/polkit-1/localauthority/50-local.d/myapp.pkla
[Allow myapp restart]
Identity=unix-user:myapp
Action=org.freedesktop.systemd1.manage-units
ResultActive=yes
判断:让 myapp 用户无需 sudo 也能重启自身服务,比给 ALL 权限安全。
7.17 /etc/login.defs 中 PASS_MIN_DAYS
bash
PASS_MIN_DAYS 0
PASS_MAX_DAYS 90
PASS_WARN_AGE 7
说明:和密码生命周期有关,与权限组合后可降低长期不改密码导致滥用。
7.18 容器场景下的权限
yaml
# docker-compose.yml
services:
myapp:
image: myapp:1.0
user: "1001:1001"
read_only: true
tmpfs:
- /tmp
volumes:
- /etc/myapp:/etc/myapp:ro
security_opt:
- no-new-privileges:true
- apparmor=myapp-profile
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
判断:
- user 指定 UID,避免容器内 root 等于宿主机 root
- read_only 让根文件系统只读
- tmpfs 给临时目录
- :ro 把配置只读挂载
- no-new-privileges 禁止 setuid 提权
- cap_drop ALL + cap_add 单独加回必要能力
7.19 Kubernetes Pod 安全上下文
yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp
spec:
securityContext:
runAsUser: 1001
runAsGroup: 1001
fsGroup: 1001
containers:
- name: myapp
image: myapp:1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
volumeMounts:
- name: conf
mountPath: /etc/myapp
readOnly: true
volumes:
- name: conf
configMap:
name: myapp-conf
判断:和 docker-compose 思路一致,但用 Pod spec 落地。
7.20 完整的多用户共享目录设置脚本
bash
#!/bin/bash
# init_shared_dir.sh
set -euo pipefail
DIR=/srv/share
GROUP=shared
if ! getent group "$GROUP" >/dev/null; then
groupadd "$GROUP"
fi
mkdir -p "$DIR"
chown root:"$GROUP" "$DIR"
chmod 2770 "$DIR"
chmod g+s "$DIR"
setfacl -d -m g:"$GROUP":rwx "$DIR"
setfacl -m g:"$GROUP":rwx "$DIR"
echo "ok: $DIR created, group $GROUP can rwx"
判断:setgid + default ACL 是共享目录的标准配方。
八、日志或指标观察方法
权限事故的关键观察点:
-
/var/log/audit/audit.log:查找
avc: denied或syscall=chmod行 -
/var/log/secure:ssh/sudo 失败、su 切换失败
-
应用日志:Permission denied、Operation not permitted
-
系统日志:
journalctl -t audit、journalctl -p err -b -
inotify 监控:可用
inotifywait -m /etc/myapp实时观察文件改动 -
Prometheus 指标:node_exporter 的
node_file_*指标监控文件权限与属主变化
8.1 审计 SUID/SGID 改动
bash
ausearch -m SYSCALL -k perm_mod | grep chmod
8.2 用 audit2allow 解读 SELinux
bash
ausearch -m avc -ts today | audit2why
判断:会输出为什么被拒绝,以及建议的策略调整方向。
8.3 用 strace 验证进程权限调用
bash
strace -f -e trace=openat,access,chmod,fchmod -p $(pidof myapp) 2>&1 | grep -E 'ENOENT|EACCES'
判断:能找到具体哪个文件路径触发了 EACCES。
8.4 Prometheus + node_exporter 的相关指标
text
node_file_info
node_filesystem_files
node_filesystem_files_free
node_file_perm_change_total
判断:可以基于 node_file_perm_change_total 在 prometheus 配告警(以实际 exporter 暴露的指标名为准)。
8.5 用 audit_exporter 输出指标
部署 audit-exporter,把 auditd 日志转为 Prometheus 指标,按关键字聚合后看 perm_mod 的速率。
判断:长期平稳,遇事故会陡增。
8.6 监控 SUID 文件总数
bash
find / -perm /6000 -type f 2>/dev/null | wc -l
把这个数字放进 cron 周期任务,每天和基线对比。
8.7 看进程尝试打开被拒绝的次数
bash
journalctl -p err | grep -c 'Permission denied'
判断:长期 0,突然飙升说明大量业务在失败。
8.8 看 sudo 使用记录
bash
journalctl _COMM=sudo --since today
判断:哪个用户在 sudo 跑了什么命令。
8.9 用 aide 做文件完整性监控
bash
aide --check
判断:定期跑 aide 比对,发现权限或文件内容变化。
8.10 集成 SIEM
把 auditd 日志通过 syslog 或 filebeat 送到 ELK / Splunk,按关键字 chmod、chown、denied 建索引,关联用户、IP、时间。
九、排查路径
9.1 “Permission denied” 类
- 属主属组是不是期望用户
- 文件权限三元组是否包含运行用户的位
- 是否在属组内:检查
id和groups - 是否有 ACL:检查
getfacl中 mask 是否遮盖了有效权限 - 父目录是否有 x 位:缺 x 意味着连进入都做不到
- 是否有 SELinux/AppArmor:检查
ausearch和journalctl - 是否有挂载选项 noexec、nosuid、nodev
- 是否被 chattr +i 锁定
- 看文件系统:FAT/NTFS 挂载对权限的处理不一样
- 看 capabilities:进程能力是否被裁剪
9.2 “为什么别人能删我的文件” 类
- 父目录是否有 sticky bit
- 文件属主是不是自己
- 父目录是否被设为 777,导致其他人能直接删
- 是否有 inotify、auditd 抓到删除来源
- 是否 NFS/SMB 客户端的属主映射问题
9.3 “为什么脚本不能执行” 类
- 文件权限是否有 x 位
- 所在分区是否有 noexec 挂载
- 脚本解释器(shebang)路径是否存在
- SELinux 是否把文件标为
unconfined_t或脚本类域 - 文件系统是否不支持可执行(FAT/NTFS 挂载)
- 是否被脚本签名机制阻止
9.4 “为什么 sudo 不工作” 类
-
/etc/sudoers语法:
visudo -c -
是否在 wheel/sudo 组:
id、groups -
/etc/sudoers.d/内是否有同名文件冲突
-
pam 配置是否启用
-
securetty、pam_nologin 是否拒绝
-
sudo 时间戳过期:清空
/var/db/sudo/<user>或sudo -k
9.5 “为什么 ACL 不生效” 类
- 文件系统是否支持 ACL:mount 时是否带
acl选项(现代默认开) - mask 是否过紧:用
setfacl -m m::rwx重置 - 是否被传统三元组覆盖:检查
ls -l末尾是否有+号 - 默认 ACL 是否对已存在的文件无效:用
getfacl看 default 段
9.6 “为什么 chattr +i 后还是被改” 类
- 文件是否在容器 overlay 层
- 是否在只读文件系统上(如 squashfs)
- 是否 NFS 共享,本地 chattr 不会被同步到服务端
- 是否有进程以 root 持文件锁,需要先解锁
9.7 “为什么 SSH 公钥认证失败” 类
-
~/.ssh必须是 700
-
authorized_keys必须是 600
-
~/.ssh的属主属组是否正确
-
StrictModes yes是否触发
-
SELinux
restorecon -R ~/.ssh
9.8 “为什么 cron 不执行” 类
-
/etc/crontab、
/etc/cron.d/*权限必须是 600 -
/var/spool/cron 目录权限
-
用户是否在 /etc/cron.allow、/etc/cron.deny 里
-
pam 是否启用了 cron 限制
9.9 “为什么 NFS 上 chmod 无效” 类
- 是否启用了 root_squash(默认开启)
- 服务端文件系统是否支持 ACL
- 客户端 mount 选项是否带
noacl关闭 ACL - NFS 版本差异:v4 才有完整 ACL 支持
十、风险提醒
-
chmod 777 -R:会让任意用户可写、可删、可改,是权限事故最大单一根因;不要用。
-
chmod 777 /etc/、
/usr/、/var/:破坏系统服务运行所需的精细权限;SSH、sudo、passwd 直接拒。 -
chown -R把属主改成错误用户:导致原服务运行用户无法访问。
-
chmod -R 755:会让密钥文件、配置文件中混入可执行权限,ssh 直接拒绝(.ssh 必须 700,私钥 600)。
-
rm -rf配合错误路径:常见误删
/opt、/data。 -
setfacl -b:会清空所有 ACL,看似清理,实则把限制打开。
-
chattr +i锁住文件后忘了 -i:导致配置文件无法被应用热加载。
-
find / -perm不带
-type或带错路径:可能扫描极慢或扫错目录。 -
sudo chmod之后没验证:导致客户端访问突然全断。
-
setuid滥用:给第三方二进制加 setuid,可能成为提权跳板。
-
noexec卸载:临时为修问题 umount 再 mount 时去掉 noexec,事后忘了加回。
-
mount -o remount,没加
ro锁:导致误改文件。 -
passwd、
sudo、ping等 SUID 二进制被替换:常见攻击路径。 -
chmod 1777 /tmp缺失 sticky:共享目录裸奔。
-
umask 000:所有人共享读写,文件语义丧失。
-
cron.allow不配置:默认多数系统允许所有用户跑 cron。
-
sudo NOPASSWD: ALL:等于给 root。
-
chmod a+w /var/log:日志被污染,审计失效。
-
chmod 777 /proc/self/...:调试时偶发,但不要在生产执行。
-
备份目录权限也 777:备份文件被改,事故无法还原。
-
密钥文件 644:私钥泄露。
-
容器内 root 写文件落到宿主机 root-owned:导致目录错乱。
-
cp -r不带
-p:丢失属主属组,权限被重置。 -
tar -xpf不带
--same-permissions:还原后权限错乱。
十一、验证方式
-
ls -ln看权限三元组、属主、属组
-
stat看 Access/Modify/Change 时间、ACL 提示(
+号) -
getfacl看 ACL 完整内容
-
sudo -u myapp cat /path切换身份验证
-
auditctl -l看规则是否生效
-
ausearch -k xxx看是否产生日志
-
strace看真实系统调用返回值
-
应用层验证:业务请求是否能正常读写
-
完整性校验:对比修复前后
find /etc/myapp -ls的输出 -
主机基线巡检:用 Ansible/CIS-CAT 跑一遍权限检查脚本
-
监控指标:观察
perm_mod速率、Permission denied 计数 -
单元测试:在 CI 跑”不能出现 777″的断言
-
端到端:用模拟请求验证应用从读配置、写日志、上传文件全流程
-
灾备演练:模拟误改权限,看恢复流程是否畅通
十二、回滚方案
12.1 文件级权限回滚
bash
cp -a /etc/myapp/app.conf.bak /etc/myapp/app.conf
chown root:root /etc/myapp/app.conf
chmod 600 /etc/myapp/app.conf
setfacl -b /etc/myapp/app.conf
chattr -i /etc/myapp/app.conf
判断:先备份当前状态,再还原原始属主、属组、权限、ACL 和属性。
12.2 ACL 回滚
bash
setfacl --restore=/backup/app.acl.before
12.3 SUID/SGID 回滚
bash
chmod u-s,g-s /path/to/file
12.4 chattr 回滚
bash
chattr -i -a /path/to/file
12.5 整盘回滚思路
bash
# 1. 停止受影响的服务
systemctl stop myapp
# 2. 从备份还原权限
tar --same-permissions --same-owner -xpf /backup/etc-myapp.tar -C /
# 3. 重新检查 ACL
getfacl -R /etc/myapp > /tmp/check.acl
diff /backup/app.acl.before /tmp/check.acl
# 4. 启动服务
systemctl start myapp
12.6 批量回滚脚本
bash
#!/bin/bash
# rollback_perms.sh
set -euo pipefail
BACKUP_DIR=${1:?usage: rollback_perms.sh <backup_dir>}
while IFS=: read -r path owner group mode; do
chown "$owner:$group" "$path"
chmod "$mode" "$path"
done < "$BACKUP_DIR/manifest.txt"
echo "rolled back"
判断:先用 find / -ls > manifest.txt 做基线,事故时用脚本还原。
12.7 ACL 全量重置
bash
setfacl -R -b /opt/app
chmod 755 /opt/app
仅在确认 ACL 不是业务依赖时才使用。
12.8 误设 SUID 回滚
bash
chmod u-s /usr/local/bin/myapp
并审计是否有其他文件被错配。
12.9 SELinux 标签回滚
bash
restorecon -R /etc/myapp
判断:修复了 SELinux 标签错配后,应用重新可以被正确访问。
12.10 umask 回滚
bash
sed -i '/umask/d' /etc/profile
echo "umask 022" > /etc/profile.d/umask.sh
注意:已有会话不会受影响,新会话生效。
十三、生产环境注意事项
- 操作窗口:避免业务高峰期改权限,建议凌晨或低峰窗口
- 灰度:先在 1 台机器验证,确认无误再批量
- 备份:使用
cp -a或tar --same-permissions备份原权限 - 审计:开启 auditd 对
perm_mod关键字的监控 - 工具:通过 Ansible/Chef/Salt 推送权限规则,避免手工改一台漏一台
- 文档:在变更单中记录属主、属组、权限三元组、ACL、属性
- 密钥:私钥文件权限必须 600,目录 700,且不能放入 ACL
- 容器:容器内的文件权限错误时,优先检查宿主机挂载点,再看容器进程 UID 与宿主机文件是否一致
- SELinux:开启 enforcing 模式,不要随便设 permissive
- AppArmor:服务 profile 要按官方模板维护,不要全 deny 后又全 allow
- 共享目录:用 setgid 让新建文件自动继承属组
- 数据目录:明确属主属组,绝不 777
- 日志目录:日志切割、归档前必须确认属主属组,否则 logrotate 会失败
- 配置文件:能用 640 + 组读就不要 644
- 提权:sudoers 限定具体命令,不给 ALL
- 监控:配置变更后 5 分钟内巡检一次
- 文件系统:xfs/ext4 默认开启 ACL,必要时
tune2fs -o acl /dev/sdX启用 - 磁盘告警:inode 满也会出现”无法创建文件”的伪权限错误
- 链路追踪:重要目录使用 auditd 关键字追踪变化
- CI 检查:在 CI 中加入”不能出现 777″硬性闸口
- 合规对照:CIS Benchmark、等保 2.0 都有专门章节检查权限
- 培训:让团队理解”为什么不能 777″,比写十条规则更有效
- 复盘:每次 777 事故写一份复盘,列出根因、影响、修复、改进项
十四、总结
chmod 777 是权限事故的最常见根因,不是工具错了,是用它的人太懒。生产环境永远不该出现 777,真正的修复路径是:
- 明确属主、属组、运行进程 UID
- 找到最小权限组合(4/2/1 三个数字的组合)
- 用 ACL 处理例外
- 用 chattr +i 锁关键文件
- 启用 auditd 做权限审计
- 用配置管理工具做基线
- 留好回滚脚本
- SELinux/AppArmor 不要关
- SUID/SGID 做好基线
- umask 设到合适粒度
十五、案例:一次完整的 777 事故复盘
为了把上面所有知识点串起来,下面给一个完整的复盘案例。
15.1 事故发生
时间:凌晨 2 点。 报警:监控显示某核心服务 health check 失败。 现象:业务请求 503,日志报错 Permission denied: cannot open /etc/myapp/app.conf。
15.2 初步判断
- 服务在运行:
systemctl status myapp显示 active - 进程在跑:
ps -ef | grep myapp有输出 - 配置文件权限是 600:只有 root 可读
进一步:journalctl -u myapp --since "5 minutes ago" 显示文件读取失败。
15.3 错误修复导致二次事故
值班同学紧急执行:
bash
chmod 777 /etc/myapp -R
systemctl restart myapp
服务恢复,看似 OK。但接下来三天:
-
配置文件被人改成
EnableDebugMode=true,泄露内部信息 -
/etc/myapp/secrets.key被其他用户读走
-
出现大量未授权访问
15.4 根因定位
复盘发现,根因是更早一次迁移时:
-
/etc/myapp属主改为 root,但属组误改为 nobody
-
文件权限被工具统一刷成 640
-
nobody 组没有任何成员,导致没人能读
正确修复本应是:
bash
chgrp myapp /etc/myapp/app.conf
chmod 640 /etc/myapp/app.conf
不需要 777,不需要 chown -R。
15.5 教训提炼
- 永远不要用 777 治 Permission denied
- 改权限前先看属主属组、看 ACL、看 SELinux
- 用 auditd 抓到所有 perm_mod 类操作
- 在 CI 加 777 检查
- 把”最小权限”作为新人入职第一课
- 把”改权限”列入变更单必填项
十六、运维手册:日常权限巡检 checklist
每周跑一遍的权限巡检脚本:
bash
#!/bin/bash
# perm_audit.sh
set -uo pipefail
echo "=== SUID/SGID baseline ==="
find / -perm /6000 -type f 2>/dev/null | sort > /tmp/perm_audit/suid.now
diff -u /var/lib/baseline/suid.txt /tmp/perm_audit/suid.now || echo "SUID baseline changed!"
echo "=== 777 files ==="
find /etc /opt /var /srv /home -type f -perm 0777 2>/dev/null > /tmp/perm_audit/777.files
if [[ -s /tmp/perm_audit/777.files ]]; then
echo "WARN: 777 files found:"
cat /tmp/perm_audit/777.files
fi
echo "=== /tmp sticky bit ==="
ls -ld /tmp /var/tmp
echo "=== /etc/shadow /etc/passwd ==="
ls -l /etc/shadow /etc/passwd
echo "=== auditd rules ==="
auditctl -l | wc -l
echo "=== SELinux mode ==="
getenforce
判断:把输出接入监控,基线变化时告警。
十七、与 chmod 相关的常见误区
17.1 “目录设成 777 应该安全”
错。共享目录用 2775 + setgid + ACL mask 才安全。
17.2 “root 什么都能改”
错。chattr +i 能挡住 root,SELinux enforcing 能挡 root,文件系统只读 mount 也能挡。
17.3 “权限只要 chmod 就够”
错。Linux 权限至少四层:属主属组、三元组、ACL、SELinux/AppArmor。少一层都可能漏。
17.4 “777 不影响安全反正防火墙拦着”
错。横向越权是内部攻击主要路径,777 给了攻击者额外跳板。
17.5 “反正有备份”
错。备份只能恢复内容,无法恢复”谁改了什么”的取证线索。
17.6 “777 临时改一下没问题”
错。临时改的时间就是事故窗口期,恶意改动就在这个窗口期发生。
17.7 “用 root 跑就没权限问题”
错。root 也受 chattr +i、SELinux、文件系统 mount 限制。root 不等于万能。
17.8 “生产环境就这样了,没法改”
错。一次彻底整改胜过 100 次临时修复。
十八、运维工具推荐
-
auditd
:内核级审计,覆盖 chmod/chown/setuid
-
aide / tripwire
:文件完整性校验,包含权限字段
-
CIS-CAT
:扫描权限基线
-
Lynis
:开源安全审计,覆盖权限、SUID、ACL
-
OpenSCAP
:合规扫描,支持多种标准
-
Ansible file / acl 模块
:批量权限管理
-
Chef audit cookbook
:声明式权限检查
-
inotify-tools
:实时文件变动监控
-
strace / ltrace
:进程级权限调用跟踪
-
setpriv / runuser
:测试用户切换
十九、与权限相关的常见面试题
19.1 chmod 755 和 chmod u=rwx,go=rx 一样吗?
完全等价。755 是数字法,后者是符号法,底层都是把 mode 设为 0755。
19.2 SUID 和 SGID 区别?
SUID 让进程以文件属主身份运行,SGID 让进程以文件属组身份运行。两者都可被 chattr 影响。SGID 在目录上能让新建文件继承属组。
19.3 find -perm 4000 和 find -perm /4000 区别?
-
-perm 4000:必须正好 4000,不能多不能少
-
-perm /4000:只要包含 4000 即可
-
-perm -4000:必须 4000 及以上的所有位都满足
19.4 为什么 /tmp 是 1777?
1777 = sticky + 777。任何用户能创建文件,但只能删自己的。符合共享临时目录语义。
19.5 ACL 的 mask 字段是什么?
mask 是除 owner 之外其他 ACL 条目的权限上限。如果 mask=rx,user:foo:rw 实际只有 rx。
19.6 SELinux enforcing 和 permissive 区别?
enforcing 拒绝违规并记录;permissive 只记录不拒绝。生产必须 enforcing。
19.7 umask 077 和 027 区别?
077:新文件默认 600、目录 700。 027:新文件默认 640、目录 750。 通常多用户共享用 027,单用户严苛用 077。
19.8 chattr +a 和 +i 区别?
+a:append only,只能追加不能修改/删除。 +i:immutable,任何修改都不允许。
19.9 setcap 和 setuid 区别?
setcap 给进程部分 root 能力,setuid 给进程全 root 身份。能力更细粒度。
19.10 sudo NOPASSWD 风险?
任何能登录该用户的角色都可 sudo 提权,等于弱化认证。
二十、权限管理的未来趋势
20.1 rootless 容器
Podman、CRI-O、K8s 都支持 rootless 容器,进程直接以普通用户跑,从源头减少 SUID。
20.2 eBPF 权限监控
用 eBPF 在内核层 hook 所有文件操作,比 auditd 更细粒度、更低开销。
20.3 LSM (Linux Security Module)
Landlock 是新一类 LSM,允许进程自定义文件系统访问控制。
20.4 可验证的最小权限策略
Cilium、Tetragon 等基于 eBPF 的策略引擎,把”最小权限”做成可验证、可回放、可审计。
20.5 一致的身份层
SPIFFE/SPIRE 把主机身份和进程身份打通,未来权限可以基于身份而不是基于 UID。
二十一、相关资源
- man 手册:
man chmod、man chown、man acl、man chattr、man capabilities - Red Hat SELinux 用户指南
- CIS Benchmark for Linux
- NSA RHEL 5/6/7/8/9 STIG
- Linux Foundation 培训课程 LFS216
- The Linux Documentation Project 权限章节
二十二、写在最后
权限事故往往发生在”图省事”那一瞬间,而排查成本是当初省下时间的几十倍。chmod 777 不是工具,是标志——它告诉你当前的修复方案是错的,至少是不完整的。
把这篇文章当作手册,遇到权限问题按章节走,比直接 777 要慢一些,但能换来:
- 长期安全
- 可审计的变更
- 可追溯的事故
- 可复盘的运维
- 可继承的知识
运维这条路,少做”省事”的事,多做”该做”的事。
二十三、典型场景下的最小权限模板
下面给出几个常见生产场景的最小权限组合,作为参考模板。直接复制即可,但要按自己业务调整。
23.1 Web 应用代码目录
bash
# 假设应用用户 myapp,主目录 /opt/myapp
chown -R root:myapp /opt/myapp
chmod 750 /opt/myapp
find /opt/myapp -type d -exec chmod 750 {} \;
find /opt/myapp -type f -exec chmod 640 {} \;
find /opt/myapp/bin -type f -exec chmod 750 {} \;
chmod 1770 /opt/myapp/uploads
chmod 1770 /opt/myapp/cache
判断:root 拥有,myapp 组可读;可执行文件 750;可写目录加 sticky。
23.2 配置目录
bash
chown -R root:myapp /etc/myapp
chmod 750 /etc/myapp
find /etc/myapp -type f -exec chmod 640 {} \;
find /etc/myapp -type f -name "*.secret" -exec chmod 600 {} \;
chattr +i /etc/myapp/app.conf
判断:敏感配置 +i 锁定,密钥文件 600。
23.3 共享上传目录
bash
mkdir -p /srv/upload
chown root:upload /srv/upload
chmod 2770 /srv/upload
setfacl -d -m g:upload:rwx /srv/upload
setfacl -m g:upload:rwx /srv/upload
判断:setgid 让新文件继承组,default ACL 让新文件自动 rwx,sticky 不加(组内可互删)。
23.4 数据库目录
bash
chown -R mysql:mysql /var/lib/mysql
chmod 750 /var/lib/mysql
find /var/lib/mysql -type f -exec chmod 660 {} \;
find /var/lib/mysql -type d -exec chmod 750 {} \;
chmod 700 /var/lib/mysql-keyring
判断:MySQL 数据目录必须属主是 mysql,socket 文件要特殊处理。
23.5 Redis 数据目录
bash
chown -R redis:redis /var/lib/redis
chmod 750 /var/lib/redis
chmod 600 /etc/redis/redis.conf
判断:Redis 单进程模型,权限简单但要保证配置文件不被其他用户读。
23.6 日志目录
bash
chown root:syslog /var/log
chmod 775 /var/log
for app in myapp nginx; do
mkdir -p /var/log/$app
chown $app:adm /var/log/$app
chmod 750 /var/log/$app
done
chmod 640 /etc/logrotate.d/*
判断:日志目录允许 adm 组读取,应用用户可写自己目录。
23.7 SSH 配置目录
bash
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys
chmod 600 /etc/ssh/ssh_host_*_key
chmod 644 /etc/ssh/ssh_host_*_key.pub
chmod 644 /etc/ssh/sshd_config
chmod 600 /etc/ssh/sshd_config.d/*.conf
判断:私钥必须 600,公钥可读但 sshd 配置不能 world-writable。
23.8 数据库备份目录
bash
mkdir -p /backup/mysql
chown root:backup /backup/mysql
chmod 750 /backup/mysql
chmod 640 /backup/mysql/*.sql.gz
chattr +a /backup/mysql/$(date +%Y%m%d).sql.gz
判断:备份文件不能 world-readable,append only 防止追加篡改。
23.9 临时部署目录
bash
mkdir -p /opt/deploy/staging
chown deploy:deploy /opt/deploy/staging
chmod 2750 /opt/deploy/staging
chmod 1770 /opt/deploy/staging/tmp
判断:部署目录 setgid 保证组内一致;tmp 加 sticky。
23.10 Kubernetes 节点目录
bash
chown root:root /var/lib/kubelet
chmod 750 /var/lib/kubelet
chmod 600 /etc/kubernetes/admin.conf
chmod 600 /etc/kubernetes/scheduler.conf
chmod 600 /var/lib/kubelet/kubeconfig
判断:Kubernetes 配置属于高敏感,必须 600。
二十四、运维自查清单
每台机器每周或每次发布后跑:
bash
# 1. SUID/SGID 基线
find / -perm /6000 -type f 2>/dev/null | sort > /tmp/now-suid
diff /var/lib/baseline/suid.txt /tmp/now-suid
# 2. 777 文件
find / \( -path /proc -o -path /sys \) -prune -o -type f -perm 0777 -print
# 3. world-writable 但无 sticky 目录
find / \( -path /proc -o -path /sys \) -prune -o -type d -perm -0002 ! -perm -1000 -print
# 4. 没有属主的文件
find / \( -path /proc -o -path /sys \) -prune -o \( -nouser -o -nogroup \) -print
# 5. auditd 状态
systemctl status auditd
auditctl -l | wc -l
# 6. SELinux 模式
getenforce
sestatus
# 7. umask
umask
# 8. /tmp sticky
ls -ld /tmp /var/tmp
# 9. SSH 权限
ls -ld /etc/ssh /root/.ssh /home/*/.ssh
ls -l /etc/ssh/ssh_host_*_key
# 10. 关键二进制完整性
rpm -Vf /usr/bin/passwd /usr/bin/sudo /usr/sbin/sshd
判断:每周把这些输出归档,做趋势分析。
二十五、与 chmod 相关的常见坑
25.1 chmod -R 755 /
恢复方法:单用户模式启动,用 rpm 重新设置权限,或从备份恢复。
教训:永远不要 chmod -R /,永远加 -type 区分文件/目录。
25.2 chown -R nobody:nogroup /
nobody 是保留用户,把生产目录改成 nobody 后所有业务崩溃。
恢复方法:从备份或包管理还原。
教训:改属主前确认 UID/GID 真实存在。
25.3 rm -rf /etc/
恢复方法:单用户 + 备份 + rpm 重装相关包。
教训:rm -rf 不要带根路径;用脚本要 set -u、set -e 严格模式。
25.4 chattr +i /etc/resolv.conf
业务需要改 DNS 时改不动。
恢复方法:chattr -i /etc/resolv.conf。
教训:上线前先确认所有需要热改的文件都没有 +i。
25.5 ACL 错设导致业务停摆
典型场景:mask 设为 0,所有非 owner 失效。
恢复方法:setfacl -m m::rwx /path。
教训:改 ACL 后用 getfacl 复核,再让业务验证。
25.6 SELinux disable 后业务启动失败
典型场景:紧急排错时设 permissive,下次重启回到 enforcing 后挂掉。
恢复方法:restorecon -R /opt/myapp、setenforce 1。
教训:临时改 SELinux 后立即规划回归 enforcing。
25.7 setcap cap_chown=+ep /
给核心二进制错误能力,破坏安全模型。
恢复方法:setcap -r /path。
教训:setcap 前确认能力不会绕过现有安全策略。
25.8 chmod a+w /usr/bin
给所有用户写系统二进制权限,恶意用户可以替换。
恢复方法:rpm -V 找出被改文件,从 RPM 包恢复。
教训:绝对不要让系统目录 world-writable。
二十六、监控与告警建议
26.1 告警项
- 新增 SUID 文件 → 告警
- 出现 777 文件(除 /tmp 等明确位置) → 告警
- 关键文件属主变化 → 告警
- auditd 规则数减少 → 告警
- SELinux 模式从 enforcing 变为 permissive → 告警
- /etc/passwd、/etc/shadow 改动 → 告警
26.2 阈值
阈值要结合业务基线。比如:
- SUID 文件总数:基线 + 5% 触发告警
- 777 文件数:>0 触发告警(除明确白名单)
- auditd perm_mod 速率:基线 + 3 sigma 告警
26.3 通知渠道
- 严重:短信 + 钉钉 + 邮件
- 警告:钉钉 + 邮件
- 提示:邮件 + 看板
26.4 看板内容
- 当前 SUID 文件数趋势
- 777 文件清单
- perm_mod 操作速率
- SELinux/AppArmor deny 速率
- 最近 24h 权限变更事件
二十七、自动化建议
把”权限基线”写成代码,让 CI 强制执行:
yaml
# .gitlab-ci.yml
perm-check:
stage: test
script:
- ./scripts/perm_audit.sh
artifacts:
paths:
- perm_audit.log
判断:CI 拒绝 777、新增 SUID、属主属组不符合预期。
二十八、文档与变更管理
权限变更需要走变更单,至少包括:
- 变更原因
- 变更前后属主属组
- 变更前后权限三元组
- ACL 变更明细
- chattr 变更
- 影响范围
- 验证人
- 回滚步骤
- 变更时间窗口
- 业务影响评估
判断:纸质或电子工单都行,关键是可追溯。
二十九、写在最后的最后
chmod 777 看似一秒钟的事,背后是整个权限模型崩塌。一个小小的 777,让权限、ACL、SELinux 三道墙全部失效。事故发生后,定位难、修复慢、影响广。
正确的思路:
- 最小权限:先假设所有权限都没有,按需打开
- 精细授权:用 ACL、setgid、sticky 而不是开 777
- 锁定保护:关键文件 +i / +a
- 审计可追:所有 perm_mod 类操作都进 auditd
- 配置管理:用 Ansible/Salt 统一基线
- 持续巡检:每周跑一次 perm_audit.sh
- CI 闸口:新代码不能引入 777
- 变更单留痕:每次改权限有据可查
- 回滚脚本:事故时 5 分钟内恢复
- 团队共识:让所有人理解”为什么不能 777″
回到最初那一句话:权限一旦放松就很难收回,事故一旦发生定位极难。生产环境靠最小权限、靠 ACL、靠审计、靠工具,不靠 777。
文末福利
今天给大家分享一份超级牛掰的Linux学习笔记,足足有1456页!是一位Linux运维大佬整理分享的,分享是获得大佬同意的,大家有需要的尽管收藏起来!
笔记介绍
这份笔记非常全面且详细,从Linux基础到shell脚本,再到防火墙、数据库、日志服务管理、Nginx、高可用集群、Redis、虚拟化、Docker等等,与其说Linux学习笔记,不如说是涵盖了运维各个核心知识。
并且图文并茂,代码清晰,每一章下面都有更具体详细的内容,十分适合Linux运维学习参考!
笔记展示
笔记下载
扫描下方二维码,回复暗号“1456页Linux笔记“,即可100%免费领取成功
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:马哥Linux运维 点击关注 👉 点击关注 👉《Linux 文件权限详解:为什么 chmod 777 是运维大忌?》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论