分类课程智能体AI
文章
订阅
分类课程AI导师
文章
价格
课程进度
9 / 25
上一节键盘技巧:把命令行变成可编辑、可回退的工作区下一节Linux 进程管理:从观测、作业控制到安全终止
自在学

© 2025 - 2026 株洲市自在学教育科技有限公司 版权所有

公网安备湘公网安备43020302000292号 | 湘ICP备2025148919号-1

关于我们隐私政策使用条款

© 2025 - 2026 株洲市自在学教育科技有限公司 版权所有

公网安备湘公网安备43020302000292号湘ICP备2025148919号-1

编程LinuxLinux 权限管理:从进程身份到文件、目录与最小授权

Linux 权限管理:从进程身份到文件、目录与最小授权

遇到 Permission denied 时,很多人的第一反应是 chmod 777。它有时确实会让报错暂时消失,却把真正的问题也一起盖住了:究竟是谁在访问,访问路径走到了哪一级,目标是文件还是目录,操作需要哪一组权限,ACL 或挂载策略有没有再加一道门?

Linux 的权限判断不是一张贴在文件上的静态标签。发起操作的是进程,进程带着 UID、GID 和附加组;路径上的每个目录都有自己的 mode;最终对象还可能受 ACL、挂载参数、capabilities 与强制访问控制约束。把这些证据按顺序对齐,权限问题就从“玄学报错”变成了可以逐项验证的判断题。

下面从这一条判断链出发。你会看到普通文件和目录的 r/w/x 为什么完全不同,删除为什么由父目录控制,umask 为什么不是简单的默认权限,setgid 与 sticky 怎样服务共享目录,以及 su、sudo 到底改变了身份、认证和授权中的哪一环。


把一次访问拆成五个问题

一次文件操作至少包含五个要素:

  1. 主体是谁:发起系统调用的进程带着什么文件系统 UID/GID、有效身份和附加组。
  2. 路径能否走通:从 / 或当前目录出发,每一级目录是否允许搜索,也就是是否有目录 x。
  3. 对象属于谁:目标 inode 的 owner、group、mode 和 ACL 是什么。
  4. 要做哪种操作:读内容、写内容、执行、列目录、创建目录项、删除或重命名,需要的权限并不相同。
  5. 还有没有额外规则:只读、noexec、nosuid 挂载,capabilities,SELinux/AppArmor,文件属性等都可能改变结果。

这五问有一个固定顺序。路径走不通时,目标文件即使是 777 也没有用;删除目录项时,盯着目标文件的写位也会找错对象;ACL 生效时,只看 ls -l 的九位又不够。

Linux 文件访问的五步判断链:进程身份、路径、对象、操作与额外策略

图:权限排查不是反复试数字,而是沿系统实际做判断的顺序收集证据。

chmod 777 同时把读、写、执行开放给所有身份。它既不能修复缺少父目录搜索权限、只读挂载或 MAC 拒绝,也常常制造新的写入与执行面。先确定缺的是哪一位,再只改那一位。

1
排查一个深层路径的 Permission denied 时,哪些信息应当先进入证据链?

先确定是谁在访问

真实、有效与文件系统身份

内核主要使用数字身份,不依赖人类可读的用户名。一个进程同时可能有多组 UID/GID:

  • 真实 UID/GID说明进程最初由谁启动,常用于归属、信号与某些认证语义。
  • 有效 UID/GID通常决定进程访问共享资源时拥有什么权限。setuid/setgid 可执行文件可能让有效身份暂时不同于真实身份。
  • 保存 UID/GID让受控程序可以暂时放下有效权限,之后再切回允许的身份。
  • Linux 还有文件系统 UID/GID,专门用于文件访问判断;正常情况下它们跟随有效 UID/GID,因此日常排查通常可以把有效身份当作起点,但要知道这层实现细节存在。

不带用户名运行 id,查看的是当前进程继承的身份向量:

shell
id
id -u
id -ru
id -g
id -G

id -u 默认给出有效 UID,id -ru 给出真实 UID。大多数普通 Shell 中二者相同;在受控身份切换程序里才可能看到差异。给 id 传一个用户名时,它会重新查询账号和组数据库,那是在问“数据库里这个用户应有哪些组”,不等于某个早已登录的进程已经刷新了自己的组向量。

2
用户名是内核进行文件权限判断时保存和比较的核心身份。

主组与附加组共同参与匹配

用户有一个主组,也可以属于多个附加组。登录会话建立时,主 GID 与附加组列表被写入进程凭据,子进程继续继承。文件组只要匹配进程的有效 GID或任一附加 GID,就能进入 group 权限类。

