Linux文件与目录rwx权限差异及Permission denied排查 不少人在服务器上遇到过这种场景ssh登录之后cd /data/app正常紧接着ls却报Permission denied或者反过来ls能看到文件名cat打开时却被拒绝。更让人困惑的是明明已经chmod 777了某个文件删掉它的时候仍然提示权限不足。这些看起来自相矛盾的现象几乎都指向同一个根因把文件和目录的rwx当成了同一套语义。Linux 权限管理真正的分水岭就在这里。文件是数据容器它的rwx描述的是能不能读内容、能不能改内容、能不能当程序跑目录是名字到inode的映射表它的rwx描述的是能不能看见条目名、能不能增删条目、能不能穿过它走到下一层。同样是r在文件上是读字节在目录上是读目录项同样是x在文件上是执行在目录上是通行证。把这层差异想清楚绝大多数Permission denied都能在三分钟内定位。这篇文章不打算把chmod、chown的语法再抄一遍手册。重点放在三件事第一用一张对照表把rwx在文件和目录上的语义彻底拆开第二用可以直接复制运行的实验脚本把 8 种目录权限组合的行为逐一验证出来第三给出一套从现象到根因的排查顺序以及生产环境里真正该遵守的授权原则。如果你正在被容器挂载权限、上传目录权限、团队共享目录权限这类问题反复折磨这篇内容值得完整读完并收藏备用。1. 这篇文章真正要解决的问题权限报错最让人难受的地方不是它难而是它看起来不该出现。你明明给文件加了写权限程序还是写不进去你明明把目录设成了可读ls -l却只显示一堆问号你明明把文件设成了 777rm依然告诉你Operation not permitted。这些现象单独看都很反直觉但只要明白目录和文件是两套模型它们就变成了必然结果。先看第一个典型误解很多人以为读文件只跟文件的r位有关。实际上要读到/data/app/conf/app.yml内核需要对路径上的每一级目录都具备x权限也就是逐级穿过的能力。/data没有x即便app.yml是 644cat一样失败。这类问题在多层目录、容器挂载、systemd服务的工作目录设置里极其常见。第二个典型误解很多人以为删除文件是文件的权限问题。真实规则是rm操作修改的是父目录的目录项判定依据是父目录有没有w和x跟文件自己是 644 还是 777 毫无关系。反过来一个 444 的只读文件只要父目录可写照样能被删掉。这条规则不理解就永远解释不了为什么只读文件会被删和为什么全权限文件删不掉这两个看似矛盾的现象。第三个典型误解把目录的r和x混为一谈。目录的r只负责列出名字目录的x才负责进入和访问。只有r没有x的目录ls能看到文件名但任何一个文件都打不开ls -l也只能显示问号只有x没有r的目录ls直接报错但只要你知道文件名cat却能成功。这种看得见摸不着和摸得着看不见的错位是权限调试中最容易浪费时间的部分。这篇文章面向三类读者正在学习 Linux 基础、希望一次性把权限模型搞明白的后端开发者需要维护服务器、排查线上权限故障的运维同学以及在做容器化部署、被UID映射和挂载权限折磨的工程团队。读完之后你应该能做到两件事看到Permission denied时不再盲目chmod 777而是能按路径、按目录、按文件分层定位设计目录结构时能提前预判权限组合带来的行为而不是上线后被动救火。2. rwx 在文件和目录上的语义差异先把结论摆出来。理解这张表本文一半的内容就已经掌握了。2.1 文件上的 rwx权限位八进制含义典型操作缺失时的表现r4读取文件内容cat、less、grep、作为cp的源Permission deniedw2修改文件内容echo 、vim保存、truncatePermission deniedx1把文件当作程序执行./script.sh、/bin/lsPermission denied这里有一个非常重要的细节文件的w位只控制内容不控制存在。你不能通过给文件加w来让别人删掉它也不能通过去掉w来阻止别人删掉它。文件的删除完全由父目录决定这一点在后面的第 6 章会用实验验证。另一个细节是关于x位的。对于 ELF 二进制程序只要有x就能执行但对于 Shell 脚本必须有r和x两位。原因是内核识别到#!开头后会把文件交给解释器处理解释器需要打开并读取脚本内容。所以一个chmod 111 script.sh的脚本执行时会报错而且报错信息往往不是权限不足而是解释器层面的读取失败。这个现象经常被误判成脚本语法问题。2.2 目录上的 rwx权限位八进制含义典型操作缺失时的表现r4列出目录中的条目名ls、通配符展开ls: cannot open directory: Permission deniedw2在目录中创建、删除、重命名条目touch、rm、mv、mkdirPermission deniedx1进入目录、访问条目元数据cd、stat、按路径打开文件Permission denied目录的w有一个关键前提必须配合x才能生效。一个只有w没有x的目录200既不能cd进去也不能在里面创建文件因为路径解析本身就需要x。所以从实际效果看目录的 200 和 600 基本等价于什么也做不了w位形同虚设。2.3 目录 8 种权限组合的行为对照这张表是本文最值得记住的部分。假设目录/data/test的所有者是当前用户我们在其中预先放好一个known.txt。八进制符号ls列名cd进入ls -l详情读known.txt创建或删除文件000---否否否否否100--x否是否是否200-w-否否否否否300-wx否是否是是400r--是否否全为?否否500r-x是是是是否600rw-是否否全为?否否700rwx是是是是是表格里有两行特别值得注意。第一行是 100它说明看不见目录内容和能访问已知文件是可以并存的这在共享目录场景下其实是一种有用的配置给x不给r别人必须知道确切文件名才能访问无法枚举。第二行是 400 和 600它们说明w在没有x的目录上没有任何实际意义。3. 权限模型基础主体、八进制与 umask在动手实验之前把几个基础概念对齐后面的现象才不会看成玄学。3.1 三类主体与匹配顺序每个文件都有三组权限位分别对应属主uowner、属组ggroup、其他人oother。判定时内核按固定顺序匹配先看当前用户是不是属主是就只用u位不是再看是否属于属组是就只用g位都不是才用o位。这个顺序有一个容易被忽略的后果匹配只发生一次且不可回退。假设app.yml的属主是alice权限是ur--,grwx,o---。此时alice自己去读这个文件内核匹配到她是属主就只看u位的r组权限再大也不会生效所以她只能读不能写。反过来如果bob在属组里他匹配到g位就拥有完整的读写执行。理解这一点才能解释为什么我明明在组里还是没权限——因为你先被属主身份截胡了。还有一类特殊主体是root。root拥有CAP_DAC_OVERRIDE能力可以绕过读、写和目录搜索的权限检查但执行权限无法绕过一个文件如果三组权限位里一个x都没有root也执行不了。这解释了为什么用root做权限实验会得到全是成功的错误结论第 4 章会专门提醒。3.2 八进制表示与符号表示三位八进制是最常用的写法每一位由r4、w2、x1相加得到。755就是rwxr-xr-x644就是rw-r--r--600就是rw-------。如果出现第四位比如1755、2770、4755那一位是特殊权限位第 7 章会讲。符号法更适合做增量修改chmod ux file给属主加执行chmod g-w,or dir同时去掉组写、把其他人设成只读chmod ar file给所有主体加读。符号法的一个坑是chmod or这种写法会覆盖而不是追加等于把o位直接设成r--很多人误以为它只是加个读权限。3.3 umask 的作用方式新建文件的默认权限不是凭空来的而是由基准权限减去umask屏蔽位得到。基准值是文件 666、目录 777注意目录天生带x文件天生不带。常见umask是022于是新文件是644新目录是755。严格来说umask不是减法而是按位清除计算方式是mode ~umask。对于022这种每位都是独立2的值减法和按位清除的结果一样所以很多人误以为是减法。但如果umask写成027新文件就是640、新目录就是750这时候用减法算也对真正会出现差异的是非对齐值实际项目中不必深究按位清除理解即可。umask还有一个隐蔽来源通过systemd管理的服务可以用UMask指令单独设置它会影响该服务进程创建的所有文件。容器运行时也有类似的默认值。如果发现某个服务生成的文件权限总是 640 而不是预期的 644先去查服务的UMask配置而不是怀疑chmod没生效。4. 环境准备与实验环境搭建这一章的目标是把实验环境搭好保证后面的验证结果可信。如果你只想看结论可以跳过直接读第 5、6 章但亲手跑一遍的印象会深得多。4.1 环境要求与检查任意主流 Linux 发行版都可以内核版本没有特殊要求本文涉及的chmod、chown、namei、getfacl都是通用工具具体版本请以实际项目为准。不建议在 macOS 上做这套实验因为它的权限模型和 ACL 行为与 Linux 有差异容易得出错误结论。WSL2 可以用但不要把实验目录放在/mnt/c这类 Windows 挂载点下那里的权限位是被模拟的chmod结果不可靠。第一步先确认当前身份。这个检查非常关键# 检查当前用户和 UID以及所属组 id如果输出里uid0(root)说明你是root请务必切换到普通用户再做实验。root会绕过绝大多数权限检查所有应该失败的操作都会成功实验结论完全失真。# 如果你是 root创建一个普通测试用户并切换 useradd -m permtester passwd permtester su - permtester第二步确认工具可用which chmod chown namei getfacl setfaclnamei通常随util-linux提供getfacl和setfacl来自acl包。如果缺失按发行版的包管理器安装即可例如 Debian/Ubuntu 系执行apt install aclRHEL/CentOS 系执行yum install acl。4.2 创建隔离的实验目录所有实验都在一个独立目录里完成结束时整体删除不会污染系统# 创建实验根目录 export LAB$HOME/perm-lab rm -rf $LAB mkdir -p $LAB cd $LAB pwd使用$HOME下的目录有三个好处第一属主天然是当前用户不需要额外chown第二避开了/tmp上可能存在的 sticky bit 干扰第三清理时一条rm -rf就能收尾。4.3 关闭可能干扰结果的因素有两个外部因素会让实验结论偏离预期提前确认一下。第一是 ACL。如果目录上存在扩展 ACLls -l的权限位后面会出现号实际生效权限由 ACL 的mask决定可能比chmod显示的值更宽松。检查方式# 查看目录是否存在扩展 ACL正常应输出 # file: ... 且没有 user: 或 group: 条目 getfacl $LAB如果发现有额外条目用setfacl -b $LAB清除后重试。第二是挂载选项。某些分区以noexec、ro或nosuid挂载会直接屏蔽对应行为让人误以为是权限位没生效# 查看根分区和 home 分区的挂载选项 findmnt -no TARGET,OPTIONS / findmnt -no TARGET,OPTIONS $HOME重点看有没有noexec和ro。如果有换一个挂载点做实验或者先把实验做完再排查挂载问题。5. 文件 rwx 的逐项验证环境就绪后先验证文件侧的行为建立基准认知。5.1 读权限 r 的验证# 文件 r 权限验证 cd $LAB mkdir -p file-test cd file-test printf hello linux permission\n demo.txt chmod 600 demo.txt echo --- 当前权限 --- ls -l demo.txt echo --- 属主读取应当成功 --- cat demo.txt echo --- 去掉读权限后再读应当失败 --- chmod 200 demo.txt ls -l demo.txt cat demo.txt预期结果是第一次cat输出hello linux permissionchmod 200之后再次cat会输出cat: demo.txt: Permission denied。这里把权限设成200只有写是为了顺便验证只有写权限时不能读这个常识。需要说明的是这段代码用的是属主位u。因为当前用户就是文件属主内核直接匹配u位所以chmod 200才是当前用户视角下的实际权限。如果你想验证o位需要另建用户成本更高但在第 7 章的 ACL 示例里会涉及多用户场景。5.2 写权限 w 的验证# 文件 w 权限验证 chmod 400 demo.txt ls -l demo.txt echo --- 只读文件尝试追加应当失败 --- echo append line demo.txt echo --- 恢复写权限后追加应当成功 --- chmod 600 demo.txt echo append line demo.txt cat demo.txt第一段追加会报Permission denied第二段成功cat能看到两行内容。这里验证的是文件w位对内容的控制。接下来验证一个关键结论文件的w位和删除无关。# 删除操作不受文件自身权限影响 chmod 444 demo.txt ls -l demo.txt echo --- 父目录可写时444 文件也能删除 --- ls -ld . rm -f demo.txt ls -l如果父目录权限是700本次实验环境就是rm -f demo.txt会直接成功尽管文件是只读的444。这里-f的作用只是忽略文件不存在的提示和跳过确认不会绕过任何权限检查——这一点经常被误解值得强调。5.3 执行权限 x 的验证执行位的差异最能体现二进制和脚本的区别。先准备一个 Shell 脚本# 创建测试脚本 cd $LAB mkdir -p exec-test cd exec-test cat hello.sh EOF #!/usr/bin/env bash echo script executed, current user: $(id -un) EOF chmod 644 hello.sh ls -l hello.sh echo --- 无 x 位直接执行应当失败 --- ./hello.sh预期输出bash: ./hello.sh: Permission denied。加上执行位后chmod 755 hello.sh ./hello.sh这次输出script executed, current user: permtester。到这里都是常识真正的差异在下一步# 只给 x 不给 rShell 脚本会失败 chmod 111 hello.sh ls -l hello.sh echo --- 111 的 Shell 脚本执行应当失败 --- ./hello.sh预期会看到类似bash: ./hello.sh: /usr/bin/env: bad interpreter: Permission denied的报错。原因前面提过内核识别#!后把文件交给解释器解释器需要读取文件内容而111没有r位读取失败。对照实验是二进制程序# 只给 x 不给 r二进制程序仍可执行 cp /bin/echo ./myecho chmod 111 ./myecho ls -l ./myecho echo --- 111 的二进制执行应当成功 --- ./myecho binary runs without read permission这次能正常输出。原因是执行 ELF 程序时内核通过mmap加载可执行段并不要求用户具备读权限。这个对比说明可执行和可读是两个独立维度脚本因为需要解释器读取所以才需要r二进制不需要。6. 目录 rwx 的逐项验证这一章是全文的核心。前面那张 8 种组合的表格这里用代码逐行验证。6.1 搭建验证骨架# 准备目录实验环境 cd $LAB rm -rf dir-test mkdir -p dir-test/inner printf i am a known file\n dir-test/inner/known.txt cd dir-test ls -lR结构是dir-test/inner/known.txt接下来我们不断改变dir-test/inner的权限观察各种操作的结果。为了实验干净建议每验证一种组合前先恢复权限# 恢复为 rwx 的辅助函数 restore_perm() { chmod 700 $LAB/dir-test/inner; }6.2 只有读权限 400看得见摸不着chmod 400 inner ls -l inner # 能列出 known.txt 的名字 cd inner # 应当失败 cat inner/known.txt # 应当失败 ls -l inner # 详情全是问号逐步看结果。ls -l inner能列出条目但输出类似ls: cannot access inner/known.txt: Permission denied total 0 -????????? ? ? ? ? ? known.txt名字能看到其他元数据全变成?。cd inner报bash: cd: inner: Permission denied。cat inner/known.txt同样失败。原因很明确r只允许读取目录项名字列表而获取inode详情和打开文件都需要x。这个组合在实际场景中的价值是只让用户知道有哪些文件不让访问但因为ls -l的报错噪音很大很少有人真的这么配。6.3 只有执行权限 100摸得着看不见chmod 100 inner ls inner # 应当失败 cd inner # 应当成功 cat known.txt # 应当成功 ls -l # 应当失败无 r cat ../inner/known.txt # 应当成功 cd .. cat inner/known.txt # 应当成功ls inner报ls: cannot open directory inner: Permission denied。cd inner成功。进去之后cat known.txt成功输出i am a known file。但ls -l再次失败因为列目录需要r。这个组合是很多人第一次见到的反直觉场景目录明明进得去、文件明明读得了ls却不行。100 权限的实际含义是只要知道确切文件名就能访问但无法枚举内容在安全要求较高的共享目录里是有意设计的配置。6.4 只有写权限 200形同虚设chmod 200 inner cd inner # 应当失败 touch inner/newfile.txt # 应当失败 cat inner/known.txt # 应当失败全部失败。这里要特别注意touch inner/newfile.txt为什么也失败虽然目标目录有w位但内核在解析inner/newfile.txt这个路径时需要对inner具备x搜索权限才能定位到目录本身x缺失导致路径解析在第一步就断了。所以结论是目录的w单独存在没有意义必须与x组合。这也解释了为什么chmod 200 dir和chmod 600 dir的实际效果几乎一样。6.5 读写执行 700完整权限chmod 700 inner cd inner ls -l # 成功 touch newfile.txt # 成功 echo data newfile.txt # 成功 rm -f newfile.txt # 成功 cd ..全部成功这是标准的目录权限配置700表示只有属主能完整操作。6.6 删除行为的决定性验证这一段是全篇最重要的实验直接推翻文件权限控制删除的误解。# 场景一目录可写文件只读能否删除 chmod 700 inner chmod 444 inner/known.txt ls -ld inner ls -l inner/known.txt rm -f inner/known.txt # 应当成功 ls inner # 确认已删除 # 场景二目录只读文件全权限能否删除 printf i am a known file\n inner/known.txt chmod 777 inner/known.txt chmod 500 inner ls -ld inner ls -l inner/known.txt rm -f inner/known.txt # 应当失败场景一成功删除场景二报rm: cannot remove inner/known.txt: Permission denied尽管文件是777。这两组结果放在一起结论无可辩驳删除文件的判据是父目录的w和x与文件自身的权限位无关。补充一个容易踩的坑mv重命名也是同理。把文件从一个目录移到另一个目录需要源目录和目标目录同时具备w和x。如果只是原地重命名同一目录内则只需要该目录的w和x。还有一个更隐蔽的场景用vim编辑一个文件。vim保存时默认会创建临时文件再rename覆盖原文件因此即使文件本身是600可写只要父目录不可写保存依然可能失败。遇到文件明明有写权限vim却保存不了的情况先检查父目录权限不要怀疑vim配置。最后恢复实验目录权限准备后续章节使用chmod 700 $LAB/dir-test/inner7. 路径搜索权限、删除判据与特殊权限位前面两章讲清楚了单个目录和文件的行为这一章把视角拉高到路径和特殊标记覆盖实际项目里最常见的那批故障。7.1 路径上每一级目录都需要 x这是权限排查的第一原则访问/a/b/c/file.txt内核需要依次穿过/、/a、/a/b、/a/b/c任何一级缺少x访问就会在这一级失败而且报错信息不会告诉你是哪一级。手工ls -ld逐级看效率很低namei就是为这个场景准备的。构造一个典型的故障现场# 构造路径权限断裂的场景 sudo mkdir -p /opt/perm-demo/conf sudo sh -c echo key: value /opt/perm-demo/conf/app.yml sudo chmod 755 /opt sudo chmod 700 /opt/perm-demo # 关键中间某级缺少 x sudo chmod 755 /opt/perm-demo/conf sudo chmod 644 /opt/perm-demo/conf/app.yml # 用普通用户访问 cat /opt/perm-demo/conf/app.yml ls -l /opt/perm-demo/conf/app.yml sudo -u nobody cat /opt/perm-demo/conf/app.ymlcat会报Permission denied但ls -l /opt/perm-demo/conf/app.yml同样报错看不出问题在哪一级。此时用namei一次性看清楚# 逐级展示路径上每一级目录的权限 namei -l /opt/perm-demo/conf/app.yml输出形如fstring: /opt/perm-demo/conf/app.yml drwxr-xr-x root root / drwxr-xr-x root root opt drwx------ root root perm-demo drwxr-xr-x root root conf -rw-r--r-- root root app.yml一眼就能看出是perm-demo这一级的drwx------挡住了路径。namei -l是排查Permission denied时最应该先想到的命令比ls逐级试错快得多。这里还有一个值得记住的细节路径解析看的是每一级祖先目录的x但只要成功cd进去后续用相对路径访问当前目录下的文件就不再需要祖先目录的x了。举例来说cd /opt/perm-demo失败时你进不去但如果通过某种方式已经处在那个目录里比如进程的工作目录就是它访问./conf/app.yml只需要当前目录的x。这解释了为什么某些服务手动cd进去能跑用绝对路径启动就失败。排查完成后清理现场sudo chmod 755 /opt/perm-demo sudo rm -rf /opt/perm-demo7.2 特殊权限位SUID、SGID、Sticky除了三组rwx还有三位特殊权限写在八进制的最高位。八进制前缀名称作用对象含义典型示例4SUID可执行文件执行时以文件属主身份运行/usr/bin/passwd常为47552SGID可执行文件执行时以文件属组身份运行部分系统工具2SGID目录新建文件继承目录的属组新建子目录继承 SGID 位团队协作目录27701Sticky目录只有文件属主、目录属主或 root 能删除条目/tmp常为1777SUID 位在目录上没有意义Linux 会忽略它。这是很多教程写错的地方。SUID 的安全风险很高因为它等于把提权能力固化到文件上生产环境里应该定期审计。SGID 用在目录上是团队协作的标配。做法是把目录的属组设成项目组权限设成2770这样任何人在里面创建的文件和子目录都会自动属于这个组而不是创建者自己的私有组。没有 SGID 的话A 创建的文件属组是 A 的私有组B 就算在项目组里也访问不了协作就会崩掉。Sticky 位最典型的应用是/tmp。它有1777权限所有人可写但因为带t位谁也不能删别人的文件。没有 sticky 的全局可写目录是非常危险的配置任何用户都能删除其他人的文件甚至是其他服务的运行时文件。验证一下# 验证目录上的 SGID 继承行为 cd $LAB rm -rf sgid-test mkdir -p sgid-test sudo chgrp nogroup sgid-test 2/dev/null || sudo chgrp bin sgid-test chmod 2775 sgid-test ls -ld sgid-test touch sgid-test/a.txt mkdir -p sgid-test/sub ls -ld sgid-test/a.txt sgid-test/sub预期sgid-test的权限显示为drwxrwsr-xs在小节位置新建的a.txt和sub的属组都继承为目录属组。清理用rm -rf sgid-test。# 验证 Sticky 位行为需要两个用户示意命令 sudo mkdir -p /tmp/sticky-demo sudo chmod 1777 /tmp/sticky-demo ls -ld /tmp/sticky-demo权限显示为drwxrwxrwt末尾的t就是 sticky。实际的多用户删除验证需要在两个普通用户之间进行原理同上非文件属主删除时会收到Operation not permitted。7.3 ACL在不改变三组权限位的前提下做细粒度授权传统rwx只能表达三种主体遇到给某个特定用户单独开权限就无能为力了。典型场景是/data/project属主是app组是dev权限750现在临时需要让alice读写但alice不在dev组里又不想改文件属主。用 ACL 解决# 查看并设置 ACL sudo mkdir -p /data/project 2/dev/null || mkdir -p $LAB/acl-demo cd $LAB mkdir -p acl-demo printf project data\n acl-demo/data.txt chmod 750 acl-demo # 给指定用户授予读和执行目录需要 x 才能进入 setfacl -m u:alice:rx acl-demo # 查看结果 getfacl acl-demo ls -ld acl-demogetfacl的输出会多出user:alice:r-x这一行同时会显示一个mask::r-x。ls -ld的权限位末尾会出现号表示存在扩展 ACL。这个号是很重要的信号排查权限问题时看到它就必须用getfacl而不是ls -l来判断真实权限。ACL 的mask是一个常见的坑。它相当于ACL 条目的权限上限所有具名用户、具名组和属组的有效权限都会被mask与运算。如果设置了user:alice:rwx但mask是r-xalice实际只能读和执行。getfacl输出里会标注effective:来提示这个情况。调整方式是setfacl -m m::rwx acl-demo或者用setfacl -n跳过mask重算。目录场景还需要默认 ACL让新建文件自动继承权限# 设置默认 ACL只影响此后在目录中新建的条目 setfacl -d -m u:alice:rwx acl-demo getfacl acl-demodefault:开头的条目就是默认 ACL它不改变目录本身的访问权限只决定新建条目的初始 ACL。这个机制在共享工作目录里非常好用比反复执行chmod -R可靠得多。8. 常见问题与排查清单把前面几章的现象汇总成一张排查表遇到问题时按顺序对照。问题现象可能原因排查方式解决方案ls能列名ls -l全是?目录缺少x权限ls -ld 目录chmod x 目录或设为755cd成功ls报Permission denied目录缺少r权限ls -ld 目录chmod r 目录注意同时保留x文件是644却读不了路径上某级祖先目录缺xnamei -l 绝对路径补齐该级目录的x文件是777却删不掉父目录缺w或xls -ld 父目录给父目录加wx只读文件被意外删除父目录可写删除不看文件权限ls -ld 父目录收紧父目录w或加 sticky 位vim编辑可写文件却保存失败父目录不可写临时文件无法renamels -ld 父目录给父目录w或调整vim备份策略Shell 脚本755却执行报错挂载点带noexecfindmnt -no OPTIONS 挂载点换挂载点或去掉noexec脚本111执行失败解释器需要读文件缺rls -l 脚本chmod 755 脚本root也执行不了某个文件三组权限位都没有xls -l 文件至少给一组x用户在组里却没组权限用户同时是属主只匹配属主位id、ls -l调整属主位或用 ACL 授权加组后权限仍不生效当前会话未加载新组id重新登录或newgrp 组名新建文件权限总是640进程或systemd的umask为027umask、systemctl show 服务 -p UMask调整umask或服务UMask目录权限改了但仍报错存在扩展 ACLls权限位不是全貌getfacl 目录看末尾号用setfacl调整注意mask容器内写挂载目录失败容器内UID与宿主机文件属主不匹配容器内id宿主机ls -n指定运行用户或调整属主改了权限立即失效上层目录或挂载选项覆盖findmnt、namei -l从挂载和路径层面排查这张表按现象 → 原因 → 命令 → 处置组织排查时从上往下扫一遍基本能命中。有一个通用建议遇到权限问题先跑namei -l再跑getfacl最后才ls -l。这个顺序能覆盖路径断裂、ACL 覆盖和基础权限三类原因比凭直觉chmod 777有效得多。9. 生产环境最佳实践与工程建议权限配置在测试环境里错了顶多重来一次在生产环境里错了可能是服务不可用或者数据泄露。这一章给出可以直接落到规范里的建议。9.1 基础权限基线从一组稳定的默认值开始能省掉大量临时决策。常规文件用644常规目录用755这是绝大多数 Web 应用和静态资源的标准配置属主可写组和其他人只读目录保留遍历能力。私钥、证书、配置文件里的敏感项用600.ssh目录用700authorized_keys用600这是 SSH 的硬性要求权限过宽会被直接拒绝登录。团队共享目录用2770靠 SGID 保证新建文件继承项目组避免出现A 建的文件 B 改不了的协作故障。共享的临时交换目录用1777靠 sticky 位防止互相误删。最需要明确的一条是禁止使用777解决问题。777意味着任何本地用户都能改写甚至替换文件内容在 Web 目录里等同于给了上传执行脚本的能力。如果一时想不出该给什么权限说明还没搞清楚运行服务的用户身份——这本身就是要先解决的问题而不是用777掩盖过去。9.2 最小权限与服务专用账号服务不要用root跑为每个服务建独立系统账号只授予它确实需要的目录权限。判断依据很具体服务需要读配置就给配置目录的r-x需要写日志和上传目录就给那两个目录的rwx其余一律不给。这样即使服务被攻破攻击面也被限制在几个目录里。授权时优先用属组而不是所有人。把服务账号加入对应业务组目录设成750或2770比开o位安全得多。如果确实需要给某个不在组内的账号授权用 ACL 精确到人比大范围放宽o位可控。9.3 chmod -R 的正确用法与风险chmod -R是生产事故的高发点。原因很简单目录和文件的正确权限本来就不同一条-R命令只能给一个值。执行chmod -R 644 /data/app会把所有子目录的执行位抹掉目录瞬间无法进入应用直接挂掉而且因为目录不可访问连恢复都要先修好父目录。正确做法是按类型分别处理# 目录统一 755文件统一 644分别处理 find /data/app -type d -exec chmod 755 {} find /data/app -type f -exec chmod 644 {} -exec ... {} 比-exec ... {} \;效率高因为它会批量传参而不是每个文件启动一次进程。如果目录里有需要保留执行位的脚本再加上第三条命令单独处理# 单独给脚本保留执行位 find /data/app -type f -name *.sh -exec chmod 755 {} 顺序很重要先设目录再设文件最后处理脚本特例。反过来的话前面的命令会把后面的结果覆盖掉。9.4 变更前备份权限变更后可回滚调整生产权限之前先把当前状态存档。getfacl -R可以把整棵目录树的权限和 ACL 完整导出setfacl --restore可以原样恢复# 变更前备份权限包含 ACL getfacl -R -p /data/app /root/app-perms-$(date %Y%m%d-%H%M%S).acl # 变更操作 find /data/app -type d -exec chmod 755 {} # 如果出问题从备份恢复 setfacl --restore/root/app-perms-20240101-120000.acl注意备份文件本身要放在安全位置权限设为600因为它会暴露完整的目录结构和权限配置属于敏感信息。备份和恢复都应该先在测试环境跑一遍确认命令行为符合预期再上生产。9.5 定期审计高危权限有两类权限配置值得定期扫一遍。第一类是全局可写且没有 sticky 位的目录# 查找全局可写且无 sticky 位的目录 find / -xdev -type d -perm -0002 ! -perm -1000 -ls 2/dev/null第二类是带 SUID 或 SGID 的可执行文件# 查找 SUID 文件 find / -xdev -perm -4000 -type f -ls 2/dev/null # 查找 SGID 文件 find / -xdev -perm -2000 -type f -ls 2/dev/null-xdev的作用是不跨文件系统避免扫到挂载的网络存储或容器文件系统导致耗时过长。这两条命令的输出应该和系统基线做对比发现新增项要确认来源这是入侵检测里很基础也很有效的一环。9.6 容器与编排环境的权限注意点容器场景下最常见的问题是UID不匹配。宿主机上的数据卷属主是UID 1000容器里进程以UID 100运行即使宿主机文件是777也可能因为其他机制失败而如果是755则一定写不进去。排查方式是两边对比数字# 宿主机查看数字 UID/GID ls -n /data/app # 容器内查看当前用户 docker exec -it 容器名 id解决方式是让两边UID对齐或者用编排平台提供的用户指定能力。Kubernetes 里可以用securityContext的fsGroup让挂载卷自动调整属组这比在宿主机上反复chmod可靠。另一个注意点是readOnlyRootFilesystem。开启之后容器根文件系统只读任何试图在根目录写文件的行为都会失败必须在emptyDir或数据卷上写。这不是rwx位的问题但报错同样是Permission denied排查时容易混淆。9.7 小结回到本文的出发点。Linux 权限管理之所以让很多人头疼不是因为规则复杂而是因为规则不统一文件和目录共用rwx三个字母含义却完全不同。把这一点想透之后剩下的都是推论。文件看自己的三位目录看自己的三位加父目录的三位路径看每一级祖先的x删除看父目录的wx特殊场景再看 SUID、SGID、Sticky 和 ACL。如果要给出一套最省事的操作习惯大概是这样的遇到Permission denied先跑namei -l定位路径断点再跑getfacl看有没有 ACL 覆盖最后才去ls -l核对基础权限位调整权限时按目录和文件分开处理绝不用一条chmod -R通吃变更前导出getfacl备份变更后立刻验证服务可用性。这三条做到权限相关的线上故障能减少一大半。下一步值得深入的方向有三个一是SELinux和AppArmor它们是在rwx之外的第二层访问控制很多权限明明对但还是被拒绝的问题最终都归结到这里二是capabilities它把root的特权拆成了几十个细粒度能力是容器安全的基础三是文件系统层面的挂载选项noexec、nosuid、ro这些设置在容器和云环境中出现频率很高值得单独花时间搞清楚。把这三块补上Linux 权限这块基本就没有盲区了。