你有没有遇到过这种情况:明明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表示)。三种操作能力就是读(read,r)、写(write,w)、执行(execute,x)。
用ls -l查看一个文件时,第一列那 10 个字符就是权限的“门牌”:
-rwxr-xr-- 1 root root 1234 Feb 14 10:00 test.sh第一个字符表示文件类型,-是普通文件,d是目录,l是软链接。后面 9 个字符每 3 个一组,分别对应属主、属组、其他人的权限。上面的例子就是:属主可读可写可执行,属组可读可执行,其他人只可读。
很多新手会背“r=4,w=2,x=1,加起来是 7”,但为什么是这个数字?这里我建议你用二进制的思路去理解:每个权限位其实就是一个 bit,1 代表有权限,0 代表没有。rwx对应二进制111,转成十进制正好是 7;rw-对应110,是 6;r--是100,是 4。这么一想,chmod 755和chmod 754就不再是魔法数字了,而是“我想把哪些 bit 打开”的直观表达。
1.2 目录的 w 权限才是“删除文件”的关键
这里我必须先讲一个最容易被误解的点:一个文件能不能被删除,取决于它所在目录的写权限,而不是文件自身的写权限。
很多人第一次遇到这种情况都会懵:我明明对文件有rw权限,怎么rm的时候系统告诉我“Permission denied”?原因很简单,rm这个操作本质上不是在“修改文件”,而是在“修改目录的目录项”。你要把文件名从目录的列表里摘掉,操作系统检查的是你对这个目录有没有写权限。所以,如果你对某个文件只有r权限,但对它所在目录有w权限,你依然可以把这个文件删掉。
目录的r权限决定你能不能列出目录里的文件名(ls),x权限决定你能不能进入目录(cd)以及能否访问其中文件。这里有个更隐蔽的坑:如果你对一个目录只有r权限而没有x权限,那ls确实能看到文件名,但当你尝试访问文件内容或使用stat查看详细信息时,系统可能报错。因为x权限在目录上的语义是“可通行”,没有它,你对目录内容的访问无法“深入”。
1.3 文件权限的“重命名与硬链接”边界
既然说到目录写权限的重要性,顺便提一个和重命名、硬链接相关的知识点。重命名文件同样属于修改目录项的操作,所以能否重命名也取决于目录权限,而不是文件权限。
硬链接是另一个容易让人疑惑的地方。硬链接的本质是在另一个目录里新增一个目录项,指向同一个 inode。因此,创建硬链接时,你不能跨文件系统,而且通常需要你对目标目录有写权限。如果你在一个目录里尝试给别人的文件创建硬链接,但该目录没有w权限,一样会失败。
我把文件与目录的权限语义差异整理成一个对照表,方便你记忆:
| 权限位 | 文件语义 | 目录语义 |
|---|---|---|
| r | 读取文件内容 | 列出目录中的文件名(ls) |
| w | 修改文件内容 | 在目录中创建、删除、重命名文件 |
| x | 将文件作为程序执行 | 进入目录(cd),访问目录内文件 |
| 常见坑 | 修改文件用 w,执行用 x | 删除文件看目录 w,进入目录看 x |
2. chmod / chown / chgrp 实操拆解:命令背后的易错点
2.1 chmod 数字法与符号法各自的应用场景
chmod是调整权限最常用的命令,它有两种写法,对应不同习惯。
数字法的好处是简洁、可批量:
chmod 750 script.sh这句命令把script.sh设置为:属主rwx,属组r-x,其他人无权限。它特别适合在脚本里批量处理,或者按固定的权限模板给一批文件套用。
符号法的好处是精确、不用心算:
chmod u+x script.sh # 给属主加执行权限 chmod g-w,o-r file.txt # 属组去掉写权限,其他人去掉读权限 chmod a+r file.txt # 给所有人加读权限我自己的经验是:交互式操作时用符号法居多,因为不需要做加法;但在批量部署脚本、需要把权限状态固化下来的场景,用数字法更稳妥。有一点要特别注意:用数字法给目录赋值时,chmod -R 755 /some/dir会把目录下所有文件也统一改成755,但有些可执行文件并不需要被“所有人执行”,这一下可能把权限面放大。谨慎的做法是先find区分文件和目录再分别处理,或者用下面的chmod参数来区分操作对象:
chmod -R u+rwX,go+rX /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 g+s /data/team_project/subdir其实只要你设置目录时用了带 SGID 的2770,新建的子目录多数发行版会自动带上 SGID。但如果你用mkdir手动建了子目录,还是检查一下。
第三步,测试实际效果。以两个不同用户(比如alice和bob)分别登录,尝试创建文件、修改文件、删除对方文件。你会发现在这个目录里,大家都能读写,但能否删别人的文件取决于目录是否还开启了 sticky bit。上面例子中没有设置 sticky bit,所以组员是可以相互删除文件的;如果你希望只能删自己的文件,就把权限设为3770(SGID + Sticky Bit)。
这个案例的完整逻辑是:
| 需求 | 对应的权限设计 |
|---|---|
| 组成员可读写进入 | 属组权限rwx |
| 其他人不可访问 | others 权限为 0 |
| 新文件自动归属组成 | 目录设 SGID |
| 防止随意删除他人文件 | 目录加 Sticky Bit |
3. 特殊权限位: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就是 SUID(Set User ID)位。它的作用是:当一个带有 SUID 的程序被执行时,进程的有效用户 ID(effective 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:目录协作场景里的“组继承器”
SGID(Set Group ID)有两种作用方式。用在可执行文件上时,进程的有效组 ID 会切换为文件属组;用在目录上时,所有在该目录下新建的文件和子目录都会自动继承该目录的属组。
团队共享场景下,SGID 的价值非常大。假设/data/team_project属组是devteam,且开启了 SGID,那么alice在内创建文件时,文件的组会自动变成devteam,而不是alice的主组(比如alice)。这样其他组员才能顺利按组权限访问。
设置目录 SGID 的命令:
chmod g+s /data/team_project # 等价于 chmod 2770 /data/team_project检查目录是否生效,可以用ls -ld看权限位是否有s:
ls -ld /data/team_project # drwxrws--- 2 root devteam 4096 Feb 14 10:30 /data/team_project注意,SGID 在文件和目录上的表现不同,这是面试和实际排错都爱考的点:文件上是“运行时切换组身份”,目录上是“新建内容继承属组”。
3.3 Sticky Bit:/tmp 的防误删设计
Sticky Bit 用在目录上时,含义是:即使目录本身允许所有用户写,用户也只能删除或重命名自己拥有的文件,不能动别人的文件。典型例子就是/tmp:
ls -ld /tmp # drwxrwxrwt 20 root root 4096 Feb 14 10:35 /tmp权限位最后的t就是 sticky bit,对应数字法中的1,所以设置方式是:
chmod 1777 /tmp/some_shared_dir如果你在一个多人可写的共享目录里想防止用户互相删文件,这个 bit 是首选方案。结合上一节的团队案例,3770(SGID + 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-x
umask 值越“大”,默认权限越“小”。安全场景里,很多人会把自己的 umask 设为077,这样新建文件只有属主才能读,属组和其他人一点权限都没有。
查询当前 umask:
umask临时修改:
umask 077持久化修改的话,一般写在~/.bashrc或/etc/profile中。如果是要影响所有用户的登录环境,系统管理员通常会在/etc/profile、/etc/bashrc或/etc/profile.d/下的脚本里设置全局默认值。
4.2 实战:不同角色定制默认权限
我曾经给一台多人开发机设置过默认权限,需求是:所有开发人员创建的文件默认让同组可读,但不能写;部分管理员目录希望文件默认同组可写。
思路是给不同用户设置不同的 umask。开发人员的~/.bashrc里加:
umask 022这样新建文件644,同组可读不可写。
对于需要协作编辑的目录,也可以考虑用chmod g+w在目录级别放开写权限,不过要注意这会让所有能进入该目录的组员都能改你的文件。若想更精确,建议用 ACL(见下一节)。
这中间有个常见问题:明明 umask 写好了,新建文件权限还是不对。排查思路是:
- 确认当前 shell 的 umask:
umask -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 放行,副作用很大。
这时候就该上 ACL(Access Control List)了。
查看文件 ACL:
getfacl /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:rwx,alice实际也只能拿到r-x。这也是实际排错时容易翻车的地方——ACL 明明给了权限,用户却依然进不去。
默认 ACL(default ACL)只对目录有效,设置后新建的文件会自动继承相应的 ACL 条项。这个特性很适合团队目录,不用再担心新文件掉权限。
5.2 root 的特殊性与 sudo 的边界
root 在传统 Linux 权限模型里几乎是“无法无天”的:不管文件权限是000还是r--,root 都能读;不管目录是否可写,root 都能删文件。内核里专门有一段逻辑检查当前用户是否是 root(UID 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'批量核对一遍关键目录。权限问题是最容易出安全事故的环节,靠脑子记不可靠,落到文档和脚本里才是正道。