例如:

text
uid=2102(bob) gid=2300(auditors) groups=2300(auditors),2200(team)

若文件是 alice:team,bob 虽然主组是 auditors,仍会因为附加组含 team 而匹配文件的 group 类。修改 /etc/group 或运行用户管理命令后,旧会话的进程凭据不会凭空更新;重新建立会话,或由明确的组切换机制启动新进程,才会拿到新组列表。

3
bob 的主组是 auditors,附加组包含 team;文件所属组为 team。若 bob 不是文件 owner,传统 mode 应选择哪一组权限?

传统三组 mode 的选择是互斥分支:进程身份匹配文件 owner 时,只看 owner 位;否则,文件组匹配有效组或附加组时,只看 group 位;再否则才看 other。owner 位不足时不会再向 group 或 other 回退。

4
文件 owner 同时也是文件所属组成员,但 owner 位没有写权限,group 位有写权限。这个 owner 能否因为组成员身份而写入?

用 ls、stat、namei 和 id 组成证据链

ls 与 stat 看对象本身

ls -l 的第一列有十个主字符:第一个是文件类型,后九个依次是 owner、group、other 的 rwx。例如:

text
-rw-r----- 1 alice team 128 Jul 15 09:18 report.txt

这表示普通文件、owner 可读写、group 只读、other 无权限。权限串后面若出现 +,通常提示存在 ACL 等扩展访问方法,应该继续运行 getfacl,不能把九位当成全部规则。

stat 更适合做精确核对:

shell
stat -c 'mode=%A octal=%a owner=%U(%u) group=%G(%g) type=%F' report.txt

可能得到:

text
mode=-rw-r----- octal=640 owner=alice(2101) group=team(2200) type=regular file

数字 UID/GID 能排除名称解析歧义,%a 能排除肉眼数错权限位。stat link 默认看符号链接自身,stat -L link 才解引用到目标;诊断链接时应该把两者都看一遍。

5
ls -l 的九位权限串后出现加号时,下一步最合适的命令是什么?

namei 找出路径上第一扇关着的门

访问 /srv/team/project/report.txt 时,进程必须依次搜索 /、/srv、/srv/team 和 /srv/team/project。任何一个中间目录缺少适用于该进程的 x,路径解析都会以 EACCES 失败,目标文件自己的 640 甚至还没有机会被检查。

shell
namei -l /srv/team/project/report.txt

namei -l 会逐级列出类型、mode、owner 和 group,是排查“文件看起来可读却打不开”的高效工具。路径中有符号链接时,它也能展示跳转后重新开始解析的各级目录。配合以下命令可以把链接本体和最终对象分开:

shell
readlink -f path/to/link
stat path/to/link
stat -L path/to/link

权限证据链:id、ls、stat、namei 与 getfacl 的分工

图:先证明进程身份,再证明路径和对象;每条命令回答的问题不同。

6
只要目标文件对进程可读,中间目录缺少 x 也不影响访问。
7
想同时核对文件的八进制 mode、数值 UID 和数值 GID,哪种证据最直接?

普通文件与目录的 rwx 不是一回事

普通文件:内容与执行入口

对普通文件,三个位可以先这样理解:

权限直接控制不直接控制
r读取文件内容是否能列出它的文件名
w修改、截断文件内容是否能删除这个文件名
x请求把文件作为程序执行内容格式是否有效、挂载是否允许执行

x 只是执行检查的一关。文件还需要是内核可识别的二进制,或有合法 shebang 的解释器脚本;文件系统的 noexec 也可能阻止直接执行。脚本能否被解释器读取还涉及脚本的读权限和解释器行为,所以“有 x 就一定能跑”并不准确。

8
一个普通文件具有 x 权限后,仍可能因哪些原因无法直接执行?

目录:名字列表、目录项与路径搜索

目录保存的是名字到对象的映射。它的 r/w/x 语义因此变成:

目录权限含义单独拥有时的典型现象
r读取目录项名字能看到名字,但没有 x 时不能正常 stat 或打开子项
w新增、删除、重命名目录项通常还要 x 才能定位并完成操作
x搜索、穿越目录知道准确名字时可继续访问,但没有 r 不能枚举名字

这解释了一个很实用的组合:目录只有 x 时,无法 ls,但若知道 report.txt 的准确名字且目标文件允许读取,仍可打开它;目录只有 r 时,可能列出 report.txt,却无法继续解析并读取。

普通文件与目录的 rwx 语义对照矩阵

图:同一个字母落在不同对象类型上,保护的操作完全不同。

9
某目录对用户只有 x,用户知道其中 data.txt 的准确名字,且文件自身可读。最可能的结果是什么?

