Linux文件权限详解:为什么chmod777是运维大忌?

admin 2026-06-30 08:19:04 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文深入分析Linux文件权限管理,重点揭示chmod777命令的安全风险,包括权限失控、审计失效和攻击面扩大等问题。文档系统讲解权限位三元组、特殊位、ACL、文件属性、umask及SELinux等核心机制,提供从基础权限到高级控制的完整知识体系。针对运维实际场景,给出精细化权限配置方案和排查流程,强调最小权限原则与安全替代方案。 综合评分: 85 文章分类: 安全建设,解决方案,技术标准,安全意识,运维经验


cover_image

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 真的会”治好”问题吗?事实上:

  1. 它把”权限错误”变成了”任何人可改”,看起来不报错,但是潜在的事故
  2. 它让日志审计、文件溯源彻底失效
  3. 它给攻击者横向越权的机会
  4. 它容易触发合规检查失败

这一篇不讲空话,全部围绕”现象 → 命令 → 输出 → 判断 → 修复 → 验证 → 回滚”展开,把 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 工作在三步:

  1. 进程标签(domain)
  2. 文件标签(type)
  3. 策略规则(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_CHOWNCAP_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

  1. 先看属主属组:ls -ln 或 stat
  2. 再看传统权限:ls -l
  3. 看是否有 ACL:getfacl
  4. 看是否有 setuid/setgid/sticky:ls -l 开头字符是否为 stST
  5. 看是否有 chattr 属性:lsattr
  6. 看 umask:umask
  7. 看进程身份:ps -efid
  8. 看 SELinux/AppArmor 拒绝日志:ausearchaudit2allowjournalctl -t audit
  9. 看挂载选项:mount | grep noexecnosuid
  10. 看权限继承:父目录权限、ACL mask
  11. 看日志:auditd、/var/log/secure、journalctl
  12. 看进程能力:getcap、ps -ef 里进程的 cap 位
  13. 看 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 第三步:选择正确的修复方案

推荐三条路径,按优先级排序:

  1. 把配置文件属主改为 myapp:chown myapp:myapp /etc/myapp/app.conf
  2. 加入 myapp 组并设 640:chgrp myapp /etc/myapp/app.conf && chmod 640 /etc/myapp/app.conf
  3. 用 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 auditjournalctl -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,按关键字 chmodchowndenied 建索引,关联用户、IP、时间。

九、排查路径

9.1 “Permission denied” 类

  1. 属主属组是不是期望用户
  2. 文件权限三元组是否包含运行用户的位
  3. 是否在属组内:检查 id 和 groups
  4. 是否有 ACL:检查 getfacl 中 mask 是否遮盖了有效权限
  5. 父目录是否有 x 位:缺 x 意味着连进入都做不到
  6. 是否有 SELinux/AppArmor:检查 ausearch 和 journalctl
  7. 是否有挂载选项 noexec、nosuid、nodev
  8. 是否被 chattr +i 锁定
  9. 看文件系统:FAT/NTFS 挂载对权限的处理不一样
  10. 看 capabilities:进程能力是否被裁剪

9.2 “为什么别人能删我的文件” 类

  1. 父目录是否有 sticky bit
  2. 文件属主是不是自己
  3. 父目录是否被设为 777,导致其他人能直接删
  4. 是否有 inotify、auditd 抓到删除来源
  5. 是否 NFS/SMB 客户端的属主映射问题

9.3 “为什么脚本不能执行” 类

  1. 文件权限是否有 x 位
  2. 所在分区是否有 noexec 挂载
  3. 脚本解释器(shebang)路径是否存在
  4. SELinux 是否把文件标为 unconfined_t 或脚本类域
  5. 文件系统是否不支持可执行(FAT/NTFS 挂载)
  6. 是否被脚本签名机制阻止

9.4 “为什么 sudo 不工作” 类

  1. /etc/sudoers

    语法:visudo -c

  2. 是否在 wheel/sudo 组:idgroups

  3. /etc/sudoers.d/

    内是否有同名文件冲突

  4. pam 配置是否启用

  5. securetty、pam_nologin 是否拒绝

  6. sudo 时间戳过期:清空 /var/db/sudo/<user> 或 sudo -k

9.5 “为什么 ACL 不生效” 类

  1. 文件系统是否支持 ACL:mount 时是否带 acl 选项(现代默认开)
  2. mask 是否过紧:用 setfacl -m m::rwx 重置
  3. 是否被传统三元组覆盖:检查 ls -l 末尾是否有 + 号
  4. 默认 ACL 是否对已存在的文件无效:用 getfacl 看 default 段

9.6 “为什么 chattr +i 后还是被改” 类

  1. 文件是否在容器 overlay 层
  2. 是否在只读文件系统上(如 squashfs)
  3. 是否 NFS 共享,本地 chattr 不会被同步到服务端
  4. 是否有进程以 root 持文件锁,需要先解锁

9.7 “为什么 SSH 公钥认证失败” 类

  1. ~/.ssh

    必须是 700

  2. authorized_keys

    必须是 600

  3. ~/.ssh

    的属主属组是否正确

  4. StrictModes yes

    是否触发

  5. SELinux restorecon -R ~/.ssh

9.8 “为什么 cron 不执行” 类

  1. /etc/crontab

    /etc/cron.d/* 权限必须是 600

  2. /var/spool/cron 目录权限

  3. 用户是否在 /etc/cron.allow、/etc/cron.deny 里

  4. pam 是否启用了 cron 限制

9.9 “为什么 NFS 上 chmod 无效” 类

  1. 是否启用了 root_squash(默认开启)
  2. 服务端文件系统是否支持 ACL
  3. 客户端 mount 选项是否带 noacl 关闭 ACL
  4. 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

    sudoping 等 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&nbsp;-a /etc/myapp/app.conf.bak /etc/myapp/app.conf
chown&nbsp;root:root /etc/myapp/app.conf
chmod&nbsp;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&nbsp;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&nbsp;-euo pipefail

BACKUP_DIR=${1:?usage: rollback_perms.sh <backup_dir>}

while&nbsp;IFS=:&nbsp;read&nbsp;-r path owner group mode;&nbsp;do
&nbsp; &nbsp;&nbsp;chown&nbsp;"$owner:$group"&nbsp;"$path"
&nbsp; &nbsp;&nbsp;chmod&nbsp;"$mode"&nbsp;"$path"
done&nbsp;<&nbsp;"$BACKUP_DIR/manifest.txt"

echo&nbsp;"rolled back"

判断:先用 find / -ls > manifest.txt 做基线,事故时用脚本还原。

12.7 ACL 全量重置

bash

setfacl -R -b /opt/app
chmod&nbsp;755 /opt/app

仅在确认 ACL 不是业务依赖时才使用。

12.8 误设 SUID 回滚

bash

chmod&nbsp;u-s /usr/local/bin/myapp

并审计是否有其他文件被错配。

12.9 SELinux 标签回滚

bash

restorecon -R /etc/myapp

判断:修复了 SELinux 标签错配后,应用重新可以被正确访问。

12.10 umask 回滚

bash

sed -i&nbsp;'/umask/d'&nbsp;/etc/profile
echo&nbsp;"umask 022"&nbsp;> /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,真正的修复路径是:

  1. 明确属主、属组、运行进程 UID
  2. 找到最小权限组合(4/2/1 三个数字的组合)
  3. 用 ACL 处理例外
  4. 用 chattr +i 锁关键文件
  5. 启用 auditd 做权限审计
  6. 用配置管理工具做基线
  7. 留好回滚脚本
  8. SELinux/AppArmor 不要关
  9. SUID/SGID 做好基线
  10. 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&nbsp;777 /etc/myapp -R
systemctl restart myapp

服务恢复,看似 OK。但接下来三天:

  • 配置文件被人改成 EnableDebugMode=true,泄露内部信息

  • /etc/myapp/secrets.key

    被其他用户读走

  • 出现大量未授权访问

15.4 根因定位

复盘发现,根因是更早一次迁移时:

  • /etc/myapp

    属主改为 root,但属组误改为 nobody

  • 文件权限被工具统一刷成 640

  • nobody 组没有任何成员,导致没人能读

正确修复本应是:

bash

chgrp&nbsp;myapp /etc/myapp/app.conf
chmod&nbsp;640 /etc/myapp/app.conf

不需要 777,不需要 chown -R。

15.5 教训提炼

  1. 永远不要用 777 治 Permission denied
  2. 改权限前先看属主属组、看 ACL、看 SELinux
  3. 用 auditd 抓到所有 perm_mod 类操作
  4. 在 CI 加 777 检查
  5. 把”最小权限”作为新人入职第一课
  6. 把”改权限”列入变更单必填项

十六、运维手册:日常权限巡检 checklist

每周跑一遍的权限巡检脚本:

bash

#!/bin/bash
# perm_audit.sh
set&nbsp;-uo pipefail

echo&nbsp;"=== SUID/SGID baseline ==="
find / -perm /6000 -type&nbsp;f 2>/dev/null |&nbsp;sort&nbsp;> /tmp/perm_audit/suid.now
diff -u /var/lib/baseline/suid.txt /tmp/perm_audit/suid.now ||&nbsp;echo&nbsp;"SUID baseline changed!"

echo&nbsp;"=== 777 files ==="
find /etc /opt /var /srv /home -type&nbsp;f -perm 0777 2>/dev/null > /tmp/perm_audit/777.files
if&nbsp;[[ -s /tmp/perm_audit/777.files ]];&nbsp;then
&nbsp; &nbsp;&nbsp;echo&nbsp;"WARN: 777 files found:"
&nbsp; &nbsp;&nbsp;cat&nbsp;/tmp/perm_audit/777.files
fi

echo&nbsp;"=== /tmp sticky bit ==="
ls&nbsp;-ld /tmp /var/tmp

echo&nbsp;"=== /etc/shadow /etc/passwd ==="
ls&nbsp;-l /etc/shadow /etc/passwd

echo&nbsp;"=== auditd rules ==="
auditctl -l |&nbsp;wc&nbsp;-l

echo&nbsp;"=== 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 chmodman chownman aclman chattrman 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&nbsp;-R root:myapp /opt/myapp
chmod&nbsp;750 /opt/myapp

find /opt/myapp -type&nbsp;d -exec&nbsp;chmod&nbsp;750 {} \;
find /opt/myapp -type&nbsp;f -exec&nbsp;chmod&nbsp;640 {} \;
find /opt/myapp/bin -type&nbsp;f -exec&nbsp;chmod&nbsp;750 {} \;

chmod&nbsp;1770 /opt/myapp/uploads
chmod&nbsp;1770 /opt/myapp/cache

判断:root 拥有,myapp 组可读;可执行文件 750;可写目录加 sticky。

23.2 配置目录

bash

chown&nbsp;-R root:myapp /etc/myapp
chmod&nbsp;750 /etc/myapp

find /etc/myapp -type&nbsp;f -exec&nbsp;chmod&nbsp;640 {} \;
find /etc/myapp -type&nbsp;f -name&nbsp;"*.secret"&nbsp;-exec&nbsp;chmod&nbsp;600 {} \;

chattr +i /etc/myapp/app.conf

判断:敏感配置 +i 锁定,密钥文件 600。

23.3 共享上传目录

bash

mkdir&nbsp;-p /srv/upload
chown&nbsp;root:upload /srv/upload
chmod&nbsp;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&nbsp;-R mysql:mysql /var/lib/mysql
chmod&nbsp;750 /var/lib/mysql

find /var/lib/mysql -type&nbsp;f -exec&nbsp;chmod&nbsp;660 {} \;
find /var/lib/mysql -type&nbsp;d -exec&nbsp;chmod&nbsp;750 {} \;

chmod&nbsp;700 /var/lib/mysql-keyring

判断:MySQL 数据目录必须属主是 mysql,socket 文件要特殊处理。

23.5 Redis 数据目录

bash

chown&nbsp;-R redis:redis /var/lib/redis
chmod&nbsp;750 /var/lib/redis
chmod&nbsp;600 /etc/redis/redis.conf

判断:Redis 单进程模型,权限简单但要保证配置文件不被其他用户读。

23.6 日志目录

bash

chown&nbsp;root:syslog /var/log
chmod&nbsp;775 /var/log

for&nbsp;app&nbsp;in&nbsp;myapp nginx;&nbsp;do
&nbsp; &nbsp;&nbsp;mkdir&nbsp;-p /var/log/$app
&nbsp; &nbsp;&nbsp;chown&nbsp;$app:adm /var/log/$app
&nbsp; &nbsp;&nbsp;chmod&nbsp;750 /var/log/$app
done

chmod&nbsp;640 /etc/logrotate.d/*

判断:日志目录允许 adm 组读取,应用用户可写自己目录。

23.7 SSH 配置目录

bash

chmod&nbsp;700 /root/.ssh
chmod&nbsp;600 /root/.ssh/authorized_keys
chmod&nbsp;600 /etc/ssh/ssh_host_*_key
chmod&nbsp;644 /etc/ssh/ssh_host_*_key.pub
chmod&nbsp;644 /etc/ssh/sshd_config
chmod&nbsp;600 /etc/ssh/sshd_config.d/*.conf

判断:私钥必须 600,公钥可读但 sshd 配置不能 world-writable。

23.8 数据库备份目录

bash

mkdir&nbsp;-p /backup/mysql
chown&nbsp;root:backup /backup/mysql
chmod&nbsp;750 /backup/mysql
chmod&nbsp;640 /backup/mysql/*.sql.gz
chattr +a /backup/mysql/$(date&nbsp;+%Y%m%d).sql.gz

判断:备份文件不能 world-readable,append only 防止追加篡改。

23.9 临时部署目录

bash

mkdir&nbsp;-p /opt/deploy/staging
chown&nbsp;deploy:deploy /opt/deploy/staging
chmod&nbsp;2750 /opt/deploy/staging
chmod&nbsp;1770 /opt/deploy/staging/tmp

判断:部署目录 setgid 保证组内一致;tmp 加 sticky。

23.10 Kubernetes 节点目录

bash

chown&nbsp;root:root /var/lib/kubelet
chmod&nbsp;750 /var/lib/kubelet
chmod&nbsp;600 /etc/kubernetes/admin.conf
chmod&nbsp;600 /etc/kubernetes/scheduler.conf
chmod&nbsp;600 /var/lib/kubelet/kubeconfig

判断:Kubernetes 配置属于高敏感,必须 600。

二十四、运维自查清单

每台机器每周或每次发布后跑:

bash

# 1. SUID/SGID 基线
find / -perm /6000 -type&nbsp;f 2>/dev/null |&nbsp;sort&nbsp;> /tmp/now-suid
diff /var/lib/baseline/suid.txt /tmp/now-suid

# 2. 777 文件
find / \( -path /proc -o -path /sys \) -prune -o -type&nbsp;f -perm 0777 -print

# 3. world-writable 但无 sticky 目录
find / \( -path /proc -o -path /sys \) -prune -o -type&nbsp;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 |&nbsp;wc&nbsp;-l

# 6. SELinux 模式
getenforce
sestatus

# 7. umask
umask

# 8. /tmp sticky
ls&nbsp;-ld /tmp /var/tmp

# 9. SSH 权限
ls&nbsp;-ld /etc/ssh /root/.ssh /home/*/.ssh
ls&nbsp;-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 -uset -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/myappsetenforce 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:
&nbsp;&nbsp;stage:&nbsp;test
&nbsp;&nbsp;script:
&nbsp; &nbsp;&nbsp;-&nbsp;./scripts/perm_audit.sh
&nbsp;&nbsp;artifacts:
&nbsp; &nbsp;&nbsp;paths:
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;perm_audit.log

判断:CI 拒绝 777、新增 SUID、属主属组不符合预期。

二十八、文档与变更管理

权限变更需要走变更单,至少包括:

  • 变更原因
  • 变更前后属主属组
  • 变更前后权限三元组
  • ACL 变更明细
  • chattr 变更
  • 影响范围
  • 验证人
  • 回滚步骤
  • 变更时间窗口
  • 业务影响评估

判断:纸质或电子工单都行,关键是可追溯。

二十九、写在最后的最后

chmod 777 看似一秒钟的事,背后是整个权限模型崩塌。一个小小的 777,让权限、ACL、SELinux 三道墙全部失效。事故发生后,定位难、修复慢、影响广。

正确的思路:

  1. 最小权限:先假设所有权限都没有,按需打开
  2. 精细授权:用 ACL、setgid、sticky 而不是开 777
  3. 锁定保护:关键文件 +i / +a
  4. 审计可追:所有 perm_mod 类操作都进 auditd
  5. 配置管理:用 Ansible/Salt 统一基线
  6. 持续巡检:每周跑一次 perm_audit.sh
  7. CI 闸口:新代码不能引入 777
  8. 变更单留痕:每次改权限有据可查
  9. 回滚脚本:事故时 5 分钟内恢复
  10. 团队共识:让所有人理解”为什么不能 777″

回到最初那一句话:权限一旦放松就很难收回,事故一旦发生定位极难。生产环境靠最小权限、靠 ACL、靠审计、靠工具,不靠 777。

文末福利

今天给大家分享一份超级牛掰的Linux学习笔记,足足有1456页!是一位Linux运维大佬整理分享的,分享是获得大佬同意的,大家有需要的尽管收藏起来!

笔记介绍

这份笔记非常全面且详细,从Linux基础到shell脚本,再到防火墙、数据库、日志服务管理、Nginx、高可用集群、Redis、虚拟化、Docker等等,与其说Linux学习笔记,不如说是涵盖了运维各个核心知识。

并且图文并茂,代码清晰,每一章下面都有更具体详细的内容,十分适合Linux运维学习参考!

笔记展示

笔记下载

扫描下方二维码,回复暗号“1456页Linux笔记“,即可100%免费领取成功


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:马哥Linux运维 点击关注 👉 点击关注 👉《Linux 文件权限详解:为什么 chmod 777 是运维大忌?》

评论:0   参与:  0