1. 一次权限误配事故我为什么决定把权限彻底讲透早些年我维护过一台内部文档服务器用的就是标准 Linux 发行版。某天早上发现线上一个知识库目录里多了几个来路不明的文件还改动了几个文档的属主。排查结果让我相当尴尬——这个目录以及下属全部子目录早前为了方便某位同事上传资料被递归执行了chmod -R 777。任何人都能写、能改、能删而那时服务器上恰好跑着一个被爆破成功的弱口令账户对方进去以后如入无人之境。这件事给我留下的教训不是“要改密码”而是很多人对 Linux 权限的理解停留在“r 是读、w 是写、x 是执行”这一层真到了需要判断“某个进程为什么能改这个文件”“这个目录为什么删不掉”“普通用户怎么也能修改系统配置文件”的时候就抓瞎了。而权限恰恰是整个 Linux 安全模型的地基——它决定了谁能读你的源码、谁能改你的配置、谁能把你的数据删光。把权限真正吃透不是考证用的知识点是每一个跟 Linux 打交道的人都绕不开的生存技能。这篇文章不打算做成 man 手册的翻译。我会从一次事故讲起把rwx的真实语义、属主与属组的判定顺序、SUID/SGID/Sticky Bit 这类特殊权限、ACL 访问控制列表以及日常排查权限问题的完整链路全部拆开揉碎讲清楚。内容既照顾刚入门的人也会给有经验的运维和开发者一些平时容易忽略的角度。2. rwx 三位权限的真实语义文件与目录是完全不同的两套规则2.1 文件权限读、写、执行各自控制什么先看最常见的场景。ls -l输出的第一列比如-rw-r--r--一共十个字符第一个字符是文件类型后面九个字符分成三组每组三个对应属主owner、属组group、其他人other的权限。每一组里r代表读、w代表写、x代表执行这个大家都熟。但往深一层问文件的“写”权限到底包含哪些操作它不仅仅是“用编辑器打开并保存”。对文件拥有写权限意味着可以修改文件内容、可以截断文件、也可以把文件大小改为零。更关键的是写权限并不包含“删除文件”的权利——删除一个文件决定权在它所在目录的写权限上。这一点很多人第一次听到会觉得反直觉但它是理解 Linux 文件系统权限的基石。再来看执行权限。文件的x位决定这个文件能不能被当作程序运行。注意能不能执行和文件内容是不是可执行的脚本或二进制是两回事一个文本文件哪怕内容完全是乱写只要有x权限你就能尝试执行它反过来一个编译好的可执行程序如果没有x权限你直接运行就会得到Permission denied只能通过sh 文件名这类方式绕过因为那是让解释器去读文件而不是文件本身被执行。这解释了为什么下载的安装包经常要先chmod x才能运行。2.2 目录权限为什么目录的写权限意味着“可以删除任何人的文件”目录的权限位和文件表面看着都是rwx实际含义却完全不同。目录本身不存储文件内容它存的是“文件名到 inode 的映射关系”所以目录的权限实际控制的是你对这一组映射关系的操作能力目录的r权限允许你列出目录里有哪几个条目文件名。但注意只有r而没有x时你虽然能看到名字列表却无法访问这些条目的 inode 信息也无法进入目录。目录的w权限允许你在目录里创建新文件、删除文件、重命名文件。这就是为什么说“目录的写权限是文件生杀大权”——只要你对某个目录有w权限即使目录里某个文件的属主是 root 而且该文件是只读的你依然可以把它删除或者改名。目录的x权限允许你“穿过”目录也就是cd进去或者访问目录内条目的元数据。它决定了你能不能真正使用目录内的文件。于是出现了一个经典结论r让你看见x让你到达w让你操纵。三个权限单独看都很简单组合起来才产生复杂行为。比如一个目录权限是r--你能ls看到里面的文件名但cd不进去也读不了任何文件权限是-wx时你进得去、能创建和删除文件却ls不出内容只能靠猜文件名访问权限是r-x时你能进能读但不能新建和删除任何东西。这组组合是运维排查“能看到目录却打不开文件”“能写文件却列不出文件名”等怪问题时的主要原因。2.3 数字权限与符号权限的换算逻辑刚接触 Linux 时我也觉得chmod 755和chmod urwx,gorx是两种并列的写法后来才意识到本质是同一件事的不同表达。数字权限里r4、w2、x1三个位相加得到 07 的数值例如rwx是 7、r-x是 5、r--是 4。chmod 755就是属主 7、属组 5、其他人 5。这个设计不是随便定的4、2、1 是三个独立的二进制位相加的结果可以唯一反推出权限组合比如 6 只能是rw-3 只能是-wx。符号权限则更直观它按“谁u/g/o/a 动作/–/ 权限位r/w/x”的语法来写。实操里我偏好数字模式因为它简洁、适合一条命令同时修改三组权限符号模式更适合只调整某一个角色的某一位比如chmod go-w file表示去掉属组和其他人的写权限不会动到属主。还有一个容易踩的坑chmod的-R递归参数。很多人图省事直接chmod -R 777 /某个目录这一下去的不只是普通文件还包括目录、脚本、可执行程序甚至把原本合理的setuid位也一并冲掉接下来会讲。我见过不止一次因为递归chmod把系统命令的权限弄乱导致连sudo都用不了的场景。递归修改权限不是不行但你必须清楚自己在改什么改之前最好先用getfacl -R或ls -lR留个底。3. 权限三元组的判定顺序进程如何决定“你能不能动这个文件”3.1 内核的权限判定流程有了rwx之后下一个核心问题是系统怎么决定用哪一组权限来校验你比如一个文件的属主是 root、属组是 staff而你用普通用户 alice 登录alice 同时又是 staff 组的成员那么 alice 操作这个文件时用的是 root 的权限、staff 组的权限还是 other 的权限答案是一个严格的分支判断顺序不可打乱如果进程的有效 UID 等于文件的属主 UID按属主权限位授予权限否则如果进程的有效 GID 或任一附加组 ID 等于文件的属组 GID按属组权限位授予权限否则按 other 权限位授予权限。注意这里用的是“有效 UID”不是“登录用户名”。普通场景下有效 UID 和真实 UID 相同但一旦涉及 SUID 程序后面细讲有效 UID 会变成程序属主的 UID整个判断就会按另一个身份走。真正理解这套流程你才能解释为什么某个用户明明在文件所属的组里却提示Permission denied——很可能是他在第一步被排除了因为他是文件属主但属主位没有对应权限而判断不会继续“降级”到组权限。另一个容易忽略的点root 用户基本不受rwx限制。内核在权限校验时对 UID 0 有特殊处理绝大多数操作直接放行。但这不代表 root 无所不能——挂载为只读的文件系统、chattr i设置的文件、SELinux/AppArmor 这类强制访问控制模块仍然可以拦住 root。所以“我是 root 怎么还改不了”这类问题的排查思路要在rwx之外找原因。3.2 属主、属组、其他边界与默认归属文件创建出来时属主默认为创建者的 UID属组默认为创建者的有效 GID或者说创建者所属的主组。这三段身份划分构成了传统 UNIX 权限模型的基本盘。但三元组模型有个天然的不足它只允许你为一类人属主、一组人属组、所有人分别设定权限。如果现实需求是“A 用户可读写、B 组只读、C 用户完全禁止”传统权限就做不到了这就给后面的 ACL 留了位置。顺带说一个常见操作顺序错误。新手经常先chmod后chown或者反过来。其实两者互不影响真正需要注意的是chown是只有 root 才能执行的操作普通用户不能把文件属主改成别人否则可以直接把文件“送”给 root 然后利用 root 的属主权限做坏事。另外chown会“顺带”把setuid和setgid位清除掉这是内核对安全性的保护防止你通过改变属主来保留一个危险的 SUID 权限。所以如果chown之后发现特殊权限位不见了别惊讶这是设计如此。3.3 chown、chgrp、chmod 的配合用法日常使用中chown和chgrp经常被合并成一条命令。比如要把目录data及其内部所有文件的属主设为alice、属组设为team可以执行chown -R alice:team data。这里冒号后面跟组名比单独先chown再chgrp少一次操作也避免了中间状态。需要提醒的是chown -R和chmod -R一样是“大杀器”尤其是对系统目录操作时尽量用--from参数限定范围例如chown -R --fromroot alice:team /srv/data只修改当前属主为 root 的文件避免误伤已经设好属主的其他文件。chmod的符号模式在配合chown时很好用。比如把目录属主改成某个服务账户后立刻chmod gs开启 setgid让新文件自动继承组属主。这种“改属主 设特殊位”的组合在共享协作目录里是常规操作。3.4 umask 与文件创建时的默认权限每次创建文件和目录系统都会用一个初始权限模板减去 umask 决定最终权限。文件默认从666开始没有x因为普通文件默认不可执行目录默认从777开始目录必须有x才能进入然后去掉 umask 中置位的权限。所以 umask 为022时新建文件是644666 - 022新建目录是755777 - 022。这是为什么大多数系统里新文件天然是“属主可读写、其他人只读”。很多安全加固建议把 umask 改成027甚至077就是为了让组内其他人和其他用户对新文件没有写权限。但要注意umask 只是“默认值”它不影响你之后显式chmod。另外在脚本里创建文件时如果依赖默认权限最好在脚本开头显式设置umask 077避免脚本以 root 身份运行时创建出全局可读的敏感文件。这个小习惯曾经帮我避免过几次配置文件泄露的尴尬。4. 三类特殊权限位SUID、SGID、Sticky Bit 的底层行为4.1 SUID为什么 passwd 命令能修改 /etc/shadow普通用户想改自己的密码而密码 hash 存储在/etc/shadow这个文件通常只有 root 能读写。普通用户是怎么改成功的答案就是 SUID。passwd这个可执行文件设置了 SUID 位当普通用户执行它时进程的有效 UID 会被临时提升为文件属主 root于是这个进程就有权限去写/etc/shadow但进程的真实 UID 仍然是普通用户所以它不能去随便改其他文件——这种“只在程序运行期间生效的临时身份切换”就是 SUID 的精髓。查看一个文件是否设置了 SUID可以用ls -l属主权限位里的x会变成s小写表示同时有执行权限大写S表示没有执行权限却设了 SUID后者通常属于配置错误。数值表示法里SUID 是最高位 4比如chmod 4755。SUID 是一把双刃剑它赋予普通用户执行特定任务的能力但也创造了一条提权路径。一个以 root 属主运行的 SUID 程序一旦存在漏洞缓冲区溢出、命令注入、可被操纵的搜索路径攻击者就可能借助这个程序拿到 root 权限。所以安全基线里通常会要求排查系统里所有的 SUID 文件确认每一个都是必要的。我自己在做服务器基线检查时会跑find / -perm -4000 -type f 2/dev/null把可疑项全部列出来逐个核对。4.2 SGID目录继承组属主的机制SGID 有两种表现。在文件上它和 SUID 类似让进程以文件属组的身份运行在目录上它的行为更有实操价值一旦目录设置了 SGID在这个目录里新建的文件和子目录会自动继承目录的属组而不是创建者的主组。这一点在多人协作目录里特别有用。比如团队共享目录/srv/share属组设为team再执行chmod gs之后无论哪个团队成员在里面创建文件文件的属组都会自动变成team其他团队成员就可以按组权限访问。如果没有 SGIDA 创建的文件属组是 A 的主组B 创建的是 B 的主组共享目录就会变得一团糟只能靠管理员反复chgrp修正。SGID 在目录上的另一个附加效果如果目录还设置了组写权限新建的子目录会自动继承 SGID 位形成“传递”所以协作目录只要在顶层设置一次往下递归创建的目录都会保持继承行为。这比一遍遍手动chgrp可靠得多。4.3 Sticky Bit/tmp 目录的防删除防线Sticky Bit粘贴位在目录上的作用是即使目录对所有人可写也只有文件属主、目录属主或 root 才能删除或重命名目录内的文件。最典型的例子是/tmp权限是1777所有人能创建文件但你不能删除别人的文件。普通用户在自己家目录下创建的文件靠的就是所属家目录的属主权限来保护而共享可写目录必须靠 Sticky Bit。查看 Sticky Bit 也是看ls -lother 权限位的x变成t大写T表示没有执行权限。设置命令是chmod t 目录数值上是 1比如chmod 1777 /tmp。在很多需要临时文件交换的目录比如某些应用的上传目录、CI 工作目录里设置 Sticky Bit 是“既要可写又要防止互删”的标配做法。但要注意Sticky Bit 只保护“删除/重命名”操作不保护文件内容。如果文件本身是 666 权限别人仍然可以直接打开文件修改内容如果目录是 777别人可以在你旁边新建同名文件造成覆盖。所以共享目录的最终安全性永远要靠文件权限 目录权限 Sticky Bit 三者一起设计而不是指望某一个单独生效。5. ACL 访问控制列表当传统权限的三元组不够用的时候5.1 什么场景强迫你只能用 ACL传统rwx的三元组模型有一个明显的天花板一个文件只能记录一个属主、一个属组、一个“其他所有”。而现实需求往往是“项目组 A 对目录可读写、项目组 B 只读、外部人员完全不可见”或者“给某个临时外包用户单独开放某个目录的访问权”。这种时候你不可能靠改属主或改属组来实现——改了属主就影响了 A 组改了属组就影响了 B 组。ACLAccess Control List就是来补这个短板的。它允许你在传统rwx之上针对指定用户和指定组单独设置权限。比如给用户zhang赋予/srv/project/data的读写执行权给组audit只读权这些都不改变文件原有的属主与属组三元组完全靠 ACL 附加条目实现。Linux 主流文件系统ext4、XFS、Btrfs 等都原生支持 ACL但需要确认文件系统挂载时是否开启了acl选项多数发行版默认开启。第一次用之前建议mount | grep acl看一眼否则setfacl可能报“操作不支持”的错误。5.2 setfacl 与 getfacl 的核心操作查看 ACL 用getfacl 文件输出会比ls -l多出一大块。设置 ACL 的核心命令是setfacl最常用的几个套路我列一下给指定用户设置权限setfacl -m u:zhang:rwx /srv/project/data给指定组设置权限setfacl -m g:audit:r-x /srv/project/data删除指定用户的 ACL 条目setfacl -x u:zhang /srv/project/data清除文件所有扩展 ACLsetfacl -b /srv/project/data如果需要递归设置整个目录树加-R。我自己更推荐用-R之前先getfacl -R备份当前 ACL 到文件改坏了可以直接恢复这个习惯在批量调整线上目录时能救命。设置了 ACL 之后ls -l的权限位末尾会多出一个号比如drwxrwx--- 2 root root 4096 ...。这个是肉眼识别“这个文件有额外 ACL 条目”的最快方式排查权限问题时一定要养成看它一眼的习惯。5.3 mask 参数容易被忽略的权限上限ACL 里最容易让新手懵的是mask。getfacl输出中有一行mask::r-x它表示“所有命名用户、命名组和属组的最大有效权限”。听起来抽象实际行为是即使setfacl -m u:zhang:rwx给 zhang 设置了全部权限如果 mask 是r-x那么 zhang 实际拿到手的就是r-x写权限被 mask 截掉了。mask 的设计目的是提供一个统一的“权限上限”方便管理员快速收紧整个目录树的有效权限。但它的副作用是很多人设置了 ACL 之后发现权限不生效排查半天才发现是 mask 挡路。如果希望某个用户的完整权限生效需要确保 mask 包含这些权限位例如setfacl -m m::rwx 文件或setfacl -m u:zhang:rwx,m::rwx 文件一次性把用户权限和 mask 都设好。还有一个联动点当通过普通chmod修改文件权限时内核会自动调整 mask。比如一个带 ACL 的文件你执行chmod 640mask 会被同步改成rw-从而可能影响已设置的命名用户权限。所以对带 ACL 的文件要么用chmod时心里有数要么始终用setfacl来改权限避免两者互相“打架”。5.4 默认 ACL让新文件自动带上指定权限ACL 还能设置“默认 ACL”default ACL效果类似目录的 SGID 继承——目录设置了默认 ACL 之后在目录内新建的文件和子目录会自动继承指定的 ACL 条目。比如setfacl -d -m u:zhang:rwx /srv/project/data以后在/srv/project/data里创建的任何新文件都会自动给 zhang 分配读写执行权。这个特性在多个团队共用一个项目目录、又不希望每次都手动对新文件补 ACL 的场景下非常实用。使用默认 ACL 时要特别注意它与 umask 的关系。默认 ACL 会取代 umask 对新建文件权限的部分限制实际创建出的文件权限由默认 ACL 决定。这很容易造成“明明设置了 umask 077共享目录里出来的新文件还是别人可读”的现象。遇到这类“权限比预期宽松”的问题先查目录的默认 ACL而不是纠结 umask 为什么没生效。6. 权限问题排查与安全加固的实战链路6.1 排查 Permission denied 的完整思路遇到Permission denied不要只盯着文件本身要按层次排查。我通常按这个顺序来ls -l看文件权限位确认属主、属组、其他三组权限是否覆盖了当前操作身份。id 用户名确认执行操作的用户属于哪些组是否存在“用户在文件属组里但组权限不够”的情况。往上检查每一级父目录的权限。文件能访问的前提是路径上每个目录都有x权限这是最容易被忽略的——文件本身是 777但它的父目录没有x照样进不去。getfacl 文件检查 ACL 条目注意号尤其注意 mask 是否截断了权限。检查文件是否被chattr i锁定为不可修改lsattr 文件可以看到。如果以上都正常但依然被拒考虑系统层面的强制访问控制SELinux/AppArmor用ausearch或dmesg翻一下审计日志往往能看到avc: denied之类的记录。这套链路看起来长实际操作熟练后一分钟内能走完。我见过很多同事卡在第 3 步父目录没有执行权限和第 4 步mask 截断这两类问题靠肉眼ls -l根本看不出来必须亲自走到getfacl和namei -l 路径才会有答案。6.2 最小权限原则的落地方法粗放式授权是权限事故的头号来源。怎么把最小权限原则落到日常运维里我总结了几条硬性做法永远不要对业务目录裸跑chmod -R 777。如果真的需要让多用户写入优先考虑“目录设 SGID 组权限 Sticky Bit”的组合而不是把 other 位放开。文件默认 644、目录默认 755 是好习惯默认不开放组写和其他写。出现“某用户需要读写、某组需要只读”的需求时直接用 ACL不要通过把所有人加进同一个组来妥协。定期扫描异常开放权限find /data -type f -perm -002找出其他用户可写的文件find / -xdev -type f -perm -4000列出 SUID 文件。对外提供上传功能的目录务必开启 Sticky Bit并且把目录的属主设置为运行服务的专用账户。这些方法都不高级但任何一个都能挡住绝大多数“低级失误导致的数据泄露”。安全不是靠一个高深的技术而是靠无数个“本可以不做但不做就会出事”的小习惯。6.3 chattr 文件属性在 rwx 之上多一道锁chattr控制的文件属性跟 ACL 和rwx正交它由文件系统层强制实施。最常用的两个是i不可变连 root 也不能改和a只能追加适合日志文件。查看用lsattr。在关键配置文件上设置chattr i是很有用的加固手段——即使某进程被入侵拿到了写权限也没办法覆盖被锁定的文件。但它也有代价需要修改配置时得先chattr -i临时解锁改完再锁回去。自动化部署脚本如果对这类文件做覆盖操作经常会莫名失败这就是典型的chattr陷阱。排查“为什么明明有权限却改不了文件”时我建议在ls -l之后顺手执行lsattr很多灵异事件当场破案。另外要提醒chattr i是把双刃剑锁了不该锁的系统文件可能导致系统无法正常更新。我一般只对少数几个核心配置文件比如 sshd 配置、审计规则使用业务数据目录不太建议因为数据的频繁变动会让解锁/上锁操作变成不必要的心智负担。6.4 sudo 与权限提升的边界sudo解决的问题和 SUID 类似都是“低权限用户执行高权限操作”但机制完全不同——SUID 是程序运行时自动切换有效 UIDsudo 是用户被显式赋予特权后以 root 身份执行指定命令。sodo原文如此疑为 sudo 误写配置在/etc/sudoers里建议永远用visudo编辑因为这个命令会做语法检查写错了不会直接保存。sudo 的授权粒度可以做到很细可以允许某个用户只执行某几条命令、在特定主机上、不需要密码。但我踩过的一个坑是让用户通过 sudo 执行vim或less等高交互式命令等于变相授予了 root shell——在vim里执行:!bash就能拿到 root 权限。所以需要仔细设计 sudo 规则尽量避免把这种能逃逸到 shell 的编辑器类命令交给普通用户。若必须开放至少要用sudoedit配合受控目录或者加NOEXEC类限制。另外注意 sudo 的secure_path。默认配置会把 PATH 限制在一组安全目录防止执行当前目录下的恶意同名命令。如果自己定制 sudoers 时不小心把当前目录加进了 secure_path就等于给攻击者递了一把钥匙。7. 踩过几次权限坑之后我的几条硬经验权限问题最磨人的不是它有多深奥而是它经常“半懂不懂也能跑”等出问题时已经很难回溯了。我这几年的体会挑几条最实用的分享出来。第一改任何重要目录的权限之前先备份现场。用getfacl -R 目录 恢复文件把完整权限存下来出问题后用setfacl --restore恢复文件一键还原。这条命令比任何“后悔药”都靠谱我处理线上事故时全靠它兜底。第二不要把chmod -R和chown -R当成常规操作。递归修改是不可逆的批量变更改之前先想清楚哪些文件不该被改动。比如递归chown之后很多需要 SUID 的二进制文件的安全位可能被清除服务就悄悄变得不正常了。第三理解“有权限”和“应该做”是两回事。权限机制只是技术边界真正的安全取决于你给每个账户、每个进程分配了多大的信任边界。一个被攻破的低权限账户如果系统中存在大量 777 文件和不合理的 sudo 规则照样可以造成巨大破坏。最后把权限知识当成一个持续更新的工具箱而不是背完就丢的考点。每遇到一次Permission denied都按第 6 节那条链路走一遍顺手记下根因。攒上几个月你会发现自己对系统安全的理解上升一个层次排查速度也快得不像话。