删除和重命名改变的是父目录中的目录项。一个 0400 的只读文件,只要攻击者对父目录有 w+x,仍可能被删除;反过来,即使文件自身可写,父目录不允许修改目录项,也不能靠文件 w 删除它。sticky 位会在共享目录上再收紧这条规则,后面单独说明。

10
把文件改成 0444 就足以防止任何其他用户删除它。

chmod:精确表达需要改变哪一位

八进制模式适合表达完整目标状态

每组 rwx 是三个比特:r=4、w=2、x=1。一位八进制数正好覆盖一组:

数字权限数字权限
7rwx3-wx
6rw-2-w-
5r-x1--x
4r--0---

chmod 0640 report.txt 把三组完整设为 owner 读写、group 只读、other 无权限。前导 0 在 chmod 命令中不是必需,但写成四位能清楚表明这是八进制 mode;若包含特殊位,则用 4755、2750、1777 等四位主体表示。

八进制适合“我知道最终状态应该是什么”。它会整体覆盖相关位,因此对已有目录树批量使用前要确认不会误删某些对象原有的执行或特殊位。

11
chmod 0640 report.txt 的准确结果是什么?

符号模式适合表达增量意图

符号模式由“对象、操作、权限”组成:

  • u/g/o/a 分别是 owner、group、other、all。
  • +/-/= 分别是添加、移除、精确设为。
  • 权限部分可以是 r/w/x/X/s/t,也可以引用另一组的当前权限。
shell
chmod u+x deploy.sh          # 只给 owner 增加执行
chmod go-w report.txt        # 移除 group 和 other 的写
chmod o= private.txt         # other 精确设为空
chmod g=u shared.txt         # 把 owner 当前 rwx 复制给 group
chmod u=g shared.txt         # 把 group 当前 rwx 复制给 owner
chmod -R a+X public-tree     # 目录加 x,普通文件仅在原有执行位时加 x

X 是递归场景里很有价值的条件执行位:对象是目录,或普通文件原先已有任一执行位时,它才产生效果。相比 chmod -R a+x,它不会把图片、日志和数据文件全部标成可执行。

省略 u/g/o/a 时,符号模式会受到当前 umask 对类别的限制。为了让脚本意图可读,推荐明确写出对象,例如 a+r、u+x,不要依赖读者猜省略规则。

chmod 八进制模式、符号模式与条件执行位 X 的转换关系

图:数字模式描述完整结果,符号模式描述相对变化,X 保留文件与目录的语义差别。

12
当前 owner 权限为 rwx,group 为 r--。执行 chmod u=g file 后会发生什么?
13
chmod -R a+X tree 会无条件把 tree 下每个普通文件都变成可执行。

umask 是创建时的清除掩码

创建新对象时,程序会向内核请求一个 mode,umask 再从请求中清除相应位:

text
最终 mode = 请求 mode & ~umask

常见工具创建普通文件时请求 0666,创建目录时请求 0777。普通文件起点没有执行位,是因为“刚写入的一串字节”不应自动变成可执行程序。以 umask 0027 为例:

text
普通文件:0666 & ~0027 = 0640
目录:    0777 & ~0027 = 0750

不要把它机械背成十进制减法。umask 是按位清除;程序若主动只请求 0600,umask 不会给它补成更宽权限。父目录存在 default ACL 时,还会使用 ACL 继承与创建请求共同计算,新对象结果不能只看当前 Shell 的 umask。

shell
umask
umask 027
touch demo-file
mkdir demo-dir
stat -c '%n %a %A' demo-file demo-dir

umask 是进程状态,子进程会继承。把它改成 077 不会追溯修改已经存在的 0640 文件,只会影响之后的创建;某些处理秘密数据的程序还会主动采用比 Shell 更严格的请求或掩码。

umask 从 0666 与 0777 中按位清除权限的过程

图:umask 只做减法意义上的“收窄”,不会添加程序没有请求的权限,也不追溯旧对象。

14
关于 umask,哪些说法正确?

chown、chgrp 与部署时一次设对属性

chmod 改权限位,chown 改 owner 或同时改 group,chgrp 只改 group:

shell
chown alice report.txt
chown alice:team report.txt
chgrp team report.txt

改变 owner 通常需要 CAP_CHOWN;普通文件 owner 可以把 group 改成自己所属的有效组或附加组,不能随意转给任意组。改变可执行文件的 owner/group 还可能清除 setuid/setgid,避免把旧的身份提升语义意外带给新 owner。

