多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Linux权限管理详解:从文件权限模型到SUID/Sticky Bit实战排错

Linux权限管理详解:从文件权限模型到SUID/Sticky Bit实战排错 1. 从Permission denied说起Linux权限到底在管什么先回想一下你第一次在Linux终端里敲命令时撞到的墙想进一个目录提示Permission denied想删一个文件提示Operation not permitted想跑一个脚本提示Permission denied。新手第一反应往往是这系统是不是坏了或者干脆甩一个sudo上去然后接着撞下一堵墙。实际上Linux的权限模型远没有你想的那么复杂。一句话概括就是Linux里所有资源都以文件的形式存在每个文件都记录了谁能对它做什么。你只要搞清楚三件事——文件是谁的、你又是谁、以及文件允许你和你的同伴们做哪些操作——权限问题就解决了一大半。我们用ls -l随便看一个文件的输出$ ls -l /etc/passwd -rw-r--r--. 1 root root 1487 Jan 8 15:16 /etc/passwd这串字符里藏着全部信息。从第二个字符开始rw-r--r--这9个字母分成三组每组三个分别代表属主user、属组group、**其他人other**的权限。字母r代表读readw代表写writex代表执行execute横线-代表没有对应权限。如果你看今天的资料时只记住一个公式请记住这个第一组的rwx针对文件主人第二组针对主人所在用户组的成员第三组针对系统里剩下的所有人。后面的root root分别表示文件的属主和属组——也就是说这个文件归root用户所有、归root组所有。你在文件系统的位置再特殊也跳不出这三组权限的判定逻辑。很多人容易把权限理解成文件能不能打开其实Linux的权限是分层的。拿一个目录举例你对目录有r权限意味着你能ls出它里面有哪些文件名有x权限意味着你能cd进去并接触到里面的文件有w权限意味着你可以在目录里创建和删除文件。注意删除文件的能力看的是你对目录是否有写权限而不是你对文件本身是否有写权限——这个细节我在后面会专门展开。提示ls -l显示的权限只是传统权限UGO权限。它还有更多拓展形态比如ACL、SELinux上下文、文件特殊属性等但那是后话。先把基础三层理清楚排查权限问题的速度能翻倍。2. 把chmod用明白数字法与符号法的计算逻辑chmod是修改权限的核心指令全称change mode。它有两种写法我强烈建议你把两种都吃透因为实际工作中你会同时遇到别人用两种风格留下的命令。2.1 数字法的来源rwx本质上是一组二进制开关先解释数字法因为它是理解权限运算的地基。r、w、x三个权限本质上就是三个开关开就是1、关就是0。三个开关组合出三位二进制数比如rwx就是111换算成十进制是4217r-x就是101等于4015r--是100等于4。所以一套完整的权限位-rwxr-xr--换算下来就是权限位二进制十进制含义rwx1117属主可读可写可执行r-x1015属组可读可执行不可写r--1004其他人只可读chmod 754 file就是这么来的它把属主设为7、属组设为5、其他人设为4。加一个-R参数可以递归应用到目录下的所有文件chmod -R 755 /data/www这段命令通常用在Web目录上——目录需要被所有人遍历x但只有属主能改内容w。数字法的好处是一眼看到底你想把文件权限调成什么样先算好数字一条命令写出去不会产生歧义。2.2 符号法是增量调整的首选符号法的语法是chmod [谁] [操作] [权限] 文件其中「谁」用u属主、g属组、o其他人、a全部表示「操作」用加权限、-减权限、直接设定表示。实际工作中最常见的场景是给脚本加执行权限chmod x run.sh注意这里省略了「谁」默认就是a即全部人都加上执行权限。如果你想只让属主能执行、其他人保持原样就要明确写ux。区别很大chmod x和chmod ax等价会让所有用户都拥有执行权限如果脚本里有敏感操作这就是妥妥的安全隐患。我处理过的服务器中至少有三台是因为脚本全权限可执行被利用来落地的。符号法适合做局部调整。比如我只想把属组的写权限去掉其他权限不动chmod g-w report.txt而不需要重新算一遍完整的数字。反过来涉及批量部署、脚本初始化的时候数字法更牢靠——因为它不受当前权限状态影响直接覆盖到位符号法是在现有权限上做增量如果现有权限和你预期不一致结果可能出乎意料。2.3 目录权限和文件权限要分开理解目录和文件在执行权限上的语义差别是新手栽跟头最密集的地方。文件上的x表示能否作为程序运行目录上的x表示能否穿过这个目录去访问里面的内容。很多人遇到文件明明有读权限cat却报Permission denied的情况往上查一层才发现是路径里某个中间目录没有x权限。我建议用这个心智模型目录是走廊文件是房间。你要走进房间看书读文件首先得能穿过走廊对目录有x权限然后还要有拿起书的权限对文件有r权限。走廊不允许通行房间里的书摆得再整齐你也碰不到。具体到命令上排查路径权限时最好带上ls -ld查看目录本身的权限而不是ls -lls -ld /home /home/user /home/user/data逐层确认每个目录的x权限是否到位。这个习惯能帮你省下大量灵异事件的排查时间。3. 属主与属组chown、chgrp的使用准则光有权限位还不够你得先把文件的主人搞清楚。chownchange owner负责改属主和属组chgrpchange group只负责改属组。常规用法是把属主和属组一次性搞定chown www:www /var/www/html chown -R www:www /var/www/html这条命令在部署Web项目时几乎必用。Nginx或Apache的进程通常以www用户运行如果目录的属主还是你的登录用户Web服务就写不进去缓存和上传文件于是你会看到一堆403 Forbidden或者failed to open stream: Permission denied。-R参数会递归地把目录下所有文件和子目录的属主一并改掉能少敲很多条命令但也正因为它是递归的执行前一定要确认路径写对了——我一个同事曾经手滑把/var/www写成了/var导致整个/var下所有文件的属主都变成了www日志、邮件、临时文件全部乱套。这类事故的根本原因就是递归操作没有先看清楚自己站在哪一层。chgrp独立使用的场景相对少一些但也不是没有。比如团队协作时你希望同组的人都能编辑某个文件但又不愿意把属主改出去chgrp devteam /shared/project.conf配合chmod 664同组成员就可以读写这个文件了。这样做的价值在于属主还是你你拥有最高控制权同组人能协作又不至于互相踩踏。再看一个更隐蔽的知识点修改文件属主的能力跟文件本身的权限无关跟你能不能sudo有关。普通用户不能把一个文件的主改成别人因为这是 root 才能执行的操作。为什么这么设计你可以换个角度想——如果任何用户都能把别人的文件改到自己名下那这个系统里就完全没有所有权这个概念了权限体系会瞬间崩塌。所以chown和chgrp基本都要求root身份运行。注意改文件属主这件事不要想着通过让文件属主把文件chown给别人绕过去。文件的当前属主确实可以用chown改自己的文件但实际上它也只能把文件改成自己所在的组或者改成别的用户这在旧版本某些条件下是允许的但大多数现代Linux发行版限制了普通用户的chown行为只有root才能随意改变属主。所以需要动属主就老老实实sudo。4. SUID、SGID、Sticky Bit三个容易踩坑的特殊权限位传统权限位之外Linux还有三个特殊权限位日常不显眼但一旦遇到坑比普通权限深得多。4.1 SUID为什么普通用户能修改自己的密码s在属主权限位出现就是SUIDSet User ID。带SUID的程序在执行时进程的有效用户ID会临时切换成文件属主而不是执行者本人。最典型的例子是passwd$ ls -l /usr/bin/passwd -rwsr-xr-x. 1 root root 35728 Nov 30 2023 /usr/bin/passwd注意属主权限位是rws而不是rwx。普通用户执行passwd时实际上以root身份运行了一小段程序因此能去修改/etc/shadow里自己的密码条目。要是没有SUID普通用户连自己的密码都改不了——因为/etc/shadow只有root能写。设置SUID的chmod写法是chmod us /path/to/file chmod 4755 /path/to/file数字法的4就放在三位权限数字的最前面表示SUID。但是SUID是非常危险的东西。一个root属主、带SUID权限的shell或解释器相当于给任何执行它的人都发了一张root临时通行证。实践中我见过有人为了方便给vim或者python加了4755权限结果等于把所有登录用户都变成了root这是一个可以直接导致服务器被攻破的失误。任何时候都不要给shell、编辑器、编译器这类可以衍生出子进程的工具加SUID。4.2 SGID让团队目录自动继承属组s出现在属组权限位就是SGIDSet Group ID。文件上的SGID意义和SUID类似执行时进程有效组会切换成文件属组而目录上的SGID则完全不同——在带SGID的目录里新建文件文件的属组会自动继承目录的属组而不是创建者的默认组。这个特性在做团队共享目录时非常有用。假设你维护一个/srv/teamdata目录想让组员创建的所有新文件都自动属于devteam组只需要chgrp devteam /srv/teamdata chmod gs /srv/teamdata之后不管哪个用户在这个目录里touch新文件文件属组都是devteam配合chmod 27702代表SGID770是rwxrwx---同组人就能一起读写。没有SGID的话每个人新建的文件都带着他自己的个人组属组其他人协作起来就得反复改chgrp非常痛苦。4.3 Sticky Bit/tmp目录为何乱不乱t出现在其他人权限位就是Sticky Bit粘滞位。它的作用很直接在一个设置了粘滞位的目录下只有文件属主或目录属主、root才能删除或重命名文件其他人即使对目录有写权限也删不动别人的文件。看看/tmp目录就明白了$ ls -ld /tmp drwxrwxrwt. 20 root root 4096 Jan 8 17:20 /tmp所有人对/tmp都有读写执行权限rwxrwxrwx但因为有t普通用户只能清理自己的临时文件动不了别人的。如果没有这个机制任何能登录系统的人都能在/tmp里把别人的配置文件删掉整个系统早乱套了。设置Sticky Bitchmod t /shared/tmp chmod 1777 /shared/tmp数字法的1就是Sticky Bit。4.4 特殊权限位一览表权限位数字表示文件上的作用目录上的作用SUID4执行时以文件属主身份运行无SGID2执行时以文件属组身份运行新文件自动继承目录属组Sticky Bit1无仅文件属主/目录属主/root可删文件排查权限问题时如果一台机器上出现了奇怪的安全事件优先查一下有没有不该存在的特殊权限位一条命令全局扫描find / -perm -4000 -type f 2/dev/null find / -perm -2000 -type f 2/dev/null5. 为什么新文件不是777umask和默认权限的计算方法很多人第一次发现我touch一个文件它的权限怎么是644而不是666的时候会一脸问号。这背后是umask权限掩码在起作用。5.1 umask的计算逻辑Linux创建文件时有一个默认的基准权限值文件是666因为文件默认不给执行权限防的是安全风险目录是777。然后系统会用这个基准值去掉umask中标记的权限位得到最终权限。umask是一个三位八进制数查看当前值$ umask 0022这里的022表示属主保留全部权限属组和其他人去掉写权限。怎么算的带着这个思路基准值666减去022其实是去掉写位得到644基准值777减去022得到755。所以新建目录通常是drwxr-xr-x新建文件通常是-rw-r--r--。注意umask不是简单的777 - 022 755这种十进制减法但实际结果在大多数不涉及特殊权限位的场景里恰好一样。更准确的理解是umask里出现的权限位在创建时会被清除掉。5.2 子Shell临时调整与永久设置临时调整直接敲umask 077之后创建的文件的默认权限会变成600目录变成700。这适合在共享服务器上创建私有数据时用防止同机其他用户看到你的文件。但要注意这个修改只在当前shell进程里生效——你新开一个终端就恢复原样了。想要永久生效就把umask写进/etc/profile、~/.bashrc等配置文件里。5.3 umask太大带来的坑把umask设成077固然安全但在团队协作场景下会产生一个让人摸不着头脑的现象你创建的文件队友一访问就提示Permission denied。本质原因就是umask把组权限和其他人的权限全屏蔽了。反过来说把umask改成000会让所有新文件变成666、新目录变成777等于人人可改安全隐患更大。个人建议个人终端保持022涉及敏感数据的目录单独用setfacl或chmod收紧不要全局调整umask。这比改完umask之后到处排查为什么这个文件别人访问不了要省心得多。6. 权限排错实战从报错到定位根因的排查链路光会指令还不够真实系统里的权限问题往往是一层套一层的。我梳理一套平时排查权限问题的完整思路供你参考。6.1 排查链路第一步明确执行者身份拿到一个Permission denied先回答两个问题我是谁我要操作的目标是谁的用一条命令就能同时看清楚id uid1000(alice) gid1000(alice) groups1000(alice),10(wheel)id输出当前用户的uid、gid和附组。很多时候你以为自己是alice在操作其实进程是以apache或nobody身份跑的——这类问题尤其多发于Web服务。遇到Web服务写不入目录别在alice的shell里试了直接切到服务运行用户下去测sudo -u www touch /var/www/html/test.txt一步就能复现问题也一步就能解决问题。6.2 排查链路第二步逐层检查路径目录权限cat /a/b/c.txt报Permission denied不代表c.txt本身权限有问题。先把路径拆开逐层看目录权限ls -ld / /a /a/b ls -l /a/b/c.txt getfacl /a/b/c.txt 2/dev/null只要中间某一层目录对其他用户没有x权限后面的一切努力都是白费。我记得排查过一个诡异案例/data目录属主是root权限是700但是有人非要用普通用户去访问/data/sub——这显然进不去因为第一层就把你挡在门外了。ls -ld看到的就是赤裸裸的现实。6.3 经典场景Docker容器报权限错误Docker相关的权限问题在热搜词里排得很靠前确实常见。最常见的两种第一种是容器内用户uid和宿主机目录属主对不上。比如容器内进程以uid 1000运行宿主机上挂载的数据目录属主是root且权限是700容器内自然无法写入。解决办法通常是把目录属主改成1000或者用--user参数指定容器运行用户再或者先chown再挂载mkdir -p /data/mysql chown -R 1000:1000 /data/mysql docker run -v /data/mysql:/var/lib/mysql ...第二种是挂载了docker.sock导致的权限放大这类属于安全设计问题不建议在个人机器上随意挂载。6.4 经典场景文件被锁死删不掉有时候你发现文件权限明明是rwx属主也是你但删除还是提示Operation not permitted。这时候问题不在chmod层面而在lsattr里的特殊属性上$ lsattr config.yaml ----i---------e--- config.yamli表示immutable不可变。带了这个属性的文件连root都不能随意修改或删除必须先去掉属性chattr -i config.yaml rm config.yaml热搜词里那句你需要来自administrators的权限才能删除在Linux世界的对应物多半就是这个i属性。chattr命令还有防病毒篡改的实际用途例如日志文件加上a属性append only只能追加不能覆盖能在一定程度上防止日志被销毁。6.5 收好这几条实用指令最后整理几条排查权限问题最高频的指令建议直接存进自己的笔记里# 查看当前用户身份及所属组 id # 查看文件/目录权限、属主属组 ls -l 文件名 ls -ld 目录名 # 查看文件ACL权限比普通权限更细 getfacl 文件名 # 查看文件特殊属性immutable等 lsattr 文件名 # 全局搜索特殊权限位文件 find / -perm -4000 -type f 2/dev/null # 修复Web目录权限属主改为服务用户 chown -R www:www /var/www/html chmod -R 755 /var/www/html7. 一条命令把权限改坏之后我的兜底办法在自己机器上练习权限指令最崩溃的瞬间往往来自一条过于自信的chmod。我见过有人在项目目录上执行chmod 777 -R结果整个目录变成人人可改Git提交记录里全是文件权限变更CI直接开始告警——虽然罪魁祸首只是想让Nginx能读文件。这类问题的恢复思路其实不复杂。大多数项目的目录权限是有明确规律的目录755、文件644。一条find命令就能把整个目录树恢复规范find /path/to/project -type d -exec chmod 755 {} \; find /path/to/project -type f -exec chmod 644 {} \;可执行脚本通常单独用chmod x补回来不用但心批量恢复会把执行位清掉——反正手动脚本本来就该逐个确认。如果是Git仓库只要还没有commit一条git checkout -- 路径也可以把工作区还原到版本库里的权限状态前提是版本库里记录的权限是对的。遇到过很多次这类事故之后我的习惯变成了权限相关的批量操作先加--preserve-root这类保护参数或者先用find干跑一遍去掉-exec确认目标范围再真正执行。面对几百台服务器的时候谨慎比熟练值钱得多。
返回列表