1. 先搞清几个ID的关系
1.1 从一次"我能改密码,却读不了密码文件"的困惑说起
刚接触Linux那会儿,我碰到过一个特别拧巴的现象:用普通账号执行passwd改自己的登录密码,一路顺畅,系统把新密码写进了/etc/shadow;可同一时刻,我cat /etc/shadow却被一句Permission denied挡在门外。当时第一反应是"系统是不是搞错了"——既然我的账号能写这个文件,凭什么连读都不让读?
后来才明白,这背后是 UID、GID、EUID、EGID 这几个身份标识在分工,再加上 SUID、SGID 这两位"临时授权开关"在背后做手脚。这件事几乎把Linux权限体系里最容易混淆的一整套概念串了起来:真实身份是谁、当前生效身份是谁、程序执行时借用了谁的身份、目录里新建的文件归谁所有。
这套东西听着抽象,实际每天都在影响你:为什么同一台机器上两个同事往同一个目录里丢文件,过两天谁的都改不动;为什么下载来的一个二进制程序一执行就是"Permission denied";为什么自己写的脚本加了chmod u+s却一点效果都没有。把这些ID搞明白,这些问题基本都能在三分钟内定位。
这篇内容适合三类人:刚学Linux命令、被权限报错反复折磨的新手;需要部署服务、给程序配权限的运维和开发;以及想系统补一遍权限模型底层的进阶读者。我会从概念一点点推到实操验证,给出可直接复现的命令和程序,顺手把踩过的坑都摊开讲。
1.2 四个ID的角色分工:真实身份与临时身份
先把最容易被混为一谈的四位请出来。UID(User ID)是用户的数字身份,GID(Group ID)是用户所属主组的数字身份,这两个是"你是谁"的档案记录,存在/etc/passwd和/etc/group里,id命令一敲就能看到。EUID(Effective UID)和 EGID(Effective GID)则是"你此刻说话算不算数"的凭据——内核在判断一个进程能不能读某个文件时,看的是有效身份,不是真实身份。
打个生活化的比方:UID 是你的身份证,EUID 是你当天戴的工牌。平时两者是同一张脸,你去食堂打饭刷工牌就行;但某些特殊岗位上,公司会临时发你一张"代理工牌",让你在这一个任务里拥有那个岗位的权限,任务结束工牌收回,你还是你。SUID 干的就是发代理工牌这件事。
为什么非得搞两套身份?因为"记录我是谁"和"判断我能干什么"是两回事。真实身份需要稳定、可审计、不能被程序随意篡改;有效身份需要灵活,允许在受控范围内临时切换。把两者分开,才能既保证追溯性(日志里记的永远是真实UID),又保证功能可实现(临时提权完成特定动作)。
还有两个常被忽略的成员:保存的设置用户ID(saved set-user-ID)和文件系统用户ID(fsuid)。前者是给seteuid预留的"后悔药",允许进程在提权和降权之间来回横跳;后者主要在网络文件系统场景下参与访问判定,日常排查碰到的概率不高,但读/proc/self/status时会看到它,别被吓到。
1.3 一条命令看清进程身上的全部身份
不用猜,内核把每个进程的身份都摊在/proc里了。执行下面这条命令,Uid那一行会有四个数字,从左到右分别是真实UID、有效UID、保存UID、文件系统UID,Gid行同理:
grep -E '^(Uid|Gid):' /proc/self/status普通用户下典型输出是这样:
Uid: 1000 1000 1000 1000 Gid: 1000 1000 1000 1000四个数字完全一样,说明当前 shell 没有任何提权动作。等你执行一个带 SUID 位的程序,那个进程的四个数字就会开始分家。这个观察方法比看任何文档都直观,我在排查权限问题时基本第一步就是抓这四列数字。
再多说一句id命令的用法,它能一次性把身份信息铺开:
id # 当前有效身份 id -r # 只看真实身份 id -u; id -g; id -G # 分别取UID、主GID、全部附加组GIDid -G那串数字很多人不当回事,其实它直接决定了你能否访问属组为某个附加组的文件。用户除了主组,还能被塞进十几个附加组,这些组的GID全都参与权限判定,后面讲权限比对顺序时会再提。
2. 从进程视角看权限判定
2.1 内核到底比对了什么
理解权限判定,最省事的办法是把自己当成内核,手里拿着两样东西:进程的有效身份(EUID + EGID + 附加组列表),以及目标文件的元数据(所有者UID、属组GID、权限位rwxrwxrwx)。剩下的事就是三次比对。
第一次比所有者:进程的 EUID 是否等于文件的 UID?相等就用文件权限位的第1组(属主三位)来决定放行还是拒绝,而且不管结果如何,比对到此结束,不会再去试组权限。这一点非常关键,很多人以为"属主不行再试试属组",实际不是,属主匹配是排他的。
第二次比属组:EUID 对不上,就看进程的 EGID 和附加组列表里,有没有任何一个等于文件的 GID。匹配上就用第2组(属组三位)判定,同样排他,不再往下走。
第三次兜底:前面都不匹配,才用第3组(其他人三位)。如果这组权限也不够,内核返回EACCES,也就是你看到的那句Permission denied。
这个顺序解释了为什么会出现"文件所有者权限是---,但属组权限是rwx,所有者本人反而读不了"的怪现象。不是bug,是设计如此。我在给共享目录配权限时就栽过这个跟头:为了收紧权限把属主位设成---,结果文件创建者自己进不去。
2.2 root 为什么也受这套规则约束
有个流传很广的说法叫"root 无敌",严格讲不准确。root 的 EUID 是 0,内核在权限检查里对 0 做了特殊放行:只要 EUID 为 0,读、写、进入目录基本都允许,但仍然有几处例外。
第一个例外是执行权限。一个文件如果没有对任何人开放x位,root 也执行不了它,会得到Permission denied。想执行必须至少有一个x位存在。第二个例外是写一个设置了不可变属性的文件,或者写只读挂载的文件系统,root 也拦不住内核的拒绝。第三个例外是,root 想读一个属主不是自己、且所有r位都被去掉的普通文件,在部分内核配置下依然能读,但这不是可以依赖的行为,别拿它当权限设计依据。
真正的结论是:root 的强权来自 EUID 为 0 这个特殊判断,而不是"跳过所有检查"。理解这一点之后,你再看 SUID 程序的行为会顺很多——SUID 的本质就是把进程的 EUID 改成文件所有者的UID,如果文件所有者恰好是 root,那么这个进程就获得了和 root 同等的判定待遇。
2.3 真实身份为什么必须保留
既然内核只看 EUID,那真实UID留着干嘛?主要有三个用途,每一个都很实在。
其一是审计。系统日志、进程记账、ps输出里记录的用户身份,都以真实UID为准。一个普通用户执行了带 SUID 的程序,进程里 EUID 变成了 0,但真实UID还是 1000,日志里仍然能追溯到这个操作是 1000 号用户发起的。要是只有一套身份,提权操作就彻底没法追责了。
其二是回退。带 SUID 的程序执行完敏感动作之后,必须立刻把 EUID 降回真实UID,否则后续代码一旦出漏洞,攻击者就白捡一个高权限进程。这就是"最小权限原则"在进程内部的具体落地,也是sudo、su、passwd这类程序源码里反复出现seteuid(getuid())的原因。
其三是身份校验。有些程序会在关键操作前核对getuid()和geteuid()是否一致,不一致就说明自己正跑在提权状态,可以据此拒绝某些危险请求。这种"自我检查"写法在老牌的邮件、打印类程序里很常见。
3. SUID:临时借用程序主人的身份
3.1 passwd 为什么必须带 SUID
回到开头那个困惑。/etc/shadow的权限一般是---------- 1 root shadow,普通用户连读都读不了。可passwd命令由普通用户执行,却能把新密码哈希写进去,唯一合理的解释是:passwd这个二进制文件身上带着 SUID 位,属主是 root。
执行ls -l $(which passwd)会看到类似这样的输出:
-rwsr-xr-x. 1 root root 32688 ... /usr/bin/passwd注意属主那三位里的s,它顶替了原本x的位置,表示"这个文件可执行,且执行时把进程的 EUID 设为文件所有者"。进程起来之后 EUID 变成 0,瞬间具备写/etc/shadow的能力;写完密码,程序内部把 EUID 降回调用者的真实UID,继续以普通身份跑完剩下的流程。
这套设计的关键在于:提权窗口被压缩到极小的一段代码里,而且这段代码是精心写过的,只做"校验旧密码 + 写新哈希"这一件事,不接受外部传入的任意文件路径。假如passwd允许用户指定要写的文件名,那 SUID 就成了一个巨大的提权后门。
用数字方式设置 SUID 位,是在常规三位权限前面加一位4:
chmod 4755 ./mytool # 等价于 chmod u+s ./mytool ls -l ./mytool # 输出 -rwsr-xr-x如果文件本身没有执行权限,chmod u+s之后的显示会是大写的S:
-rwSr--r-- 1 user user ... mytool大写S表示"SUID位设了,但没有执行权限",这种情况下 SUID 完全不生效,因为不执行就不会有进程,没有进程就无从谈起进程身份。我见过不少人排查半天,最后发现是忘了chmod +x。
3.2 SUID 生效的三个硬性条件
SUID 不是设了就一定管用,至少三个条件得同时满足,缺一个都会静默失效,而且不会有任何报错,特别容易误判。
第一个条件,文件系统不能以nosuid挂载。很多发行版出于安全考虑,会对/tmp、/home、/run/user等目录所在的分区加上nosuid选项。挂载信息看findmnt或mount输出里的选项列,看到nosuid就说明这个分区上的 SUID 位形同虚设。我最初在家目录里做实验,chmod u+s后程序照样以普通身份运行,折腾了半小时才发现是/home的挂载选项在作怪。把实验挪到/opt或/usr/local下就正常了。
第二个条件,文件的所有者必须是目标身份。SUID 借的是"文件所有者"的身份,如果所有者是你自己,那 EUID 从 1000 变成 1000,什么也没发生。只有所有者是 root(或其他需要借用的账号)且你有权限执行时,才有意义。当然,普通用户也没法随便把文件所有者改成 root,这本身又是一层保护。
第三个条件,执行链路得是真正的execve调用。内核只在execve加载可执行文件的那一刻处理 SUID 位。任何绕过execve的方式,比如让解释器读取脚本内容再逐行执行,都不会触发 SUID 逻辑。这一点是下一小节的重点。
提示:
nosuid是排查 SUID 失效的第一怀疑对象,其次是文件是否真带执行位。第三顺位才去怀疑程序自身逻辑。
3.3 为什么给脚本加 SUID 没用
这是被问得最多的问题之一:写了个 bash 脚本,chmod u+s,执行后发现id -u还是自己的UID。答案很干脆——Linux 内核故意不支持脚本上的 SUID。
原因在于脚本的执行方式。你敲./script.sh时,内核看到文件头是#!/bin/bash,于是它去加载/bin/bash这个二进制,把脚本路径作为参数传给它。真正被execve加载的可执行文件是bash而不是脚本,内核处理 SUID 时看的是bash的权限位——而bash当然没有 SUID 位。脚本本身的s位在这个过程中完全被忽略。
有人会想到直接bash script.sh或者source script.sh,这两种方式连execve都可能不走,更不可能触发 SUID。还有人想到把脚本编译成二进制,这倒是可行,但把 shell 逻辑塞进C程序里通常得不偿失。
真需要脚本以别的身份跑,正路只有两条:一是用sudo配置精细到命令和参数的授权规则,让特定用户能免密执行某个脚本,这是最常见也最可控的做法;二是把脚本核心逻辑改写成C程序,自己处理好seteuid的升降,明确在什么时机提权、什么时机降回。第二种做法成本高,只在有强需求时考虑。
顺带说个相关坑:有些系统上你想让脚本带上 SGID 来继承属组,同样无效,原因一模一样。目录上的 SGID 是另一码事,那个是真有效,后面单独讲。
3.4 SUID 的安全隐患与排查思路
SUID 本质上是把一个"以高权限运行"的入口交给了普通用户,风险等级自然高。历史上大量本地提权问题都出在这类程序上。常见的风险模式有三类,值得每个管机器的人心里有数。
第一类是参数注入。程序接受用户输入并拼接成命令或路径,一旦没做严格校验,就能被诱导去执行不该执行的东西。第二类是环境变量污染。老程序如果没清理LD_PRELOAD、PATH这类变量,攻击者可以借机加载自己的库。现代发行版对 SUID 程序做了环境变量清洗,但老程序不一定享受到。第三类是功能过宽。一个 SUID 程序如果顺带提供了"读取任意文件"的调试选项,那它基本等于把 root 读权限公开了。
排查思路是从"清单"开始,而不是从"怀疑"开始。先把系统上所有带 SUID 和 SGID 的文件列出来,心里有数之后再逐个评估:
find / -xdev \( -perm -4000 -o -perm -2000 \) -type f -printf '%m %u %g %p\n' 2>/dev/null | sort-xdev的作用是不跨文件系统,避免扫到网络挂载和移动设备上耗时太久。加上2>/dev/null屏蔽掉那些没权限进入的目录报出的错误,输出会清爽很多。把这份清单存下来定期对比,出现新增文件就该警觉。
-perm -4000里的减号很关键,意思是"至少包含这一位",而不是"精确等于"。如果写成-perm 4000,那就只能匹配权限位恰好是4000的文件,实际一个都找不到。
4. SGID:比 SUID 低调但用处更广
4.1 文件上的 SGID:借用属组身份
文件上的 SGID 和 SUID 是一个套路,只是换成了组:执行时把进程的 EGID 设为文件属组。设置方式是数字前加2:
chmod 2755 ./mytool # 或 chmod g+s ./mytool ls -l ./mytool # 输出 -rwxr-sr-x有个容易混淆的地方:文件带 SGID 但属主位没有x时,显示为大写S;属组位没有执行权限同样显示大写S。判断是否真正生效,先看有没有执行位,再看挂载选项。
实际场景里,文件级 SGID 用得比 SUID 少得多,但也确实有它的位置。比如某个程序需要读取只有特定组才能访问的数据文件,你可以让这个程序属于那个组并带上 SGID,这样任何能执行它的人都能临时获得组的读取能力,而不用把每个人塞进那个组。这也是一种"最小授权",只不过粒度从用户变成了组。
同样的nosuid限制适用,同样的脚本无效规则适用。这三个坑在 SUID 上踩过一次,SGID 上就不会再踩第二次。
4.2 目录上的 SGID:团队共享目录的标准解法
这个用法才是 SGID 真正发光的地方,也是我日常配置里用得最多的一个特性。把它设在目录上,含义完全变了:在这个目录里新建的文件和子目录,属组会自动继承目录的属组,而不是创建者的主组。同时新建的子目录会自动继承 SGID 位。
场景很好理解:三个人协作,都加入了devteam组,共同维护/srv/project目录。如果什么都不做,A 创建的文件属组是 A 的主组,B 创建的是 B 的主组,两个人互相都改不了对方的文件,共享目录形同虚设。给目录加上 SGID 之后,不管谁创建,文件的属组统一是devteam,配合组的读写权限,协作就顺了。
设置命令和验证:
sudo mkdir -p /srv/project sudo chgrp devteam /srv/project sudo chmod 2775 /srv/project ls -ld /srv/project # drwxrwsr-x 2 root devteam 4096 ... /srv/project属组三位出现s,就说明 SGID 生效了。之后任何成员在其中创建文件,ls -l看到的属组都会是devteam。这一步很多人只做了一半——目录权限设了,但umask没管,结果新文件默认权限是644,同组人只有读没有写,一样改不动。
4.3 实操:把多人协作目录配到位
完整流程我按顺序列一遍,每一步都有它的道理,别跳。
第一步,建组加人。组名按项目起,成员用usermod -aG追加:
sudo groupadd devteam sudo usermod -aG devteam alice sudo usermod -aG devteam bob注意-a不能省,省了就会把人从其他附加组里踢出去,只剩这一个组。这个坑我踩过,把某个账号从docker组里挤掉,事后查了半天。
第二步,设目录属组和权限位:
sudo chgrp devteam /srv/project sudo chmod 2775 /srv/project2775拆开看:2是 SGID,775是属主和属组读写执行、其他人读执行。如果目录里还需要别人能进入但不能列目录,可以把最后一位改成0,变成2770。
第三步,处理umask。默认umask 022会让新文件权限是644、新目录是755,组内成员对文件没有写权限。要在该目录下协作,成员需要把umask设成002,这样新文件是664、新目录是775。设置方式可以是个人~/.bashrc里加一行umask 002,也可以在共享 shell 配置里针对组内成员统一处理。
第四步,验证。用两个不同账号各建一个文件,看属组:
touch /srv/project/a.txt ls -l /srv/project/a.txt如果属组显示devteam,说明 SGID 起作用了;如果显示创建者主组,回头检查目录权限位上的s有没有真的出现,以及目录所在分区有没有nosuid挂载。
注意:SGID 目录只影响"新建"的对象,历史文件的属组不会自动修正。迁移旧数据时,得用
chgrp -R手动刷一遍。
4.4 顺带说说粘滞位,别和 SGID 搞混
/tmp目录的权限是drwxrwxrwt,最后那个t是粘滞位(sticky bit),数字表示是1,组合起来就是1777。它的作用是:目录里所有人可写,但只有文件所有者、目录所有者或 root 才能删除或重命名别人的文件。
这个特性常被和 SGID 一起提,因为三个特殊位挤在同一个位置显示。s出现在属主位是 SUID,出现在属组位是 SGID,t出现在其他人位是粘滞位。三者可以叠加,一个目录权限写成3775就是 SGID 加粘滞位。
粘滞位解决的是"共享目录互相删文件"的问题,SGID 解决的是"共享目录属组不统一"的问题,两者经常搭配使用。比如团队共享目录可以设成3775:新建文件属组统一继承,同时谁也不能删别人的东西,只在确有必要时由管理员介入。
5. 完整实操:亲手验证每个ID的变化
5.1 准备一个干净的实验环境
理论说再多不如跑一遍。准备一个不在nosuid分区下的目录,/opt/lab就挺合适:
sudo mkdir -p /opt/lab sudo chown $(id -u):$(id -g) /opt/lab cd /opt/lab findmnt -no OPTIONS -T /opt/lab最后一条命令输出该目录所在挂载点的选项,确认里面没有nosuid。这一步别省,我见过太多"实验失败"其实是环境问题。
顺手准备一个打印身份的C程序,后面所有验证都靠它:
/* idtest.c */ #include <stdio.h> #include <unistd.h> int main(void) { printf("real uid=%d gid=%d\n", getuid(), getgid()); printf("effective uid=%d gid=%d\n", geteuid(), getegid()); FILE *fp = fopen("/proc/self/status", "r"); if (fp) { char line[256]; while (fgets(line, sizeof line, fp)) if (line[0] == 'U' || line[0] == 'G') if (line[1] == 'i' || line[1] == 'd') fputs(line, stdout); fclose(fp); } return 0; }编译并跑一次基线:
gcc -o idtest idtest.c ./idtest输出里四列数字应该完全一致,全是你的UID和GID。
5.2 给程序加上 SUID 观察变化
sudo chown root:root idtest sudo chmod 4755 idtest ls -l idtest ./idtest第二行命令把所有者改成 root,只有 root 才有权限改,所以得用sudo。ls -l应该看到-rwsr-xr-x。执行之后,real uid还是你的号码,effective uid变成 0,/proc/self/status里的四列会呈现1000 0 0 0这样的组合(不同内核版本保存位可能有差异,但前两列的分裂是确定的)。
这个输出就是全部秘密的具象化:真实身份没变,有效身份被临时借走了。程序内部如果调用seteuid(getuid()),effective uid会立刻回到 1000,后面的代码就以普通身份运行。
5.3 验证 SGID 在目录上的继承效果
mkdir -p /opt/lab/shared chmod 2775 /opt/lab/shared touch /opt/lab/shared/test.txt ls -ld /opt/lab/shared ls -l /opt/lab/shared/test.txt先看目录权限位上的s在不在,再看test.txt的属组是不是和目录一致。如果你自己就是目录所有者,属组大概率本来就一致,看不出差异,这时候换一个账号或者先chgrp到别的组再测,效果才明显。
再往下挖一层,验证子目录继承:
mkdir /opt/lab/shared/sub ls -ld /opt/lab/shared/sub子目录的权限位里也应该带s,这是 SGID 目录的递归特性。这个特性在搭建多层级项目目录时特别省事,不用一层层手动设。
5.4 用脚本做一次全盘权限审计
机器上的特殊权限文件,最怕的是"不知道什么时候多出来一个"。定期跑一遍清单,输出到文件里对比,成本很低:
#!/bin/bash # perms-audit.sh OUT=/var/log/perms-$(date +%F).log find / -xdev \( -perm -4000 -o -perm -2000 -o -perm -1000 \) -type f \ -printf '%m %u %g %p\n' 2>/dev/null | sort > "$OUT" diff /var/log/perms-last.log "$OUT" 2>/dev/null cp -f "$OUT" /var/log/perms-last.log三个-perm条件分别覆盖 SUID、SGID、粘滞位。-printf输出权限数字、所有者、属组、路径四列,比ls -l更适合做文本对比。diff那行会直接告诉你和上次相比新增或消失了哪些条目,空输出就代表没变化。
把这个脚本挂进定时任务,每天跑一次,出现异常条目时邮件告警。这套机制的价值不在"发现已知问题",而在"及时发现未知变化"——某个被利用的账号悄悄给一个可执行文件加了 SUID,靠人肉检查基本发现不了,靠对比清单就是一目了然。
6. 常见问题与排查清单
6.1 问题速查表
把日常碰到的现象、原因、处理方式整理成一张表,遇到问题先对号入座,能省掉大半时间:
| 现象 | 最可能的原因 | 处理方向 |
|---|---|---|
| 加了 SUID 但 EUID 没变 | 文件在nosuid分区 | 用findmnt查挂载选项,换目录 |
| 加了 SUID 但 EUID 没变 | 脚本不是二进制 | 改写成C程序,或用精细授权方案 |
显示-rwSr--r--大写S | 缺执行权限 | chmod +x补齐 |
| SUID 程序执行报 Permission denied | 所有x位都被去掉 | 至少开一个执行位 |
| 共享目录新建文件属组不对 | 目录没设 SGID | chmod g+s并核对显示 |
| 属组对了但还是改不了 | umask让组无写权限 | 协作成员统一umask 002 |
| 属主自己反而进不了目录 | 属主位权限被收紧,属组不参与判定 | 恢复属主位的x |
find -perm 4000什么都找不到 | 用成了精确匹配 | 改成-perm -4000 |
这张表里前两行是最高频的,基本占了我在群里被问到的八成。第三、四行属于"配置了一半",也很常见。第五到第七行集中在共享目录场景,是团队协作问题的常客。
6.2 几个只有踩过才知道的细节
说几个文档里不太写、但实际会撞上的点。
第一个,chmod u+s和chmod 4755效果一样,但心理感受不同。用符号方式描述意图更清晰,用数字方式更精确、更适合写进自动化脚本。我现在的习惯是:手工调试用符号,脚本里一律用数字,避免符号写法在不同权限组合下产生意外。
第二个,cp复制一个带 SUID 的文件,目标文件的 SUID 位通常不会被保留。这既安全又反直觉,很多人备份完发现权限没了就是这个原因。要保留特殊位,得显式加参数或者事后再chmod。反过来,tar打包解包是保留这些位的,所以拿别人的备份包解压时得多留个心眼。
第三个,修改 SUID 文件的内容会自动清掉 SUID 位。这是内核的保护机制——既然内容变了,就不能再信任它借用身份的能力。编辑完发现权限没了,不用怀疑人生,重新chmod即可。这也解释了为什么升级系统包之后,某些特殊权限需要包管理器重新设置。
第四个,newgrp能切换当前 shell 的有效GID,但它是开一个子 shell,不是改当前进程。切完之后id -g变了,exit退出又回来了。临时需要以某个组身份操作时挺好用,别指望它永久生效。
第五个,附加组信息的读取是有时效的。往组里加人之后,对方已经登录的会话不会立刻感知,需要重新登录或者用newgrp刷新。给同事配完权限对方说"还是不行",八成是这个原因,让对方重登一次就好。
6.3 我的配置原则
折腾这些年,权限这块我给自己定了三条规矩,简单但管用。
能不用 SUID 就不用。确实需要提升权限执行某个动作时,优先考虑把动作拆出来做成一个服务,用固定的低权限账号运行,再通过受控接口调用;其次是精细化的授权规则,限定到具体命令和参数;实在绕不开才在二进制上加 SUID。每一次"能用 SUID 就用了"的选择,都是在给未来的自己埋一个需要审计的坑。
SGID 目录优先于手动chgrp。只要是多人在同一目录里写东西,第一反应就是把目录的 SGID 设上,而不是事后定期跑脚本去刷属组。前者一劳永逸,后者永远在补漏,而且总有忘记跑的几天。
权限清单常备。系统里有哪些特殊的权限文件,我心里得有个数。这个数不是靠记忆,是靠每天自动生成的那份清单。有了基线,任何新增都是信号;没有基线,所有异常都是背景噪音。
这套东西学起来一开始会绕,但一旦把"真实身份负责追溯、有效身份负责判定、特殊位负责临时借用"这三句话捋顺,后面所有现象都能自己推出来。我在实际配置中最深的体会是:几乎所有的权限事故都不是因为规则太复杂,而是因为少设了一个位、少确认了一次挂载选项,或者少问了对方一句"你重新登录了吗"。