前段时间帮同事排查一个生产环境的问题,他折腾了大半天,最后发现就是权限没配对。这个场景我见过太多次了,不管是刚接触 Linux 的新手,还是写了好几年代码的老手,跟权限打交道时多少都栽过跟头。Linux 权限这个事,说大不大,说小不小,但它就是那种"平时用不到、一用到就卡壳"的知识点。所以这期就把权限这个东西整个拆开揉碎了讲一遍,从底层原理到高频命令,再到实际排错思路,争取一篇文章帮你把这块地基打牢。
Linux 不是一个像 Windows 那样习惯用可视化界面管理的操作系统,绝大部分操作都靠命令行完成。而权限,就是你在这套系统里所有操作能不能顺利执行的根本。简单说,权限决定了谁能读文件、谁能写文件、谁能执行程序,谁能进某个目录。今天这篇主要面向 Linux 初学者、运维新手,以及那些一直用"复制粘贴命令但不知道原理"的朋友。我会用最贴近实际操作的讲法,把 Linux 权限相关的知识点讲明白。
1. 权限体系设计思路拆解
1.1 为什么 Linux 要把权限设计得这么"较真"
很多人第一次接触 Linux,最不习惯的就是动不动Permission denied。你可能会想:"我就改个文件,怎么这么多事?"但正是这种"较真",才让 Linux 在服务器领域占了几十年统治地位。
Linux 的权限模型继承自 Unix,是一种典型的DAC(自主访问控制)模型。它的核心逻辑很简单:每个文件都记录着"谁能碰我",而这个"谁"不是直接写人名,而是通过UID(用户ID)和GID(组ID)来标识。当一个进程想访问某个文件时,内核会比对发起这个操作的进程属于哪个用户、哪些组,再跟文件身上的权限信息做匹配,匹配通过就放行,不通过就报错。整个过程在内核层面完成,谁也没法绕过。
这种设计的巧妙之处在于,它虽然是权限模型里最古老最简单的一种,但它的简单反而成为优势——规则清晰、执行高效、没有复杂的上下文依赖。Windows 里那种 ACL 权限列表虽然更精细,但配置起来复杂得多,而且有时候你搞不清到底是什么规则在生效。Linux 这种"一个文件的权限就是九个字符"的设计,一眼看过去就能判断问题出在哪,这在排障时是巨大的优势。
1.2 权限的三种身份:你不是"你",你是"你的身份"
理解 Linux 权限,先要过一道坎:在 Linux 眼里,你通过用户名登录进来,但内核不认用户名,只认 UID。用户名是给人看的,UID 是给机器认的。你执行任何操作,本质上都是带着这个 UID 在跑。
每个文件都关联着三种身份,分别用三个字母表示:
- u(user/owner):文件的主人,也就是属主。
- g(group):文件所属的用户组,组内的所有成员共享这一组权限。
- o(other):既不是属主也不属于属组的人,也就是"路人"。
你对一个文件能做什么,取决于你在这三种身份里命中哪一个,然后就看那个身份对应的权限位给没给够。举个例子,你在一个团队里,项目文件属于devops组,你是这个组的成员,那你吃到的就是g那一段的权限,u的权限再大也跟你无关。这个"命中一个身份后只看对应权限位"的逻辑,是理解后面所有命令的基础。
1.3 用户和组的关系:一对多、多对多
在 Linux 里,用户和组不是简单的"一个人属于一个组",而是一个用户可以加入多个组。系统里有一个"主组"(primary group)的概念,用户创建文件时,文件默认归属的组就是主组。同时用户可以加入多个"附加组"(supplementary group),用来获取额外资源的访问权。
这里建议所有初学者先记住一句话:权限判断以"进程发起者的身份"为基准,而不是以"你当前登录终端窗口的界面"为基准。很多人用 sudo 执行命令后以为当前身份变了,其实只有那一条命令是以 root 身份跑的,其他还是普通用户。这些基础概念不搞清楚,后面看权限配置会越看越晕。
2. 权限的底层载体:文件属性与 inode 信息
2.1 ls -l 输出的每一个字符都别放过
每次执行ls -l,你都会看到类似下面这样的输出:
-rw-r--r-- 1 root root 220 2024-05-15 10:30 /etc/hostname这串输出里的第一个字段,-rw-r--r--,就是文件的权限标识,一共 10 个字符。第一个字符表示文件类型,后面九个字符才是权限位,三个一组:
- rw- r-- r-- │ │ │ │ │ │ │ └── other(其他人)的权限 │ │ └─────── group(属组)的权限 │ └──────────── user(属主)的权限 └──────────────── 文件类型第一位的-表示这是一个普通文件。如果你看到d就是目录,l就是符号链接,b是块设备,c是字符设备。后面九位里,r代表读,w代表写,x代表执行,没有权限的位置就是-。
2.2 怎么快速读懂一堆权限字符串
遇到drwxr-xr-x这种一串东西,别死记硬背,要按结构拆。拆开是这样的:d是目录,rwx是属主可读可写可执行,r-x是属组可读可执行但不可写,最后的r-x是其他人可读可执行但不可写。
我把 Linux 里最常见的几种权限组合整理了一下,都是日常高频出现的:
| 权限字符串 | 数字表示 | 含义 | 典型场景 |
|---|---|---|---|
-rw------- | 600 | 仅属主可读写 | 私钥文件、敏感配置文件 |
-rw-r--r-- | 644 | 属主可读写,其他人只读 | 普通配置文件、网页静态文件 |
-rw-rw-r-- | 664 | 属主和属组可读写,其他人只读 | 团队协作文件 |
-rwx------ | 700 | 仅属主可读可写可执行 | 私有脚本 |
-rwxr-xr-x | 755 | 属主全权限,其他人可读可执行 | 可执行程序、目录 |
drwxr-xr-x | 755 | 属主可读可写可进,其他人可读可进 | 最常见的用户目录 |
drwxrwxrwt | 1777 | 所有人可读写进,但只能删自己的文件 | /tmp 目录 |
其中644和755是最常见的两个数字,基本覆盖了绝大多数场景。如果你不确定该给什么权限,记住这两个值基本不会出大错。
2.3 目录权限跟文件权限的区别:很多人栽在这里
这里必须单独拎出来说,因为目录的 r/w/x 跟文件的 r/w/x 含义完全不同,这是新手最容易踩坑的地方。
- 文件的
r:可以读取文件内容。文件的w:可以修改文件内容。文件的x:可以执行这个文件。 - 目录的
r:可以列出目录里有什么(ls 能看到名字)。目录的w:可以在目录里创建、删除、重命名文件。目录的x:可以进入这个目录(cd 进去),也是能不能"穿过"这个目录访问里面文件的钥匙。
r和x在目录上经常要配着给。只有r没有x,你能ls看到一堆文件名,但看不到文件的类型、权限等详细信息,也进不去子目录。只有x没有r,你能进去但不知道里面有什么,除非你恰好知道确切文件名。所以在实际场景里,目录几乎都是r-x或rwx成对出现的。
一个特别容易被忽视的坑:你要访问一个深层路径下的文件,你必须对路径上的每一级目录都有x权限。比如你要读/home/alice/data/config.yaml,你至少要对/home、/home/alice、/home/alice/data都有执行权限,缺一个都不行。就算文件本身的权限是 777,只要中间某个目录挡了一道,照样拒绝访问。我见过太多人卡在这一步了。
3. 权限管理的三大核心命令实操
3.1 chmod:用数字还是用字母,我劝你都要会
chmod是 change mode 的缩写,专门用来修改权限位。它有两种表达方式,一种是数字法,一种是符号法。
数字法的逻辑是基于二进制位的:r的值是 4,w是 2,x是 1,三者之和就是一组权限的数字。比如rwx= 4+2+1 = 7,r-x= 4+0+1 = 5,r--= 4。然后按"属主、属组、其他"三个数字组合起来,就是chmod 754 file这样的格式。
chmod 754 script.sh # 属主全权限,属组读写执行,其他人只读执行 chmod 600 ~/.ssh/id_rsa # 私钥必须严格限制权限 chmod 755 /opt/app/bin # 给目录加执行权限,方便所有人进出符号法更适合在原有权限基础上做微调。它的格式是"谁+操作+什么权限":
chmod u+x file # 给属主加执行权限 chmod g-w file # 去掉属组的写权限 chmod o=r file # 把其他人的权限设为只读 chmod a+r file # 给所有人加读权限(a 代表 all) chmod -R u+rx dir # 递归给目录内的所有文件加读和执行权限我的使用习惯是:大范围设置用数字法,因为精确;小范围调整用符号法,因为不易误伤。新手容易犯的错是写chmod -R 777直接把整个目录放开了。这种操作在极少数临时调试场景下可以用,但一旦留在生产环境里,基本等于给攻击者开了一道门。后面我会专门讲替代方案。
3.2 chown:把文件还给该有的人
chown是 change owner 的缩写,改属主和属组的命令。工作里最常见的场景是把某个目录从 root 手里转给指定用户,或者统一修改一批文件的属主属组。
chown alice file.txt # 把属主改为 alice chown alice:devops file.txt # 同时修改属主为 alice、属组为 devops chown :devops file.txt # 只改属组,属主不变 chown -R alice:devops /data/webapps # 递归修改整个目录树有一个细节值得注意:chown可以一次既改属主又改属组,但只有 root 才能把文件的属主改成别的用户。普通用户就算对自己的文件,也只能用chgrp把属组改成自己所在的组,不能把属主改成别人。这是系统层面的安全限制,为的是防止你把文件甩锅给别人、绕过权限管理。
3.3 一个完整案例:部署一个 Web 服务的标准权限配置
综合运用一下上面的命令。假设我要在一台新服务器上部署一个 Python Web 应用,应用使用www-data用户运行,代码放在/var/www/myapp。
# 创建目录并交给 www-data 用户 mkdir -p /var/www/myapp chown -R www-data:www-data /var/www/myapp # 代码目录权限 750:属主全权限,属组可读可进,其他人不可见 chmod -R 750 /var/www/myapp # 日志目录要给写权限,因为运行时要写文件 chmod -R 770 /var/www/myapp/logs # 配置文件里的密钥文件,收紧到只有属主能读 chown root:www-data /var/www/myapp/.env chmod 640 /var/www/myapp/.env这套配置的逻辑是:代码文件只允许属主改,属组(www-data 所在组)可读可执行,外部其他人一律不可见;日志目录因为运行时需要写入,所以属组也给写权限;.env 文件里存着数据库密码之类的敏感信息,所以权限收紧到只有 root 本人能改、属组能读。这套思路的核心原则就是:只给"够用的权限",多给一份都是风险。
4. 特殊权限位:SUID、SGID、Sticky Bit
4.1 SUID:为什么普通用户能改密码
前面说过 Linux 权限是九个字符,但这只是常规权限。在特殊场景下,还会有第 4 个隐藏的权限位,就是SUID(Set User ID)。它的作用是:当一个可执行文件设置了 SUID 位,普通用户运行它时,进程的有效身份会自动切换成文件属主,而不是运行者本人。
最经典的例子是/usr/bin/passwd。普通用户要改自己的密码,就必须去写/etc/shadow文件,这个文件只有 root 能写。但系统怎么让普通用户改得了密码呢?关键就是 passwd 文件带着 SUID 位。你在终端里执行:
ls -l /usr/bin/passwd # -rwsr-xr-x 1 root root 68208 2024-01-15 /usr/bin/passwd注意属主的执行位不是x而是s,这就表示 SUID 已开启。普通用户执行 passwd 时,进程会以 root 的身份去写 shadow 文件,写完成就退出。权限控制得非常精准:我只让你改自己的密码,不给你任何其他 root 能力。
SUID 的设置方法是chmod u+s 文件或chmod 4755 文件(4 开头的四位数)。但我要强烈建议:没事别给任何文件加 SUID 位。SUID 意味着所有普通用户运行这个程序时都临时拥有属主的权限,如果属主是 root,那就等于任何人运行它都有 root 权限。这是典型的提权攻击入口,黑客拿下一个低权限账户后,第一件事就是找系统里异常的 SUID 文件。
4.2 SGID:让目录里新建的文件自动继承属组
SGID(Set Group ID)和 SUID 类似,但它作用于组。它对文件的效果是:运行该文件时,进程临时获得属组的权限。而它更常见的用途是在目录上:当一个目录设置了 SGID 位,所有在这个目录里新建的文件或子目录,属组会自动继承目录的属组,而不是创建者的主组。
这个特性在团队协作时非常有用。比如devops组的成员在/srv/project目录下各自创建文件,如果没有 SGID,文件会归属各自的个人主组,其他人就没法访问;设置了 SGID 就不一样了,所有新文件自动归devops组,组内成员都能按组权限访问。
chmod g+s /srv/project # 或用数字:2775,2 表示 SGID看权限字符串的时候,属组的执行位变成s就代表 SGID 生效。类似的,如果目录原本属组就没有执行权限,你会看到大写的S,那是无效的 SGID 位,得先补上x权限才有意义。
4.3 Sticky Bit:为什么 /tmp 里删不掉别人的文件
/tmp是一个公共目录,所有用户都能往里面写临时文件。那问题来了:如果所有人都能写,那 A 用户不是可以把 B 用户创建的文件删掉吗?Linux 的解决办法就是用Sticky Bit(粘滞位)。当一个目录设置了粘滞位,里面的文件只有文件属主、目录属主或者 root 才能删除或重命名,其他人就算有写权限也动不了。
ls -ld /tmp # drwxrwxrwt 6 root root 4096 2024-05-15 /tmp最后那个t就是 Sticky Bit 的标志。设置方式:
chmod +t /shared/tmp # 或用数字:1777在共享目录、上传目录这类多用户都要写、但不能互相删的场景下,粘滞位几乎是标配。
讲到这里,三个特殊权限的数字位可以放在一起记:4是 SUID,2是 SGID,1是 Sticky Bit。所以chmod 4755是带 SUID 的 755,chmod 2775是带 SGID 的 775,chmod 1777是带粘滞位的 777。
5. 扩展权限:ACL 与 sudo 的精细化管理
5.1 什么时候该用 ACL 而不是改权限位
常规的 u/g/o 三段式权限有一个局限:它只能给三种身份设置权限。但如果你的需求是"让 A 用户能读这个文件,让 B 用户能读写这个文件,其他人都不允许",三段式就搞不定了。这时候要用ACL(Access Control List,访问控制列表)。
ACL 可以理解为在常规权限之外,额外给指定用户或指定组单独设定权限。常用命令是setfacl和getfacl。
# 给用户 alice 单独设置对某文件的读写权限 setfacl -m u:alice:rw /data/shared/report.txt # 给 devops 组单独设置读执行权限 setfacl -m g:devops:rx /data/shared/ # 查看文件的 ACL 规则 getfacl /data/shared/report.txt # 删除某条 ACL 规则 setfacl -x u:alice /data/shared/report.txt # 清空所有 ACL 规则 setfacl -b /data/shared/report.txt设置 ACL 之后,ls -l输出的权限位后面会多一个+号。比如-rw-rw----+就说明这个文件还有额外的 ACL 规则。这里要提醒一句:ACL 虽然灵活,但它增加了排障的复杂度。出了问题别只盯着九个权限位看,记得用getfacl查一下有没有隐藏的 ACL 规则。我的建议是能用常规权限解决就不用 ACL,避免把系统权限搞成"一次性迷宫"。
5.2 sudo 权限管理与 sudoers 文件配置
最后说sudo,这是 Linux 里做精细化授权最重要的工具之一。它的配置集中在/etc/sudoers文件里,用visudo命令编辑(这是规范操作,直接用 vim 改有语法错误导致系统混乱的风险)。
# 允许用户 alice 执行所有命令 alice ALL=(ALL:ALL) ALL # 允许 devops 组成员以 root 身份执行 systemctl 开头的命令 %devops ALL=(root) /usr/bin/systemctl # 允许用户 bob 不用输入密码执行 /bin/systemctl restart nginx bob ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginxsudoers 文件的核心语法是"谁 在哪台机器上=(以谁的身份) 能执行什么命令"。日常运维中推荐遵循最小化授权的原则:能指定命令就指定命令,别给ALL;能指定服务就指定服务,别给全部权限。这看起来麻烦,但真出问题的时候,sudo 的限制越小,事故半径就越小。
另外,sudo 和 SUID 的区别值得理解:sudo 是通过配置文件精确控制"谁能以谁的身份执行什么命令";SUID 是"只要运行这个文件,临时获得属主身份"。sudo 更精细也更安全,所以在现代 Linux 管理实践中sudo是主流方式,SUID 的适用场景非常有限。
6. 权限故障排查:结合实例讲思路
6.1 场景一:明明文件权限是 777,为什么还是 Permission denied
之前有个朋友问我:"我chmod -R 777了目录,为什么我的脚本还是提示没权限?"我让他执行ls -ld看了一下目录的上层路径,结果一下就发现问题了——他给的权限是/data/app/script.sh,但/data这个目录的权限是700,属主是 root,而他用普通用户访问。也就是说,他根本没权限"穿过"/data这一层。
Linux 的路径访问逻辑是从根目录/一级一级往下走的,每一层目录都必须有执行权限,任何一层被卡住,后面的全都访问不了。这种问题排查起来也简单,从根目录开始逐级用ls -ld检查:
ls -ld / /data /data/app /data/app/script.sh哪一级权限看起来不对,优先怀疑它。然后看当前用户对路径上所有父目录的权限是否足够。这个思路比看文件本身的权限能更快定位问题。
6.2 场景二:挂载 Windows 或移动硬盘后所有文件都是 777
有段时间我挂载 U 盘或者 NTFS 格式的移动硬盘,因为 U 盘和 NTFS 的格式不支持 Unix 权限位,所以系统会在挂载时自动给所有文件一个默认权限。你可能看到所有文件都显示 777,怎么改 chmod 都没用。
这是文件系统本身不支持权限位导致的,不是系统坏了。正确的做法是调整挂载参数:
mount -t ntfs-3g -o uid=1000,gid=1000,umask=022 /dev/sdb1 /mnt/usbuid和gid指定挂载后文件归属哪个用户和组,umask指定文件的默认权限掩码。022的含义是去掉组和其他人的写权限,也就是文件默认644、目录默认755。如果你用的是桌面 Linux,图形界面下插 U 盘通常会自动处理;但服务器环境下手动挂载时就要特别注意这些参数。
6.3 场景三:服务启动失败,日志显示无法写入 PID 文件
这种坑我自己踩过不止一次。部署完一个应用,systemd 启动直接失败,日志提示无法创建 PID 文件。排查步骤是这样:
# 1. 查看服务以什么用户运行 grep '^User' /etc/systemd/system/myapp.service # User=myapp # 2. 查看目标目录权限 ls -ld /var/run/myapp /var/log/myapp # 3. 检查属主是否匹配 stat -c '%U %G' /var/run/myapp如果是/var/run下的子目录,很多 Linux 发行版重启后会清理,导致它重新以 root 创建目录、属主变成 root,应用用户反而没权限写。解决方式有两个方向:一是启动前让 systemd 自动创建并归属正确的用户,二是把 PID 文件配置到应用用户有权限的目录里。
排查这类问题的思路,说穿了就三步:先确认进程是以哪个用户跑的,再确认目标路径的属主属组是否匹配,最后确认目录里每一级的权限是否放行。
7. 养成好习惯:权限安全实践总结
踩过足够多的坑以后,我在实际操作中慢慢沉淀了几个习惯:第一,ls -l输出里的时间戳可以帮我快速判断文件是否被动过,所以权限不对时,看一眼文件属主和修改时间,很多问题当场就有头绪;第二,我很少在不知道后果的情况下使用chmod -R 777,很多场景用前面说的 SGID 或者 ACL 就能优雅解决问题;第三,每隔一段时间会用find / -perm -4000 -type f扫一遍有没有异常的 SUID 文件,这是检查系统是否被植入后门的常用手段之一。
在权限管理这个领域,"最小权限"四个字永远值得反复强调。给少了可能影响业务,给多了就是引狼入室。真正熟练的运维,不是在出问题时手忙脚乱地chmod 777,而是在部署之初就想清楚:这个目录谁要读、谁要写、谁要执行,然后只给这么多。长期下来,系统的稳定性和安全性都会好很多。