ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux权限机制深度解析:从用户身份到Permission denied排障

Linux权限机制深度解析:从用户身份到Permission denied排障 Permission denied大概是Linux新手撞见的第一堵墙。我当年刚把Linux当主力系统时ssh登录自己的云服务器vim打开nginx配置:wq直接弹错切到root再保存才成功。当时心想这权限也太玄学了——明明是我的机器凭啥不让我改后来做了多年运维和开发才慢慢看明白Linux权限一点不玄它就是一套环环相扣的规则——先回答我是谁再看文件怎么判断我的身份最后才轮到规则允不允许我这么干。这篇文章就顺着这条线索把我这几年的理解、踩过的坑、常用的排查套路一次讲透。适合刚入门Linux、被Permission denied折磨过、以及准备Linux面试的朋友。1. 先回答我是谁Linux用户身份的三个真相1.1 id 命令教你重新认识自己很多人进入Linux干的第一件事是敲 whoami然后得到一个用户名就心满意足。但真正排障时id 比 whoami 重要得多。打开终端执行 id你会看到类似这样的输出$ id uid1000(zhangsan) gid1000(zhangsan) groups1000(zhangsan),4(adm),27(sudo),998(docker)这行输出信息量很大uid1000(zhangsan) 表示当前用户是 zhangsanUID 是1000gid1000(zhangsan) 表示主组是 zhangsangroups 后面则列出了我同时属于的附加组——adm、sudo、docker。在Linux的视角里我是谁不是一个名字而是一串数字和组关系。附加组绝不是摆设。权限判断时内核会检查进程的 egid 是否与文件属组GID匹配同时也检查进程是否属于附加组。你被加进 sudo 组、docker 组之后往往要注销重新登录才生效因为新会话才会重新读取用户的组信息。我见过好几个人加完组后马上敲命令报错依旧气得以为是系统坏了其实只是没重新登录。遇到这种我明明加了组怎么还没权限的情况先切一个全新会话再用 id 确认。1.2 /etc/passwd 不是密码本/etc/shadow 才是“用户”在Linux里体现为一堆记录核心文件是 /etc/passwd。不要被名字骗了它不存密码存的是用户属性。一行一个用户冒号分隔7个字段zhangsan:x:1000:1000:Zhangsan:/home/zhangsan:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin这7个字段从左到右是用户名、密码占位符、UID、GID、注释信息、家目录、登录shell。密码那格的 x 表示真正的密码放在 /etc/shadow 里。shadow 文件权限是 640 且属主 root:shadow普通用户没资格读这样即使系统文件泄露密码哈希也不会直接暴露。老版本系统直接把哈希放在 passwd 里任何能读 passwd 的人都能拿去离线爆破后来才拆出 shadow 机制这也是面试爱考的历史题。很多人不理解为什么 daemon、www-data 这种根本没法登录的用户也占一行因为在Linux里用户和能登录的人是两回事。nginx、mysql 这类服务在启动时会以某个系统用户身份运行目的就是让进程拥有一个低权限身份即使进程被攻破攻击者也没有多少权限可越。这就是权限最小化的雏形。你自己新建的普通用户通常 UID 从1000开始分配系统服务账号则集中在1~999。看到 UID 小于1000、登录shell是 nologin 的用户基本可以断定是服务账号不用想着用它登录。1.3 文件系统只认数字UID 才是真正的身份证这是我见过最多人困惑的源头为什么把某个用户删掉之后他的文件还原封不动地留在磁盘上因为文件系统里记录的属主是 UID 数字不是用户名。你删除了用户文件里的数字还在ls -l 找不到对应名字就会直接显示数字。同理你修改了用户名文件的属主也不会变因为 UID 没变。root 的特殊性也在这个数字上。内核做权限判断时有一条独立的门槛如果进程的有效用户ID等于0绝大多数权限检查直接放行。这就是为什么你把某个普通用户的 UID 改成0他实际上就等于 root。日常操作中不要随便改 UID尤其不要制造第二个0号用户这是安全审计里非常刺眼的标记。2. 文件怎么看你的身份一行 ls -l 的完整解读2.1 十个字符的暗号怎么拆说完了我是谁再看第二个问题文件怎么看我。随便 ls -l 一下会看到这样的输出-rwxr-xr-x 1 zhangsan zhangsan 4096 Mar 12 10:00 script.sh drwxr-xr-x 2 zhangsan zhangsan 4096 Mar 12 10:00 webapp第一列的10个字符是权限的核心。第1个字符表示文件类型后面9个字符分成三组每组3个分别对应属主owner、属组group、其他人others的权限。文件类型常见的有- 普通文件、d 目录、l 软链接、b 块设备比如硬盘、c 字符设备比如终端、s 套接字、p 命名管道。这里有个很容易踩的坑软链接的权限位其实没有实际意义。你 ls -l 软链接时看到的是链接文件本身的属性而真正决定能否访问的是软链接指向的目标文件权限。所以对软链接执行 chmod 是无效操作改的其实是目标文件。新手经常对着一个 lrwxrwxrwx 纠结半天完全没必要。2.2 rwx 在文件和目录语境里几乎是两套规则这是全文最重要的部分。r、w、x 三个字母对文件和对目录的含义截然不同权限对文件对目录r读取文件内容列出目录里的文件名w修改文件内容在目录中创建、删除、改名文件x执行这个文件进入目录并访问其中文件通行证很多人只在文件语境里理解权限一遇到目录就懵。最典型的问题是为什么我能 ls 列出某目录下的文件名但 cd 进去却提示 Permission denied原因就是你有这个目录的 r能看到列表但没有 x不能进去。反过来有 x 没 r 的情况是你能 cd 进去但 ls 不到任何名字——因为读不到目录项。所以给外部用户开放一个目录标准做法是 rx不是只给 r。还有一个反直觉的点删除一个文件权限看的是父目录的 w 和 x而不是文件本身的权限。也就是说你在一个可写的目录里即使某个文件属于别人只要没有 sticky bit 约束你也能把它删掉——因为删除这个动作是作用于目录条目不是作用于文件内容。我实习时就搞反过chmod 777 了那个文件结果还是删不掉后来才明白要对父目录下手。对目录有写权限的用户可以随意改名、删除目录里别人创建的文件这对共享目录来说是个大隐患引出后面要讲的 sticky bit。2.3 chmod 与 chown改权限的正确姿势理论说完动手就是 chmod 和 chown。chmod 常用两种写法。数字法r4、w2、x1相加得到一位数字三位分别代表属主、属组、其他。比如 chmod 755 代表属主 7rwx、属组 5rx、其他 5rx。符号法chmod ux script.sh、chmod g-w file.log适合只调整某个角色不用整个重算日常改起来更安全。我个人的建议是新增文件给权限时用数字法一锤定音修改已有文件限时用符号法尽量不影响其他角色的权限。比如你想给脚本加执行权限chmod x script.sh 就够了不要写成 chmod 755因为后者可能顺手把别人的写权限也改了。chown 改属主属组。chown zhangsan:devops file 同时改属主和属组chown zhangsan file 只改属主chown :devops file 只改属组。加 -R 递归时务必小心这是经典事故触发点。我曾经在项目目录里执行 chown -R 时没有仔细确认路径差点顺着当前目录把 /home 下的东西全改了属主一身冷汗。现在我的习惯是递归操作前先 pwd再 ls 看一眼目标确认无误才动手。3. 内核如何裁决你能不能干从进程身份到命中即止3.1 ruid、euid 与 sudo 的本质前面聊的都是我是谁但真正干活的是另一层身份——进程身份。你在终端敲一条命令shell 会 fork 出一个子进程这个进程带着一组身份信息去访问文件。权限检查的对象不是终端里的用户名而是这个进程的 euid有效用户ID和 egid有效组ID。这里涉及三个容易混淆的概念ruid真实用户ID是启动进程的用户euid有效用户ID是内核做权限检查时看的身份默认等于 ruidsuid保存的用户ID供 setuid 程序切换身份后恢复用。大多数时候三者的值一样所以感觉不到差异。sudo 的本质就藏在这套机制里sudo 命令以你的身份启动但会切换到目标用户默认 root的身份再执行命令。也就是说sudo 不是临时给你开了个权限通道而是换了一重身份重新跑命令。理解了这一点就能明白为什么 sudo cat /etc/shadow 能成功而 cat /etc/shadow 会失败——前者进程的 euid 是0后者是你的1000。也正因如此sudo 授权的范围、是否需要密码都由 /etc/sudoers 控制别随便给普通用户无密码 sudo 权限。3.2 owner → group → other命中即止的匹配逻辑内核判断一个进程能否访问文件时用的是命中即止的顺序先看进程的 euid 是否等于文件的属主 UID相等就按属主权限位判断不再往下看不相等再看进程的 egid 或附加组是否匹配文件的属组 GID匹配就按属组权限位判断最后才轮到其他人权限。不存在把属主、属组、其他三个权限叠加取最大这种事。这个顺序解释了很多怪现象。举个例子文件属主是 zhangsan属组是 devops权限位是 rw-r-----你属于 devops 组按理应该有 r 权限但如果你的 UID 正好是文件属主也就是说这个文件就是你的系统会按属主权限位判断——如果属主位是 rw-你反而没有 r 之外的其他权限即使组位可能更宽松。再比如文件对属主是空权限对属组是 rw而你是属主你就会直接被拒绝哪怕你是属组成员——因为第一个匹配到的层级是 owner已经决定了结果。3.3 路径上的每一层目录都要过关namei 排障权限检查还有一个躲不开的前置条件路径上每一层目录都必须有 x 权限进程才能走到目标文件。访问 /home/zhangsan/secret.txt 时内核要依次检查 /、/home、/home/zhangsan 的权限最后才检查文件本身。任何一层目录被卡住都会报 Permission denied而且报错往往不告诉你是哪一层卡的。排查路径权限问题我用得最顺手的是 namei 命令$ namei -l /var/www/html/index.php f: /var/www/html/index.php drwxr-xr-x root root / drwxr-xr-x root root var drwxr-xr-x root root www drwxr-xr-x root root html -rw-r--r-- root root index.php每一级的权限、属主属组一目了然比一层层 ls -ld 快得多。很多文件明明有权限却进不去的诡异问题用 namei 一查往往卡在中间某层目录上。4. 特殊权限与协作目录passwd 的秘密和 /tmp 的规矩4.1 setuid/setgid程序也能临时换身份这里解答一个经典谜题/etc/shadow 只有 root 能读写普通用户修改自己密码时passwd 命令凭什么能把新密码写进去看一眼 passwd 的权限$ ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 59976 ... /usr/bin/passwd注意属主权限位的 s这就是 setuid 位。普通用户执行这个文件时内核会把进程的 euid 临时设置为文件属主 root于是 passwd 进程能以 root 身份修改 shadow但执行命令的用户依然是普通用户。这样一来用户只通过这个精心准备的入口改密码不能直接读 shadow也不会拥有完整 root 权限。setuid 可以出现在属主的 x 位上setgid 出现在属组的 x 位上。如果 s 位是大写的 S说明该文件没有 x 位这种情况下特殊位不生效但往往意味着配置异常是安全隐患。系统里真正的 setuid 二进制屈指可数可以用 find / -perm -4000 2/dev/null 扫一遍看到意外的 setuid 文件要格外警惕——这类文件一旦可被利用等于拿到一把提权钥匙。4.2 sticky bit为什么 /tmp 可以随便写却不能乱删再看 /tmp 目录$ ls -ld /tmp drwxrwxrwt 20 root root 4096 ...权限位最后的 t 是 sticky bit翻译成中文常叫粘滞位。它的作用是在设置了 sticky bit 的目录里即使目录本身是任何人可写也只有文件属主、目录属主或 root 才能删除或改名文件。这样 /tmp 虽然是 1777任何人可读可写可进入普通用户也不能把别人放在临时目录里的文件删掉。面试经典题就是为什么 /tmp 是 1777 而不是 777答案就是 sticky bit。数字写法里特殊权限有专门的位setuid4、setgid2、sticky1放在三位权限之前。chmod 1777 就是 1sticky 777任何人rwx。4.3 一个实用案例设计一个两人共享的协作目录假设要让 zhangsan 和 lisi 在同一个目录下协作两人都能创建文件、读取对方的文件。标准流程是建组并加人groupadd devopsusermod -aG devops zhangsan lisi建目录并设组属mkdir /srv/sharedchown root:devops /srv/shared给协作权限chmod 2770 /srv/shared。这里的 2 是 setgid 位作用是让目录下新建的文件自动继承目录属组 devops而不是创建者的主组。如果不加 setgidzhangsan 创建的文件属组是 zhangsanlisi 自然没有写权限加了之后文件属组都是 devops两人就能互相协作。如果担心误删可以再加 sticky bit变成 3770这样目录内文件只能由属主本人删除。这道题在面试里出现频率极高考察点就是 setgid 的继承逻辑。能讲清楚 setgid 解决新文件组归属这一个点基本就有80分了。5. 权限报错排障实录从Permission denied倒推真相5.1 一套固定的排障顺序遇到 Permission denied我的排查顺序基本是固定的按这个来可以少走很多弯路用 id 确认当前进程身份搞清楚我是谁用 ls -ld 逐级检查路径上每一层目录的权限不要只看文件本身用 namei -l 把整条路径权限列出来快速定位是哪个层级的目录卡住如果这时还很正常看 dmesg 或 journalctl -k 里有没有 SELinux/AppArmor 的 avc 拒记记录如果涉及容器再想一下容器内外的 UID 映射关系。问题通常集中在四个地方路径中间某层目录缺 x文件属主属组看错特殊权限干扰SELinux/AppArmor 拦截。前两个占绝大多数。5.2 Docker 挂载卷的 UID 错位容器化时代最常踩的坑之一把宿主目录挂载进容器后容器里的应用报没有权限。原因不难理解容器内进程用的是镜像里定义的 UID比如 nginx 官方镜像用 www-dataUID 是101而宿主机上挂载目录的属主是我自己的用户 zhangsanUID 1000。容器进程访问挂载目录时底层直接查宿主 inode 的权限101 号用户去访问属主为1000的目录按 owner→group→other 落到 other 权限啥也不匹配自然被拒。解决思路有三条看场景选择调整宿主目录属主给容器内需要的 UID比如 chown -R 101:101 ./data用 docker-compose 的 user: 1000:1000 让容器进程以宿主机用户的 UID 运行在 SELinux 环境下给挂载目录打正确的 context 标签。我自己的经验是先搞清楚容器进程到底以什么 UID 跑进容器敲一句 id -u 就清楚了别在外面瞎猜。很多人在容器里调半天结果只是宿主目录属主不对一条 chown 就能解决。5.3 U盘与 NAS 挂载后的权限怪异U盘、移动硬盘、NAS 挂载到 Linux 上经常出现能读不能写或者所有文件看起来都是 root 属主的情况。这涉及文件系统类型的差异vfat/exfat/ntfs 并没有 Linux 的 rwx 权限位概念它们挂载时会把所有文件映射成同一个权限由挂载选项决定。典型修法是手动挂载并指定属主和 umasksudo mount -o uid1000,gid1000,umask022 /dev/sdb1 /mnt/usb这样 U盘中所有文件在 Linux 看来都属于 uid1000 的用户权限是 755 之类的可写状态。查看当前挂载选项用 mount | grep /mnt/usb。如果你发现 U盘里的文件全是 root 属主、700 权限基本可以断定是之前挂载时用了默认 uid0只需要重新挂载即可不需要去 chmod因为对这类文件系统 chmod 往往不生效这也是很多人对 U盘 chmod 失败感到困惑的原因。5.4 别急着 sudo也别动不动 chmod 777权限一时调不通最省事的办法是 chmod -R 777但这个方案我强烈不建议。777 意味着任何本地用户都能读写执行等于把服务器上的防线拆掉了一大半。我见过因为项目目录被 777结果同级开发者误删整个目录的真实事故——权限太开放人人都有操作能力错误也随之放大。更稳妥的修复思路是先看文件应该属于谁就把属主改给对应的服务用户再按进程运行时需要的权限来给能读就给读能执行就给执行绝不顺手多给。比如 nginx 需要读静态资源给 644 就够了不需要写权限目录则需要有 x 权限才能进入给 755。权限修复的目标从来不是能用而是刚好够用——够用之外多出来的每一点权限都是日后的风险。6. 进阶ACL、能力位与 SELinux三个绕不开的第二权限层6.1 ACL突破一属主一属组的限制传统 ugo 模型只能给一个属主、一个属组、其他所有人各一套权限一旦出现组A只能读、组B只能写、某个特定用户只读、其他人没有权限这种需求普通权限就抓瞎了。ACL访问控制列表就是为这个场景设计的setfacl -m u:zhangsan:rw /srv/data/file setfacl -m g:devops:rx /srv/data/file getfacl /srv/data/file设置之后ls -l 的权限位末尾会出现一个 号表示该文件带有 ACL。ACL 里的规则不会走传统的 other 匹配它的优先级更高能实现比 ugo 更精细的授权。需要提醒的是ACL 和传统权限并不矛盾它们叠加生效。getfacl 输出里的 mask 字段是最大有效权限如果 mask 是 r-x那么即使某条 ACL 写了 rww 也会被 mask 挡住。很多人在 ACL 里配了权限却不起作用多半是 mask 卡住了用 setfacl -m m:rwx 调整 mask 即可。6.2 capabilities把 root 拆成一张张能力卡setuid 太粗暴——一下把整个 root 权限借出去。现代 Linux 提供 capabilities 机制可以把 root 的超级权限拆成几十项小能力比如 CAP_NET_BIND_SERVICE 让进程能绑定1024以下端口CAP_DAC_OVERRIDE 让进程能绕过文件权限检查。nginx 想监听 80 端口可以不用 root 跑只需给可执行文件加上能力位sudo setcap cap_net_bind_serviceep /usr/sbin/nginx这个机制在容器里尤其常见容器默认会丢弃大量危险 capability所以容器里的 root 和宿主机 root 不是一回事。面试聊到容器安全时能说出容器用的是 capabilities 收缩而不是完整 root会非常加分。6.3 SELinux当所有权限都正常却仍然被拒文件权限、属主、ACL 全都没问题但还是被拒这时就该想到 SELinux 了。SELinux 是强制访问控制机制在传统的谁能访问什么文件之上再加了一层哪个进程能访问什么类型文件的规则。判断是不是它拦截最直接的方法是查内核日志dmesg | grep avc journalctl -k | grep avc看到 typeAVC 开头的记录比如 avc: denied { read }, 基本可以确定是 SELinux 的拦截。临时验证可以 setenforce 0让它进入宽容模式但生产环境不要长期关掉。正确的处理是给文件打上正确的 context 标签用 ls -Z 查看当前标签再用 chcon、semanage 等工具修正。比如网页目录文件被拘留在 home 类型 context 下Web 服务读不到最常见的原因就是需要改成 httpd_sys_content_t。7. 面试现场这几个权限问题最能看出基本功7.1 文件为什么是 644目录为什么是 755这个问题几乎每轮面试都会出现。标准答案不是背数字而是说清逻辑普通文件默认不需要执行权限所以属主可读写是 6属组和其他人只读是 44组合成 644目录必须拥有 x 才能进入所以属主 7属组和其他人 5组合成 755。回答时如果能补一句这是权限最小化的默认表现考官会立刻觉得你懂原理而不是背过答案。7.2 删除文件到底需要什么权限高频题也是最容易答反的。删除一个文件需要的是对父目录的 w 和 x 权限文件本身的 rwx 与删除无关。如果目录还设置了 sticky bit那么只有文件属主、目录属主或 root 才能删除。这个问题考察的是文件与目录权限语义差异答完可以顺便提 /tmp 为什么是 1777把两个知识点串起来。7.3 什么是权限最小化的落地姿势很多候选人张口就说要最小化但讲不出落地手段。我一般会等他们把这几条说出来服务不用 root 跑用独立系统账号文件只给运行所需的最少权限比如只读就给 640/644sudo 权限按命令白名单授权而不是给 ALL不用 777特殊场景用 setgid 共享目录容器里用 capabilities 收缩而不是裸 root。能把权限最小化落到这些点上才是真正做过生产环境的人。7.4 遇到容器权限错乱第一反应应该是什么最近面试经常遇到这条题。第一反应不是进容器里 sudo而是先搞清楚两件事一是容器进程的真实 UID用 id -u 确认二是宿主机挂载目录的属主和权限用 ls -n 看数字 UID 而不是用户名。这两个数字对齐了问题基本就解决了。讲这个分析过程比直接说chown 一下要专业得多——面试官想听的是你的排查逻辑不是背操作。这篇内容从我是谁到我能干啥完整走了一遍中间穿插了我实际踩过的坑和常用排障手段。以后再看到 Permission denied不用慌按 id 确认身份、namei 查路径、再排查特殊权限和 SELinux 这条链路走问题基本都能快速定位。
返回列表