符号链接要特别谨慎。chown link 通常跟随链接修改目标,chown -h link 才尝试改链接本身。对可能被其他进程改动的不可信目录树使用递归和跟随链接选项,可能出现检查与使用之间的竞态;先限定根目录、检查链接,再决定是否递归。

部署文件时,install 可以一次性复制并设定属性,减少“复制完成后短暂暴露错误权限”的窗口:

shell
install -o root -g app -m 0640 app.conf /etc/myapp/app.conf
install -d -o root -g app -m 0750 /var/lib/myapp

owner 是谁、group 与 mode 是什么,应该在部署定义中一起写清。分散的 cp、chown、chmod 不仅难审查,中途失败时还可能留下只完成一半的状态。

15
普通用户是某文件 owner,并且属于 team 组。下列哪项通常允许?

三个特殊位解决三类不同问题

setuid 与 setgid 可执行文件

普通可执行文件运行时,进程通常保持调用者的身份。二进制可执行文件设置 setuid 后,成功执行可能把有效 UID 设为文件 owner;setgid 类似地改变有效 GID。ls -l 会用 s 表示“特殊位和对应 x 同时存在”,用大写 S 表示“特殊位存在但对应 x 不存在”。

shell
chmod u+s program
chmod g+s program
chmod 4755 program

这不是通用的“给脚本提权”办法。Linux 忽略解释器脚本上的 setuid/setgid,实验中 4755 shell script 仍以调用者的真实和有效 UID运行。即便是二进制,底层文件系统使用 nosuid、进程设置 no_new_privs、被跟踪等条件也会让身份转换失效。

setuid 程序一旦有输入校验、路径搜索、环境处理或竞态漏洞,缺陷会在获得的身份下放大。现代服务更倾向把权限拆成较小的 capability、由服务管理器设定身份,或通过狭窄的 sudo 规则暴露必要动作。

16
给 Shell 脚本设置 4755,就能可靠地让任何执行者获得脚本 owner 的有效 UID。

setgid 目录让团队组稳定继承

setgid 放在目录上时,重点不再是执行身份,而是协作继承:新建文件和目录优先继承父目录的 group,新子目录通常继续带 setgid。这样团队目录不会因为创建者主组不同而逐渐混入多种 group。

shell
chgrp team /srv/project
chmod 2770 /srv/project

2770 表示 setgid 加上 owner/group 的 rwx。它只解决“新对象属于哪个组”,不会自动保证组可写:文件最终 mode 仍受创建请求、umask 与 default ACL 影响。若团队成员要互相编辑,常见做法是搭配 umask 0002 或更明确的 default ACL,并在沙盒里验证新文件和新目录的实际结果。

17
setgid 团队目录中,新文件已经继承 team 组,但同组成员仍不能写。最可能还要检查什么?

sticky 让共享目录可写但不互删

/tmp 常见 mode 是 1777:所有人都能创建目录项,但 sticky 限制删除和重命名。通常只有目标文件 owner、目录 owner 或具备相应权限的进程能删除该目录项。

shell
chmod 1777 shared-dropbox
ls -ld shared-dropbox
drwxrwxrwt ... shared-dropbox

末尾小写 t 表示 sticky 与 other 的 x 同时存在;大写 T 表示 sticky 存在但 other 没有 x。sticky 不会阻止别人读取一个公开可读文件,也不会给文件内容增加写保护,它只收紧共享可写目录中的删除和重命名规则。

18
在 1777 且 owner 为 root 的共享目录中,bob 通常能删除 alice 创建的文件吗?

setuid、setgid 目录与 sticky 三个特殊位的对象和效果对照

图:三个特殊位不能只背 4、2、1,还要把它们放回可执行文件或目录的具体语义。

19
下列哪些是特殊位与对象类型的正确搭配?

ACL 把三组权限扩展到命名用户和组

access ACL 与 mask 的交集

owner/group/other 不够表达“只给 bob 额外读写,但不让整个 team 写”时,可以使用 POSIX ACL:

shell
setfacl -m u:bob:rw report.txt
getfacl report.txt

典型输出可能是:

text
user::rw-
user:bob:rw-           #effective:r--
group::r--
mask::r--
other::---

user:bob:rw- 是条目声明,mask::r-- 是命名 user、文件所属组和命名 group 能获得的最大权限。bob 的有效权限是二者交集,所以这里只读。文件 owner 的 user:: 和 other:: 不受 ACL mask 限制。

ACL 的选择顺序仍然是分支:先匹配文件 owner;再匹配命名 user;再汇总所有匹配的文件组或命名 group,并与 mask 相交;最后才使用 other。ls -l 中 group 三位在扩展 ACL 存在时常对应 mask,因此仅凭 ls 不能看出是哪条命名规则被收窄。

