
你有没有遇到过这种情况明明chmod 777都给了用户还是报“权限不足”或者删不掉一个文件ls -l一看自己明明有w权限却提示Operation not permitted。如果你对这些现象背后的逻辑一知半解那这篇关于 Linux 基本权限的梳理应该能帮你把整条线串起来。我最早是从 Windows 带过来的习惯总觉得权限就是“勾选一下”那么简单后来真上了生产环境被rwx、属主、属组这些概念折腾了好几回才慢慢摸清楚。这篇文章我不会照抄手册而是按照我自己排查问题、设计权限方案时的思考路径来写先讲清楚rwx在文件和目录上的本质区别再拆解chmod/chown/chgrp实操中的易错点接着延伸到 SUID/SGID/Sticky Bit 这套特殊权限机制然后聊聊 umask 对默认权限的影响最后补上 ACL、root 与排查流程。无论你是刚接触 Linux 的新手还是需要处理服务器权限问题的一线运维这篇都值得花十几分钟过一遍。1. rwx 三元组文件与目录的权限语义完全不同1.1 权限位到底是给谁看的Linux 的权限模型本质上是给“三类身份”分别设置“三种操作能力”。三类身份分别是文件的属主owner常用u表示、属组group常用g表示、其他人others常用o表示。三种操作能力就是读readr、写writew、执行executex。用ls -l查看一个文件时第一列那 10 个字符就是权限的“门牌”-rwxr-xr-- 1 root root 1234 Feb 14 10:00 test.sh第一个字符表示文件类型-是普通文件d是目录l是软链接。后面 9 个字符每 3 个一组分别对应属主、属组、其他人的权限。上面的例子就是属主可读可写可执行属组可读可执行其他人只可读。很多新手会背“r4w2x1加起来是 7”但为什么是这个数字这里我建议你用二进制的思路去理解每个权限位其实就是一个 bit1 代表有权限0 代表没有。rwx对应二进制111转成十进制正好是 7rw-对应110是 6r--是100是 4。这么一想chmod 755和chmod 754就不再是魔法数字了而是“我想把哪些 bit 打开”的直观表达。1.2 目录的 w 权限才是“删除文件”的关键这里我必须先讲一个最容易被误解的点一个文件能不能被删除取决于它所在目录的写权限而不是文件自身的写权限。很多人第一次遇到这种情况都会懵我明明对文件有rw权限怎么rm的时候系统告诉我“Permission denied”原因很简单rm这个操作本质上不是在“修改文件”而是在“修改目录的目录项”。你要把文件名从目录的列表里摘掉操作系统检查的是你对这个目录有没有写权限。所以如果你对某个文件只有r权限但对它所在目录有w权限你依然可以把这个文件删掉。目录的r权限决定你能不能列出目录里的文件名lsx权限决定你能不能进入目录cd以及能否访问其中文件。这里有个更隐蔽的坑如果你对一个目录只有r权限而没有x权限那ls确实能看到文件名但当你尝试访问文件内容或使用stat查看详细信息时系统可能报错。因为x权限在目录上的语义是“可通行”没有它你对目录内容的访问无法“深入”。1.3 文件权限的“重命名与硬链接”边界既然说到目录写权限的重要性顺便提一个和重命名、硬链接相关的知识点。重命名文件同样属于修改目录项的操作所以能否重命名也取决于目录权限而不是文件权限。硬链接是另一个容易让人疑惑的地方。硬链接的本质是在另一个目录里新增一个目录项指向同一个 inode。因此创建硬链接时你不能跨文件系统而且通常需要你对目标目录有写权限。如果你在一个目录里尝试给别人的文件创建硬链接但该目录没有w权限一样会失败。我把文件与目录的权限语义差异整理成一个对照表方便你记忆权限位文件语义目录语义r读取文件内容列出目录中的文件名lsw修改文件内容在目录中创建、删除、重命名文件x将文件作为程序执行进入目录cd访问目录内文件常见坑修改文件用 w执行用 x删除文件看目录 w进入目录看 x2. chmod / chown / chgrp 实操拆解命令背后的易错点2.1 chmod 数字法与符号法各自的应用场景chmod是调整权限最常用的命令它有两种写法对应不同习惯。数字法的好处是简洁、可批量chmod 750 script.sh这句命令把script.sh设置为属主rwx属组r-x其他人无权限。它特别适合在脚本里批量处理或者按固定的权限模板给一批文件套用。符号法的好处是精确、不用心算chmod ux script.sh # 给属主加执行权限 chmod g-w,o-r file.txt # 属组去掉写权限其他人去掉读权限 chmod ar file.txt # 给所有人加读权限我自己的经验是交互式操作时用符号法居多因为不需要做加法但在批量部署脚本、需要把权限状态固化下来的场景用数字法更稳妥。有一点要特别注意用数字法给目录赋值时chmod -R 755 /some/dir会把目录下所有文件也统一改成755但有些可执行文件并不需要被“所有人执行”这一下可能把权限面放大。谨慎的做法是先find区分文件和目录再分别处理或者用下面的chmod参数来区分操作对象chmod -R urwX,gorX /some/dir注意大写X它表示“仅当目标本身已是目录或已有执行权限的文件时才赋予执行权限”。这条命令在实际部署中非常实用它能避免可执行位被无脑放大。2.2 chown 和 chgrp修改归属时的身份限制chown用来改属主chgrp用来改属组。现代 Linux 里我一般直接用一条chown同时改两者格式是属主:属组chown alice:developers project/ chown -R alice:developers project/几个容易踩的坑第一普通用户通常不能把自己的文件chown给别人只有 root 能做。这个限制是内核级别的目的是防止用户通过“把文件丢给 root”来绕开配额或审计。如果遇到Operation not permitted先反省一下是不是没加 sudo。第二chown会同时清除文件的 SUID/SGID 特殊权限位。原因很好理解切换属主之后原来的 SUID 语义可能形成权限提升漏洞内核出于安全考虑直接把这些位清掉。所以如果你发现某个程序在执行chown后特殊权限丢了这不是 bug是保护机制。第三chgrp虽然单独存在但功能其实被chown user:group覆盖了。你可以把文件属组改成你所在的任何组但不能改成你不在的组除非有 root 权限。2.3 实战案例从零搭建一个团队共享目录这里我以一个常见的需求为例演示完整的权限设计过程。场景服务器上有一个/data/team_project目录团队成员都在devteam组中要求组内成员可以读、写、进入但不能随意删除别人的文件其他用户完全不可见。第一步创建目录并设置属组sudo mkdir -p /data/team_project sudo chown root:devteam /data/team_project sudo chmod 2770 /data/team_project这里2770的前置2是 SGID 位。目录设置了 SGID 后所有在目录内新建的文件/子目录会自动继承目录的属组devteam而不是创建者的主组。这一步对团队协作至关重要否则新文件可能带着个人主组其他组员就没有访问权限了。第二步让子目录也继承组并限制删除他人文件sudo chmod gs /data/team_project/subdir其实只要你设置目录时用了带 SGID 的2770新建的子目录多数发行版会自动带上 SGID。但如果你用mkdir手动建了子目录还是检查一下。第三步测试实际效果。以两个不同用户比如alice和bob分别登录尝试创建文件、修改文件、删除对方文件。你会发现在这个目录里大家都能读写但能否删别人的文件取决于目录是否还开启了 sticky bit。上面例子中没有设置 sticky bit所以组员是可以相互删除文件的如果你希望只能删自己的文件就把权限设为3770SGID Sticky Bit。这个案例的完整逻辑是需求对应的权限设计组成员可读写进入属组权限rwx其他人不可访问others 权限为 0新文件自动归属组成目录设 SGID防止随意删除他人文件目录加 Sticky Bit3. 特殊权限位SUID、SGID 与 Sticky Bit 的实际用途3.1 SUID为什么普通用户能改自己的密码先看一条命令ls -l /usr/bin/passwd # -rwsr-xr-x 1 root root 59976 Feb 6 2024 /usr/bin/passwd注意到属主权限里的rws了吗这个s就是 SUIDSet User ID位。它的作用是当一个带有 SUID 的程序被执行时进程的有效用户 IDeffective UID会切换为该程序属主的 ID而不是执行者的 ID。passwd命令的属主是 root所以普通用户执行它时进程以 root 身份运行才有权限去修改/etc/shadow这个只有 root 能写的密码文件。这是 Linux 里最典型的 SUID 应用也是理解“最小权限原则被打破”的经典案例。用数字法设置 SUID是在原有三位权限前加4例如chmod 4755 some_program对应ls -l显示为-rwsr-xr-x。这里我要给一个强烈建议永远不要轻易给自定义脚本设置 SUID尤其是那些可以被其他用户调用的脚本。SUID 是权限提升的“高压电”一旦脚本能被普通用户触发并执行几乎等于把 root 钥匙交了出去。排查安全问题时find / -perm -4000是检查系统里所有 SUID 文件的常用命令建议定期看一遍。3.2 SGID目录协作场景里的“组继承器”SGIDSet Group ID有两种作用方式。用在可执行文件上时进程的有效组 ID 会切换为文件属组用在目录上时所有在该目录下新建的文件和子目录都会自动继承该目录的属组。团队共享场景下SGID 的价值非常大。假设/data/team_project属组是devteam且开启了 SGID那么alice在内创建文件时文件的组会自动变成devteam而不是alice的主组比如alice。这样其他组员才能顺利按组权限访问。设置目录 SGID 的命令chmod gs /data/team_project # 等价于 chmod 2770 /data/team_project检查目录是否生效可以用ls -ld看权限位是否有sls -ld /data/team_project # drwxrws--- 2 root devteam 4096 Feb 14 10:30 /data/team_project注意SGID 在文件和目录上的表现不同这是面试和实际排错都爱考的点文件上是“运行时切换组身份”目录上是“新建内容继承属组”。3.3 Sticky Bit/tmp 的防误删设计Sticky Bit 用在目录上时含义是即使目录本身允许所有用户写用户也只能删除或重命名自己拥有的文件不能动别人的文件。典型例子就是/tmpls -ld /tmp # drwxrwxrwt 20 root root 4096 Feb 14 10:35 /tmp权限位最后的t就是 sticky bit对应数字法中的1所以设置方式是chmod 1777 /tmp/some_shared_dir如果你在一个多人可写的共享目录里想防止用户互相删文件这个 bit 是首选方案。结合上一节的团队案例3770SGID Sticky Bit的组合非常常见。需要留意的是Sticky Bit 只影响“删除/重命名”操作不影响“修改内容”。也就是说bob不能删alice的文件但如果有写权限他依然可以打开alice的文件清空内容。要真正防止内容被篡改得结合 ACL 或更细粒度的权限模型。4. umask 与默认权限新文件为什么一出生就带着某个权限4.1 umask 不是“减掉的权限”而是“屏蔽掉的权限位”很多资料把 umask 简单解释为“创建文件时默认减掉的权限”比如umask 022时新建文件权限是666 - 022 644新建目录是777 - 022 755。这个说法在大部分场景下能对上但本质上umask 是按位取反后的“掩码”它决定的是创建文件时哪些权限位会被强制清零。为什么新文件默认不是666、新目录不是777因为普通文件不应该默认获得执行权限除非程序显式要求比如脚本编译出来的二进制所以系统把基础权限设成了666而目录为了可进入基础权限是777。当 umask 为022时文件666去掉022的置位位得到644即rw-r--r--目录777去掉022的置位位得到755即rwxr-xr-xumask 值越“大”默认权限越“小”。安全场景里很多人会把自己的 umask 设为077这样新建文件只有属主才能读属组和其他人一点权限都没有。查询当前 umaskumask临时修改umask 077持久化修改的话一般写在~/.bashrc或/etc/profile中。如果是要影响所有用户的登录环境系统管理员通常会在/etc/profile、/etc/bashrc或/etc/profile.d/下的脚本里设置全局默认值。4.2 实战不同角色定制默认权限我曾经给一台多人开发机设置过默认权限需求是所有开发人员创建的文件默认让同组可读但不能写部分管理员目录希望文件默认同组可写。思路是给不同用户设置不同的 umask。开发人员的~/.bashrc里加umask 022这样新建文件644同组可读不可写。对于需要协作编辑的目录也可以考虑用chmod gw在目录级别放开写权限不过要注意这会让所有能进入该目录的组员都能改你的文件。若想更精确建议用 ACL见下一节。这中间有个常见问题明明 umask 写好了新建文件权限还是不对。排查思路是确认当前 shell 的 umaskumask -S确认是否有/etc/profile、~/.profile、~/.bash_profile、~/.bashrc里多处设置后执行的会覆盖先执行的检查程序本身是否显式调用了open()并指定权限参数比如mkdir(path, 0755)这类写死的模式umask 只是对它做“减掩码”处理无法让它变大umask 和进程的关系值得多说一句umask 是 shell 的内建状态子进程会继承。所以你在一个终端里改了 umask在这个终端里启动的所有程序创建文件都会受影响但其他终端、系统服务则不会。5. 进阶场景ACL、root 与权限排查思路5.1 ACL当基本权限不够用时基本权限模型只有属主、属组、其他人三档在真实项目中经常不够用。比如你想让alice对/data/project有只读权限但alice既不是属主也不在属组里按传统权限模型就得改属组或把 everyone 放行副作用很大。这时候就该上 ACLAccess Control List了。查看文件 ACLgetfacl /data/project给指定用户添加权限setfacl -m u:alice:r /data/project给指定组添加权限setfacl -m g:devteam:rw /data/project删除指定用户权限setfacl -x u:alice /data/project设置了 ACL 后ls -l显示的权限位末尾会多一个drwxrwx--- 2 root devteam 4096 Feb 14 10:40 /data/project此时真正生效的权限组合要以getfacl的输出为准不能只看ls -l的传统三位权限。ACL 还有一个很有用的特性mask。mask 限制了所有“命名的用户/组”在 ACL 中能拿到的最大权限。比如 mask 为r-x那即使setfacl -m u:alice:rwxalice实际也只能拿到r-x。这也是实际排错时容易翻车的地方——ACL 明明给了权限用户却依然进不去。默认 ACLdefault ACL只对目录有效设置后新建的文件会自动继承相应的 ACL 条项。这个特性很适合团队目录不用再担心新文件掉权限。5.2 root 的特殊性与 sudo 的边界root 在传统 Linux 权限模型里几乎是“无法无天”的不管文件权限是000还是r--root 都能读不管目录是否可写root 都能删文件。内核里专门有一段逻辑检查当前用户是否是 rootUID 0是的话基本跳过大部分权限判断。但有两个细节不要误解。第一root 也不是绝对无敌的面对只读文件系统、SELinux 强制策略、nosuid挂载选项等root 同样受限。第二sudo -u切换用户执行命令时权限判断按目标用户来算不会因为用了 sudo 就自动变成 root 权限除非目标用户就是 root。生产环境的原则是“能不用 root 就不用”。通过 sudo 按需授权命令白名单控制在/etc/sudoers里。这里我给出一个排查权限问题时常用的命令栈id # 当前用户、UID/GID、附加组 ls -ld /data/project # 传统权限位 getfacl /data/project # ACL 详细规则 mount | grep /data # 挂载选项是否包含 noexec/nosuid/ro getenforce # SELinux 是否启用及模式如果权限问题还是没解决最容易被忽略的凶手就是 SELinux。关闭 SELinux 或者临时放行测试setenforce 0这条命令会让 SELinux 进入 permissive 模式只记录不拦截。注意这只是排查手段不能作为长期方案。5.3 一套完整的权限排查链路我把自己踩过坑的排查路径整理成一个“问题-检查点”流程你可以照着走先确认“用户是谁”。id看用户属主组和附加组。很多时候用户明明在devteam组里但新加的组要重新登录或执行newgrp devteam才会在当前会话生效。再确认“文件/目录是谁的”。ls -l看属主、属组getfacl看 ACL。如果属组不对检查目录 SGID 是否生效或者直接chown修正。确认“挂载层”。mount看文件系统是否以ro、noexec、nosuid方式挂载。尤其常见于外接磁盘、云盘挂载点权限明明设了但每次写操作都被read-only file system挡住。确认“安全层”。SELinux 和 AppArmor 是排在权限模型之后的强制访问控制。ls -Z可以查看文件的 SELinux 上下文ausearch -m avc -ts recent可以查最近的拒绝日志。对初学者来说看日志是最快定位方式。如果还是 blocked检查程序自身逻辑。比如有些服务是以低权限用户启动的它能不能读某个文件取决于它启动时那个用户的权限而不是你当前终端的用户权限。这个链路走完绝大多数“权限不足”问题都能水落石出。最后再分享一个我的个人习惯每次搭建一个需要协作的目录我都会把权限设计、属主属组、ACL 规则写进 README 或运维文档并且用stat -c %a %U %G %n批量核对一遍关键目录。权限问题是最容易出安全事故的环节靠脑子记不可靠落到文档和脚本里才是正道。