ACL 命名用户条目与 mask 取交集得到 effective 权限

图:条目写着 rw 不代表实际 rw,mask 是命名 user/group 的共同上限。

20
ACL 中 user:bob:rw-,mask::r--。bob 不是文件 owner 时,有效权限是什么?

default ACL 是新子项的继承模板

目录可以有 default ACL:

shell
setfacl -m d:u:bob:rwX,d:m:rwx project
getfacl project

它不直接授权访问目录自身,而是在之后创建子项时复制成新对象的 access ACL。创建调用请求的 mode 仍是上限:普通文件即使从 default 条目继承了 rwx,若程序只请求 0666,执行位也会被清除;目录请求 0777 时则可能保留 x。

default ACL 不追溯已经存在的文件。设置完成后要分别创建一个文件和一个子目录,用 getfacl、stat 观察继承结果,而不是从父目录配置推断全部子项已经变化。

21
给目录设置 default ACL 后,目录中所有既有文件会立即获得同一 ACL。
22
ACL 排查时,哪些信息需要一起看?

mode 不是权限系统的全部

传统 mode 属于自主访问控制的一层。下面任一条件都可能让“九位看着没问题”却仍失败:

  • capabilities:Linux 把传统 root 权力拆成 CAP_DAC_OVERRIDE、CAP_DAC_READ_SEARCH、CAP_CHOWN 等单元。UID 0 在容器、用户命名空间或收窄后的 capability 集合中未必拥有所需单元。
  • 挂载参数:只读挂载阻止写入,noexec 限制直接执行,nosuid 让 setuid/setgid 与文件 capabilities 不产生提权效果。
  • ACL:命名规则和 mask 可能收窄或扩展三组表达能力。
  • 强制访问控制:SELinux、AppArmor 等策略可以在 mode 允许后继续拒绝。
  • 文件属性与应用规则:immutable、append-only 标志,服务自身的路径白名单和沙盒策略也可能参与。

可按环境选择证据命令:

shell
findmnt -no TARGET,OPTIONS --target path/to/object
getfacl path/to/object
getcap path/to/program
lsattr path/to/object
ls -Z path/to/object        # 仅在支持安全上下文的环境中有意义

root 也不能被描述成“无视一切”。传统超级用户通常能绕过很多自主访问检查,但执行一个所有执行位都关闭的普通文件仍有额外限制;只读文件系统、缺少 capability、MAC、命名空间和硬件/远端策略都可能继续约束它。准确说法是“检查当前进程实际拥有的身份与能力”,而不是看到 uid=0 就停止分析。

23
目标 mode 为 0777 仍然写失败,哪些方向值得继续检查?

su 与 sudo:切换身份不等于无限授权

su 与 su - 的差别首先在环境

su user 运行目标 UID/GID 的 Shell,但为了兼容通常保留较多调用方环境和当前目录;su - user 或 su --login user 会更接近一次登录:清理大部分环境,初始化 HOME、SHELL、USER、LOGNAME、PATH,并切到目标主目录。

shell
su alice
su - alice
su - alice -c 'id; pwd; printf "%s\n" "$HOME"'

认证由 PAM 和系统配置参与。普通场景中 su 常要求目标账号的凭据,但不能把这一点写成跨所有系统的绝对规则;root 调用、PAM 策略、账号状态都会改变结果。更重要的是,su 一旦给出交互 Shell,后续每条命令不再逐条经过 sudo 策略匹配,审计粒度与授权范围都更粗。

24
希望获得更接近目标用户登录会话的主目录和环境,通常应使用哪种形式?

sudo 把策略、认证与审计放在命令边界

sudo 前端会把调用者、目标身份、主机、命令与参数交给策略插件。常见 sudoers 部署会认证调用者并在短时间缓存凭据,但 NOPASSWD、targetpw、PAM 和其他策略都能改变认证方式,因此要查看具体规则:

shell
sudo -l
sudo -ll
sudo -n /usr/bin/id
sudo -k

sudo -l 列出授权,-n 禁止交互提示,适合自动化在需要密码时立即失败,sudo -k 使当前缓存凭据失效。编辑规则使用 visudo 或 visudo -f /etc/sudoers.d/name,先做语法检查再生效。

一个实验性的最小规则可以只允许 bob 以 root 运行 /usr/bin/id:

sudoers
bob ALL=(root) NOPASSWD: /usr/bin/id

这不允许 bob 读取 /etc/shadow,也不允许运行 /usr/bin/env。生产规则还要评估参数匹配、被授权程序是否有 Shell escape、插件加载或任意文件写入能力。允许编辑器、解释器、包管理器或用户可改写目录里的程序,常常等同于允许一条绕出命令白名单的路径。

sudo 可记录成功与失败尝试,策略也能配置输入/输出日志;但 I/O 记录不是“安装 sudo 就自动拥有”的保证,日志位置和保留方式也由系统日志与插件配置决定。审计设计必须写清记录什么、保存多久、谁能读、如何防篡改。

su、su - 与 sudo 在身份、环境、授权和审计上的边界

图:su 更像进入另一个身份会话,sudo 更适合把必要动作限定在一条策略边界内。

25
sudoers 只授权 bob 运行 /usr/bin/id。下列哪项最准确?
26
设计 sudo 最小权限规则时,哪些风险需要评估?

在一次性沙盒中验证整条权限链

下面的实操只在一次性 Debian 容器中创建临时用户、组和测试对象。这样做的原因不是“为了演示几条命令”,而是要让 owner、文件组、附加组、ACL mask 与 sticky 的判断条件彼此独立,同时避免改动日常账号和目录。容器退出后由 --rm 删除;脚本末尾仍显式清理实验目录并复查。

复制并运行以下命令。它安装最小 ACL 工具,创建 alice、bob、carol 和 team/auditors 两个组;bob 的主组是 auditors,附加组含 team,因此能验证“附加组也参与文件组匹配”。

shell
docker run --rm -i --name welearn-linux-ch09-practice \
  debian:bookworm-slim bash <<'LAB'
set -u
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq acl util-linux >/dev/null
 
LAB=/tmp/welearn-ch09
rm -rf "$LAB"
mkdir -p "$LAB"
groupadd -g 2200 team
groupadd -g 2300 auditors
useradd -m -u 2101 -g team -G auditors -s /bin/bash alice
useradd -m -u 2102 -g auditors -G team -s /bin/bash bob
useradd -m -u 2103 -g auditors -s /bin/bash carol
 
as_user() {
  user=$1
  shift
  set +e
  runuser -u "$user" -- "$@" 2>&1
  rc=$?
  set -e
  printf '[exit=%s user=%s]\n' "$rc" "$user"
}
set -e
 
printf '\n== 身份与对象证据 ==\n'
id alice
id bob
id carol
install -d -o alice -g team -m 0750 "$LAB/evidence"
install -o alice -g team -m 0640 /dev/null "$LAB/evidence/report.txt"
printf 'team report\n' > "$LAB/evidence/report.txt"
chown alice:team "$LAB/evidence/report.txt"
stat -c '%n %A %a %U(%u) %G(%g)' "$LAB/evidence/report.txt"
namei -l "$LAB/evidence/report.txt"
as_user bob cat "$LAB/evidence/report.txt"
as_user carol cat "$LAB/evidence/report.txt"
 
printf '\n== 目录 r、w、x 矩阵 ==\n'
for spec in rx:0550 x:0110 r:0440 wx:0330; do
  name=${spec%%:*}
  mode=${spec##*:}
  mkdir "$LAB/dir-$name"
  printf '%s\n' "$name" > "$LAB/dir-$name/known.txt"
  chown -R alice:team "$LAB/dir-$name"
  chmod 0644 "$LAB/dir-$name/known.txt"
  chmod "$mode" "$LAB/dir-$name"
done
as_user bob sh -c "ls '$LAB/dir-rx'; cat '$LAB/dir-rx/known.txt'"
as_user bob ls "$LAB/dir-x"
as_user bob cat "$LAB/dir-x/known.txt"
as_user bob sh -c "ls '$LAB/dir-r'; cat '$LAB/dir-r/known.txt'"
as_user bob touch "$LAB/dir-wx/new.txt"
as_user bob rm "$LAB/dir-wx/known.txt"
 
printf '\n== 父目录删除与 sticky ==\n'
mkdir "$LAB/open" "$LAB/sticky"
chmod 0777 "$LAB/open"
chmod 1777 "$LAB/sticky"
runuser -u alice -- sh -c "printf a > '$LAB/open/alice.txt'; chmod 0400 '$LAB/open/alice.txt'; printf a > '$LAB/sticky/alice.txt'"
as_user bob rm "$LAB/open/alice.txt"
as_user bob rm "$LAB/sticky/alice.txt"
as_user alice rm "$LAB/sticky/alice.txt"
 
printf '\n== umask 与 setgid 继承 ==\n'
(
  umask 0027
  : > "$LAB/umask-file"
  mkdir "$LAB/umask-dir"
  stat -c '%n %a %A' "$LAB/umask-file" "$LAB/umask-dir"
)
install -d -o alice -g team -m 2770 "$LAB/shared"
runuser -u bob -- sh -c "umask 0002; touch '$LAB/shared/from-bob'; mkdir '$LAB/shared/subdir'"
stat -c '%n %U %G %a %A' "$LAB/shared" "$LAB/shared/from-bob" "$LAB/shared/subdir"
 
printf '\n== ACL mask 与 default ACL ==\n'
printf 'base\n' > "$LAB/acl-file"
chown alice:team "$LAB/acl-file"
chmod 0640 "$LAB/acl-file"
setfacl -m u:bob:rw,m:r "$LAB/acl-file"
getfacl -p "$LAB/acl-file"
as_user bob sh -c "printf denied >> '$LAB/acl-file'"
setfacl -m m:rw "$LAB/acl-file"
as_user bob sh -c "printf allowed >> '$LAB/acl-file'"
 
install -d -o alice -g team -m 2770 "$LAB/acl-default"
setfacl -m d:u:bob:rwX,d:m:rwx "$LAB/acl-default"
runuser -u alice -- sh -c "umask 0027; : > '$LAB/acl-default/new-file'; mkdir '$LAB/acl-default/new-dir'"
getfacl -p "$LAB/acl-default/new-file"
getfacl -p "$LAB/acl-default/new-dir"
 
printf '\n== 清理 ==\n'
rm -rf "$LAB"
test ! -e "$LAB"
echo 'LAB_STATUS=PASS'
LAB

先看身份与普通文件。预期 bob 读取成功并返回 0,因为附加组 team 命中 group 的 r--;carol 返回非零并显示 Permission denied,因为她不是 owner,也不属于 team,只能使用 other 的 ---。namei -l 同时证明路径上的目录都有 bob 可用的 x。

再看目录矩阵。dir-x 的 ls 失败,但读取已知 known.txt 成功;dir-r 能输出名字,随后 cat 因缺少搜索位失败;dir-wx 中创建和删除已知名字成功。这里的失败是实验目标,函数打印退出码后继续运行。

对比 0777 与 1777 父目录。bob 能删掉 open 中 alice 的 0400 文件,证明删除不看目标写位;在 sticky 中删除 alice 文件返回 Operation not permitted,alice 自己删除成功。

最后核对创建规则。umask 0027 应得到文件 640、目录 750;setgid 目录里 bob 创建的文件 group 为 team,新子目录还显示 2775。ACL 第一次给 bob 的条目是 rw、mask 是 r,写入失败;mask 改成 rw 后写入成功。default ACL 只出现在之后创建的对象。脚本删除 /tmp/welearn-ch09,输出 LAB_STATUS=PASS,容器再由 --rm 移除。

若环境无法联网拉取 acl 包,脚本会在安装阶段停止,不应把后续缺少 setfacl 误判为文件系统不支持 ACL。若已有同名容器,先确认没有正在进行的练习,再删除那个已停止的练习容器;不要去掉 --rm 后把临时账号环境长期留着。

27
实操中为什么要让 bob 的主组不是 team、附加组才包含 team?

一套可复用的权限排障顺序

不要从“应该可以”开始,按证据推进:

  1. 复现准确操作并保存退出码:读、写、创建、删除和执行的权限需求不同。尽量直接尝试真实操作,不要先用 access() 风格的预检查再使用,因为两步间对象可能被替换。
  2. 确认进程凭据:运行 id,必要时看目标进程的 /proc/PID/status 中 Uid、Gid、Groups、CapEff。
  3. 逐级检查路径:namei -l path 找到第一个缺少目录 x 的组件,同时注意符号链接目标。
  4. 检查对象属性:stat 获取类型、数值 mode、UID/GID;getfacl 检查命名条目、mask 与 default ACL。
  5. 把操作映射到正确对象:写文件内容看文件 w,删除/重命名看父目录 w+x,sticky 再检查 owner。
  6. 继续看额外策略:findmnt、getcap、MAC 日志、文件属性和应用沙盒。
  7. 做最小修复并重新复现:只添加业务确实需要的权限,保留修改前后 stat/getfacl 证据。

常见误判可以压缩成一张对照表:

现象容易做错更准确的下一步
文件 644 仍读不了反复 chmod 文件namei -l 查父路径 x
文件 444 被别人删了给文件再减写位收紧父目录 w+x,共享目录用 sticky
ACL 写着 bob:rw 仍写不了重复添加同一条目看 mask 与 effective
setgid 目录中新文件不能协作写取消 setgid检查新文件 group 写位、umask/default ACL
root 操作仍被拒绝假定系统损坏查 capability、挂载、MAC、命名空间
sudo 规则看着很窄只看命令路径审查参数、Shell escape 与文件可写性
28
文件可读但打开失败时,哪些命令组合能形成较完整的初步证据?

把权限配置变成可审查的工程事实

权限的目标不是“越小越安全”这句口号,而是让每个进程刚好能完成被授权的动作,同时让配置可复现、可解释:

  • 私密配置常从 0600 或 0640 起,目录常从 0700、0750 或 0755 中按共享边界选择。
  • 团队写目录优先考虑明确 group、setgid、合适的 umask/default ACL,而不是 0777。
  • 部署用 install -m -o -g 或配置管理同时声明 owner、group、mode。
  • sudo 规则写到具体目标身份、绝对命令与必要参数,并检查程序是否能打开 Shell 或修改任意文件。
  • 定期用 find、stat、getfacl 审查世界可写、意外 setuid/setgid 与超出预期的 ACL,不要在不了解目录边界时直接递归修正。

继续查证时,优先使用这些规范和官方说明:

  • GNU Coreutils:文件权限、chmod、chown、stat 与 id
  • POSIX:文件访问权限、目录保护与路径规则
  • Linux credentials(7):进程 UID、GID 与附加组
  • Linux path_resolution(7):逐级路径搜索
  • Linux acl(5):ACL mask、访问算法与 default ACL
  • Linux capabilities(7):拆分后的特权单元
  • util-linux su(1):登录环境与身份切换
  • sudo(8) 与 sudoers(5):策略、认证、环境和日志

最后给自己留一条可执行的验收标准:能用 id 说清主体,用 namei 说清路径,用 stat/getfacl 说清对象,用一次实际操作和退出码证明结果,再说明为什么只改了那几位。做到这一步,权限不再是一串需要背诵的数字,而是一套可以复查的授权决策。

29
权限修复完成的充分证据是:报错消失,并且所有相关目录都已改成 777。
  • 把一次访问拆成五个问题
  • 先确定是谁在访问
    • 真实、有效与文件系统身份
    • 主组与附加组共同参与匹配
  • 用 ls、stat、namei 和 id 组成证据链
    • ls 与 stat 看对象本身
    • namei 找出路径上第一扇关着的门
  • 普通文件与目录的 rwx 不是一回事
    • 普通文件:内容与执行入口
    • 目录:名字列表、目录项与路径搜索
  • chmod:精确表达需要改变哪一位
    • 八进制模式适合表达完整目标状态
    • 符号模式适合表达增量意图
  • umask 是创建时的清除掩码
  • chown、chgrp 与部署时一次设对属性
  • 三个特殊位解决三类不同问题
    • setuid 与 setgid 可执行文件
    • setgid 目录让团队组稳定继承
    • sticky 让共享目录可写但不互删
  • ACL 把三组权限扩展到命名用户和组
    • access ACL 与 mask 的交集
    • default ACL 是新子项的继承模板
  • mode 不是权限系统的全部
  • su 与 sudo:切换身份不等于无限授权
    • su 与 su - 的差别首先在环境
    • sudo 把策略、认证与审计放在命令边界
  • 在一次性沙盒中验证整条权限链
  • 一套可复用的权限排障顺序
  • 把权限配置变成可审查的工程事实

目录

  • 把一次访问拆成五个问题
  • 先确定是谁在访问
    • 真实、有效与文件系统身份
    • 主组与附加组共同参与匹配
  • 用 ls、stat、namei 和 id 组成证据链
    • ls 与 stat 看对象本身
    • namei 找出路径上第一扇关着的门
  • 普通文件与目录的 rwx 不是一回事
    • 普通文件:内容与执行入口
    • 目录:名字列表、目录项与路径搜索
  • chmod:精确表达需要改变哪一位
    • 八进制模式适合表达完整目标状态
    • 符号模式适合表达增量意图
  • umask 是创建时的清除掩码
  • chown、chgrp 与部署时一次设对属性
  • 三个特殊位解决三类不同问题
    • setuid 与 setgid 可执行文件
    • setgid 目录让团队组稳定继承
    • sticky 让共享目录可写但不互删
  • ACL 把三组权限扩展到命名用户和组
    • access ACL 与 mask 的交集
    • default ACL 是新子项的继承模板
  • mode 不是权限系统的全部
  • su 与 sudo:切换身份不等于无限授权
    • su 与 su - 的差别首先在环境
    • sudo 把策略、认证与审计放在命令边界
  • 在一次性沙盒中验证整条权限链
  • 一套可复用的权限排障顺序
  • 把权限配置变成可审查的工程事实