☰
Linux rwx权限详解:文件与目录语义及共享目录实战
2026/10/24 5:46:00 网站建设 项目流程

在日常运维和开发中,「这个文件明明是我的,为什么删不掉」「ls 能看见文件,cat 却报 Permission denied」「目录给了 r 为什么还是进不去」这三类问题出现频率极高。它们的答案都指向同一个知识点:Linux 的 rwx 权限,对文件和对目录,是完全两套语义。本文围绕 rwx 在文件和目录上的真实作用展开,从权限位解析、逐位实测,到一个多用户共享目录的完整搭建案例,并附上高频报错对照表和工程化建议。无论你是刚接触 Linux 的学生,还是需要给团队配置共享目录的后端或运维同学,都可以直接照着复现。

1. 从几个「反直觉」的现象说起

1.1 三个最常见的权限困惑

先把几个真实场景摆出来,它们看起来互相矛盾,但答案其实是统一的。

场景一:用户 alice 对一个文件拥有rw-权限,但执行rm时被拒绝,提示rm: cannot remove 'xxx': Permission denied。

场景二:某个目录权限是r--,ls能看到里面的文件名,但ls -l全部显示成-????????? ? ? ? ? ? file.txt,而且cd进不去。

场景三:一个脚本deploy.sh权限是-rw-r--r--,执行./deploy.sh报bash: ./deploy.sh: Permission denied;改成chmod +x之后能跑了,但同事把文件复制到另一台机器,又报同样的错误。

这三个现象不是 Linux 有 bug,而是很多人只记住了「r 是读、w 是写、x 是执行」这句口诀,却没有区分「作用于谁」。口诀对文件基本成立,对目录则完全不成立。目录的 rwx 描述的是「能不能看到目录里有哪些条目」「能不能穿过这个目录」「能不能增删目录里的条目」,跟「读取目录文件内容」没有关系。

理解了这一点,上面三个问题就都有了解释:删除文件真正校验的是父目录的写权限,而不是文件自身的写权限;目录只有 r 没有 x 时,只能读到「名字列表」却无法访问任何条目的元数据;脚本复制后丢失执行位,是因为cp默认不带-p时不保留权限。

1.2 权限模型的三要素:身份、权限位、对象类型

Linux 的权限模型(DAC,自主访问控制)可以用一句话概括:一个主体(进程的有效 uid/gid)对某个客体(文件或目录)能做什么,由客体上记录的三组权限位决定。

拆开来看包含三个要素:

  • 身份分组:每个文件记录一个属主(owner/u)、一个属组(group/g),其余所有人归为 others(o)。内核判定时按 u → g → o 的顺序,命中即停,不会「取并集」。
  • 权限位:每组身份对应三位,分别是 r(读)、w(写)、x(执行/进入)。三组共 9 位。
  • 对象类型:文件、目录、符号链接、设备文件、套接字等。同样的 rwx 位,在不同对象类型上含义不同,本文的重点就是文件和目录这两种。

顺带说明「命中即停」的重要性。假设文件属主是 alice、属组是 devteam、权限是-rw-rw----。alice 属于 devteam,但她访问时内核先用 owner 位判定,得到rw-,就直接结束,不会因为组权限也是rw-而叠加。反过来,如果文件属主是 root、属组是 devteam、权限-rw-r-----,alice 属于 devteam,那么她拿到的是组权限r--,只能读不能写。这个顺序是很多「我明明在组里为什么没权限」问题的根因。

1.3 为什么必须区分文件与目录

用一个类比帮助记忆:

  • 文件是「一本书」,r 是能不能翻开看,w 是能不能在书上写字,x 是这本书能不能当成「程序」来运行。
  • 目录是「一个书架」,r 是能不能看到书架上贴的书名标签,x 是能不能走到书架前伸手取书,w 是能不能往书架上放书、把书拿下来扔掉、或者把标签换掉。

关键差别在于:书架上有什么书(目录里的条目列表)和书本身能不能读(文件内容)是两件事。你可能有权限看到书架上有《A 书》,但没有权限翻开它;也可能知道《A 书》在哪,却因为没有 x 权限而根本走不到书架前。

所以正确的记忆方式不是背 9 个字母,而是记住「目录的权限管的是条目操作,文件的权限管的是内容操作」这条主线。后文所有细节都是这条主线的推论。

2. 环境准备与权限查看工具

2.1 实验环境说明

本文的命令在主流 Linux 发行版上均可复现,包括 CentOS/RHEL 系、Ubuntu/Debian 系以及各类容器镜像。不同发行版在默认umask、默认用户组策略、sudo配置上有差异,具体数值请以你自己的环境为准;本文示例以常见的 root 可切换环境、默认umask 0022为例,重点演示权限语义,而不是某个发行版的特定行为。

实验前建议确认几件事:

  • 你有一个可以sudo或su -的账号,因为创建用户、用户组、修改他人文件属主都需要特权。
  • 有至少两个普通测试用户,用于验证「同组」「不同组」「其他人」三种身份。可以新建,也可以用现有账号。
  • 所有实验目录建议放在/srv或/opt下自建的子目录,不要拿系统目录做实验。涉及删除和权限变更的操作,务必先在测试环境或虚拟机、容器中完成验证,不要直接在生产机上试。

准备两个测试用户和一个测试组,命令如下:

# 以下命令需要 root 或 sudo 权限 # 创建测试组 groupadd devteam # 创建两个测试用户,并加入 devteam 组 useradd -m -s /bin/bash alice useradd -m -s /bin/bash bob usermod -aG devteam alice usermod -aG devteam bob # 再加一个不在组内的用户,用于验证 others useradd -m -s /bin/bash carol # 确认结果 id alice id bob id carol

预期输出类似:

uid=1001(alice) gid=1001(alice) groups=1001(alice),1002(devteam) uid=1002(bob) gid=1002(bob) groups=1002(bob),1002(devteam) uid=1003(carol) gid=1003(carol) groups=1003(carol)

这里需要注意:usermod -aG中的-a表示追加,漏掉-a会把用户从其它附加组里移除,这是一个非常典型的生产事故来源。改完组之后,用户已经登录的会话不会立即生效,需要重新登录或使用newgrp devteam刷新。

2.2 ls -l 的输出逐位拆解

看权限最常用的命令是ls -l(看目录自身用ls -ld)。以一行典型输出为例:

-rwxr-xr-- 1 alice devteam 1024 Jan 1 10:00 deploy.sh

从左到右逐段解释:

  • 第 1 个字符-:对象类型。-普通文件,d目录,l符号链接,c字符设备,b块设备,s套接字,p命名管道。
  • 第 2–4 位rwx:属主(alice)的权限。
  • 第 5–7 位r-x:属组(devteam)的权限。
  • 第 8–10 位r--:其他人(others)的权限。
  • 后面的1是硬链接数(对目录而言是子目录数量相关值),接着是属主、属组、大小、时间、文件名。

如果第 10 位不是-而是s、S、t、T,说明启用了 SUID、SGID 或 Sticky 位。这些属于特殊权限位,本文第 5 节会简单涉及,重点仍放在 rwx 上。

还有一种常见「异常」输出:

-????????? ? ? ? ? ? file.txt

问号不是权限位真的坏了,而是ls -l需要读取文件的 inode 元数据才能显示属主、属组、大小,而当前用户对父目录缺少 x 权限,stat()调用失败,于是只能把未知字段打印成问号。这个现象本身就是「目录 r 与 x 分离」的直接证据,第 4 节会专门实测。

2.3 用 stat 与 namei 精确定位权限问题

ls -l适合快速扫一眼,排查问题时更推荐stat和namei。

stat /srv/project/shared

stat会以八进制和符号两种形式输出权限,还会给出 inode、链接数、三个时间戳等信息,非常适合写进排查记录:

File: /srv/project/shared Size: 4096 Blocks: 8 IO Block: 4096 directory Device: fd00h/64768d Inode: 131073 Links: 2 Access: (2775/drwxrwsr-x) Uid: ( 0/ root) Gid: ( 1002/devteam) Access: 2025-01-01 10:00:00.000000000 +0800 Modify: 2025-01-01 10:00:00.000000000 +0800 Change: 2025-01-01 10:00:00.000000000 +0800

namei则专门解决「路径上每一级目录的权限分别是什么」这个高频问题:

namei -l /srv/project/shared/deploy.sh

输出会逐级列出每一层目录的属主、属组和权限位。当出现「上级目录少一个 x 导致整条路径走不通」时,namei -l几乎是最快的定位手段,比手工一层层ls -ld高效得多。

2.4 chmod、chown、umask 三个基础工具

修改权限用chmod,它支持符号法和数字法两种写法。

符号法适合「只调整某几位」:

chmod u+x deploy.sh # 给属主加执行位 chmod g-w shared # 去掉属组的写权限 chmod o=r deploy.sh # 其他人设为只读(覆盖式赋值) chmod a+r deploy.sh # 所有人加读权限,a = u+g+o chmod u=rwx,g=rx,o= deploy.sh # 精确赋值,others 无任何权限

数字法适合「整体设定」:

chmod 644 deploy.sh # rw-r--r-- chmod 755 deploy.sh # rwxr-xr-x chmod 600 private.key # rw------- chmod 750 shared # rwxr-x---

数字的算法是把 r=4、w=2、x=1 相加。7=4+2+1,6=4+2,5=4+1,4=4,0=0。九位权限就是三组数字依次排列。

chown改属主和属组,chgrp只改属组:

chown alice deploy.sh # 只改属主 chown alice:devteam deploy.sh # 同时改属主和属组 chown :devteam deploy.sh # 只改属组 chown -R alice:devteam /srv/project/shared # 递归,谨慎使用

umask决定新建文件和目录的「初始权限」。真实权限 = 默认最大权限 减去 umask。文件最大是 666(一般不给新建文件执行位),目录最大是 777。

umask # 0022 touch demo.txt && ls -l demo.txt # -rw-r--r-- 1 alice alice 0 Jan 1 10:00 demo.txt mkdir demo_dir && ls -ld demo_dir # drwxr-xr-x 2 alice alice 4096 Jan 1 10:00 demo_dir

把umask设为0027,新建文件会变成640、目录变成750,这就是很多安全基线的推荐值。需要注意的是,umask是进程属性,只影响当前 shell 及其子进程,想长期生效要写进/etc/profile、/etc/bashrc或用户自己的~/.bashrc。

3. rwx 对文件的作用

3.1 r:读取文件内容

对普通文件来说,r 权限表示「允许读取文件内容」。它覆盖的操作包括cat、less、head、tail、grep、od等读操作,也包括作为cp的源文件被复制。

# 准备一个测试文件 echo "hello linux permission" > /srv/demo.txt chmod 400 /srv/demo.txt ls -l /srv/demo.txt # -r-------- 1 root root 21 Jan 1 10:00 /srv/demo.txt cat /srv/demo.txt # hello linux permission

注意,即使只有 r 没有 w,文件内容依然可以被读取。这一点在配置私钥、证书、含敏感信息的配置文件时很重要:chmod 400表示「只有属主能读,谁都不能改」,比600更严格。

反过来,如果没有 r,即使有 w,也会出现「能覆盖写但不能读」的奇怪状态。此时用echo "x" >> file追加是可行的,但cat file会被拒绝。实践中几乎不会主动配置这种权限,但排查「文件被清空却看不到原内容」时,可以留意这一点。

3.2 w:修改文件内容

w 权限控制的是「修改文件内容」,包括追加、截断重写、原地修改。典型操作是echo >>、vim保存、sed -i、truncate等。

chmod 600 /srv/demo.txt echo "second line" >> /srv/demo.txt cat /srv/demo.txt # hello linux permission # second line chmod 400 /srv/demo.txt echo "third line" >> /srv/demo.txt # -bash: /srv/demo.txt: Permission denied

这里有两个容易混淆的点需要澄清。

第一,w 权限不包含「读」。很多人以为「能写就能读」,其实不是。vim file打开一个只有 w 没有 r 的文件时会报错,因为编辑器需要先读取内容到缓冲区。所以实际使用中,可编辑的文件通常同时具备 r 和 w(即6)。

第二,文件权限不等于能否删除文件。这是本文最核心的结论之一,下一小节专门讲。

3.3 x:执行文件

x 权限对文件意味着「把它当作程序来执行」。它适用于两类文件:编译好的二进制程序,以及带 shebang 的脚本。

对二进制可执行文件,内核加载器只需要 x 权限,不需要 r 权限。你可以验证这一点:/bin/ls的权限通常是-rwxr-xr-x,把 r 从 others 拿掉后,普通用户依然可以执行ls(当然这属于危险操作,只在测试机上验证):

# 危险操作,仅在测试环境验证,验证后务必改回 ls -l /bin/ls # -rwxr-xr-x 1 root root 138208 ... /bin/ls chmod o-x /bin/ls ls # -bash: /bin/ls: Permission denied chmod o+x /bin/ls

对脚本则不同。脚本本身不是一个可以被内核直接加载的程序,它的执行流程是:内核看到文件头部的#!(shebang),转交给对应的解释器,比如/bin/bash,由解释器以读取文件的方式加载脚本文本再逐行解释。因此脚本执行实际上需要两项权限:内核层面的 x,以及解释器层面读取脚本所需的 r。

实践中这意味着,把脚本权限设成--x是不安全的做法,虽然某些场景下能跑起来,但由于解释器读不到内容,通常会报错。推荐脚本统一用755或750。

3.4 脚本「能跑了又跑不了」的两种典型原因

第一种原因是复制过程丢失权限。cp在不加-p时不保留权限位,新文件按当前umask生成:

ls -l deploy.sh # -rwxr-xr-x 1 alice devteam 120 Jan 1 10:00 deploy.sh cp deploy.sh /tmp/deploy_copy.sh ls -l /tmp/deploy_copy.sh # -rw-r--r-- 1 alice alice 120 Jan 1 10:00 /tmp/deploy_copy.sh

正确的做法是cp -p deploy.sh /tmp/(保留权限与时间戳),整体迁移目录时用cp -a或rsync -a。用scp传文件到另一台机器同样会因为目标端umask而丢失执行位,这是「本地能跑,服务器上跑不了」的高频原因。

第二种原因是挂载选项。哪怕权限位是755,如果所在文件系统是以noexec挂载的,执行时会直接报Permission denied。/tmp、/data之类的分区在安全加固后经常被设成noexec。可以这样确认:

findmnt -no OPTIONS /tmp # rw,nosuid,nodev,noexec,relatime

如果输出里包含noexec,那么该目录下的所有可执行文件都无法直接运行,需要向管理员申请调整挂载策略,而不是去改chmod。

3.5 关键结论:能否删除一个文件,看的是父目录

这是必须单独强调的一条。文件自身的 w 权限管的是「文件内容能不能改」,删除、重命名、移动一个文件,本质上是修改它所在的目录,因此内核校验的是父目录的 w 和 x,与文件自身权限无关。

理论上的解释是:目录本质上是一张「名字 → inode 编号」的映射表,删除文件就是从这张表里删掉一行,所以这是对目录的写操作。这也能解释为什么rm一个444的文件可能成功,而rm一个777的文件却可能失败——真正起作用的是目录权限。

下一节会用完整实测来验证这个结论。

4. rwx 对目录的作用(重点)

4.1 r:能不能看到目录里有哪些条目

目录的 r 权限允许「列出目录中的条目名称」,对应操作是ls 目录、find 目录 -maxdepth 1、shell 的 Tab 补全。它只保证你能拿到名字列表,不保证你能拿到条目的属性,也不保证你能访问条目。

mkdir -p /srv/lab/ronly echo "content" > /srv/lab/ronly/a.txt chmod 444 /srv/lab/ronly ls /srv/lab/ronly # a.txt

能列出名字。但此时访问条目会失败:

cat /srv/lab/ronly/a.txt # cat: /srv/lab/ronly/a.txt: Permission denied cd /srv/lab/ronly # -bash: cd: /srv/lab/ronly: Permission denied ls -l /srv/lab/ronly # ls: cannot access '/srv/lab/ronly/a.txt': Permission denied # total 0 # -????????? ? ? ? ? ? a.txt

原因就是目录缺少 x。ls能读到目录的条目列表,但cat、cd、stat都需要在内核里「解析路径」,而路径解析要求路径上每一级目录都有 x 权限。

4.2 x:能不能「穿过」这个目录

目录的 x 权限(也常被叫做「搜索权限」)表示「允许把该目录作为路径的一部分进行穿越」。它带来三种能力:

  • cd进入该目录。
  • 访问该目录下已知名字的文件(前提是文件自身权限允许)。
  • 对已知路径的文件执行stat、读、写。

注意这里的关键限定词是「已知名字」。有 x 无 r 时,你无法列举目录内容,但如果别人告诉你确切文件名,你可以直接访问。

mkdir -p /srv/lab/xonly echo "secret" > /srv/lab/xonly/b.txt chmod 111 /srv/lab/xonly ls /srv/lab/xonly # ls: cannot open directory '/srv/lab/xonly': Permission denied cat /srv/lab/xonly/b.txt # secret

这个「知道路径就能进」的特性在安全上是需要注意的:chmod 111的目录并不等于「目录内容被保护」,只等于「目录内容不可枚举」。如果文件名可以被猜测(比如config.ini、backup.tar.gz),那么内容依然是暴露的。真正的保护需要同时去掉 x,或者依赖上层应用鉴权。所以生产环境中目录权限不能只设 x,这在权限基线上是一条硬要求。

4.3 w:能不能在目录里增删条目

目录的 w 权限允许修改目录这张「名字表」,具体包括:

  • 创建新文件或子目录(touch、mkdir、cp到该目录、>重定向新建)。
  • 删除已有条目(rm、rmdir)。
  • 重命名(mv同目录内改名)。
  • 移动条目进出该目录(同文件系统内的mv)。
mkdir -p /srv/lab/wdir chmod 777 /srv/lab/wdir # 任意用户都可以在这里创建文件 touch /srv/lab/wdir/newfile.txt ls -l /srv/lab/wdir # -rw-r--r-- 1 alice alice 0 Jan 1 10:00 newfile.txt

有一个非常重要的细节:目录的 w 必须搭配 x 才有意义。内核在创建、删除条目时需要解析路径,而解析路径依赖 x。只给w(即-w-,数字2)的目录,实际操作几乎全部失败,这是理论上的「死权限」。

实测一下:

mkdir -p /srv/lab/wonly chmod 222 /srv/lab/wonly touch /srv/lab/wonly/test.txt # touch: cannot touch '/srv/lab/wonly/test.txt': Permission denied

4.4 八种目录权限组合对照表

把目录的三种 rwx 位按是否开启组合起来共 8 种,实际效果如下表(以ls、cd、cat 已知文件、touch 新文件、rm 已知文件五类操作为例):

权限位(u)数字ls 列举cd 进入cat 已知文件touch 新建rm 删除已有
---0失败失败失败失败失败
--x1失败成功成功失败失败
-w-2失败失败失败失败失败
-wx3失败成功成功成功成功
r--4成功失败失败失败失败
r-x5成功成功成功失败失败
rw-6成功失败失败失败失败
rwx7成功成功成功成功成功

从表里能读出三条结论:

  1. x 是「一切访问」的前置条件。缺 x 时,哪怕有 r 有 w,也无法进入目录、无法访问任何条目、无法创建文件。rw-(6)看起来权限很高,实际是个废权限。
  2. r 只影响「列举」,不影响「访问」。--x(1)能进能读写已知文件,但不能ls。
  3. w 必须与 x 组合才能创建和删除。这是「-wx可以增删但看不到名字」这种看似矛盾却真实存在的状态。

补充一个常见误区:rm -rf 目录能否成功,取决于目录自身及其各级子目录是否有 w+x,以及路径上每一级父目录是否有 x。逐层递归的删除过程中任何一层缺 w 都会中断。这也是「rm -rf报 Permission denied 却只报了一堆子路径」的原因。

4.5 一个容易踩的坑:编辑器的保存策略

常规认知是「文件没有 w 权限就改不了」。用>或>>重定向时这个结论成立,因为 shell 会以写模式打开文件:

chmod 444 /srv/demo.txt echo "x" > /srv/demo.txt # -bash: /srv/demo.txt: Permission denied

但部分编辑器保存文件时并不是原地写入,而是「写一个临时文件,然后 rename 覆盖原文件」。rename 是对目录的操作,只需要目录有 w+x,不需要原文件有 w 权限。所以在某些配置下,目录可写而文件444时,编辑器仍可能保存成功。

这个行为在不同编辑器、不同版本、不同配置下表现不一致,不必当作定论去记,但它给了我们两个实用启示:

  • 判断「这个文件到底能不能被改」,不能只看文件权限,还要看父目录的权限,以及所属用户能否通过其它路径(例如同组的可写目录 + rename)绕过。
  • 需要真正保护关键文件时,除了设chmod 400,还应确保其所在目录不可被非特权用户写入,否则保护是不完整的。

5. 完整实战:搭建多用户共享目录

5.1 场景与目标

需求描述:团队devteam(成员 alice、bob)需要一个共享工作目录,同时还需要三种不同粒度的子目录:

  • shared/:组内成员可以自由读写、互相新增文件。
  • readonly/:只有 admin(root)能写,组内成员只读。
  • dropbox/:组内成员可以上传文件,但不能查看或删除别人的文件,适合「提交材料」场景。

目标目录结构:

/srv/project/ ├── shared/ ├── readonly/ └── dropbox/

这个案例覆盖了目录 w 与 x 的组合、SGID 对新建文件属组的影响、Sticky 位对删除的限制,非常适合作为 rwx 语义的综合练习。所有操作请在测试环境完成,生产环境的权限变更需要走变更流程并做好备份与回滚准备。

5.2 创建目录结构并设置属主属组

# 创建顶层目录 mkdir -p /srv/project/{shared,readonly,dropbox} # 顶层目录归 root:devteam,组可进入但不可写 chown root:devteam /srv/project chmod 750 /srv/project # shared:组内可读写,加 SGID 让新建文件继承 devteam 组 chown root:devteam /srv/project/shared chmod 2775 /srv/project/shared # readonly:root 可写,组内只读,others 无权限 chown root:devteam /srv/project/readonly chmod 750 /srv/project/readonly # dropbox:组内可写不可读,加 Sticky 位限制删除他人文件 chown root:devteam /srv/project/dropbox chmod 1730 /srv/project/dropbox

逐条解释这些数字的用意:

  • 顶层750:rwxr-x---。属主 root 全权,devteam 成员可以穿过顶层(有 x),但不能在顶层随意创建删除(无 w),防止有人绕过子目录策略直接在/srv/project下建目录。others 完全进不来。
  • shared的2775:开头的2是 SGID。对目录而言,SGID 的效果是「在该目录下新建的文件和子目录,属组自动继承为目录的属组」,而不是创建者的主组。没有 SGID 时 alice 建的文件属组是 alice,bob 就写不了;有了 SGID,属组统一是 devteam,配合组 w 位才能真正共享。这是共享目录里最容易被忽略的一步。
  • readonly的750:devteam 是r-x,能进去、能读文件,但不能建、不能删。
  • dropbox的1730:1是 Sticky 位(等价于o+t),730表示属主rwx、组-wx、others 无权限。组内成员可以进入和写入(有 w+x),但不能列举目录内容(没有 r),同时 Sticky 位保证只能删除自己创建的文件。

5.3 逐项验证权限效果

验证一:共享目录的读写与属组继承。

# 切到 alice su - alice echo "alice's note" > /srv/project/shared/note_by_alice.txt ls -l /srv/project/shared/note_by_alice.txt # -rw-rw-r-- 1 alice devteam 13 Jan 1 10:00 note_by_alice.txt

注意属组是devteam而不是alice,这就是 SGID 生效的结果。此时 bob 可以覆盖修改这个文件,因为组权限是rw-。

验证二:只读目录的组内限制。

su - bob echo "try write" > /srv/project/readonly/c.txt # -bash: /srv/project/readonly/c.txt: Permission denied

但读取是允许的:

# 先由 root 在 readonly 放一个文件 echo "public doc" > /srv/project/readonly/doc.txt chmod 644 /srv/project/readonly/doc.txt # bob 读取 cat /srv/project/readonly/doc.txt # public doc

验证三:dropbox 的「可写不可读」。

# bob 上传文件 echo "bob's report" > /srv/project/dropbox/report_bob.txt # 成功 ls /srv/project/dropbox # ls: cannot open directory '/srv/project/dropbox': Permission denied # 因为组内没有 r 权限,无法列举 # bob 尝试删除 alice 的文件(先由 alice 放一个) su - alice -c 'echo "alice report" > /srv/project/dropbox/report_alice.txt' su - bob -c 'rm /srv/project/dropbox/report_alice.txt' # rm: cannot remove '/srv/project/dropbox/report_alice.txt': Operation not permitted

最后一条正是 Sticky 位的作用。如果没有1前缀(即权限为730),bob 就能删掉 alice 的文件,因为目录对组有 w+x。

验证四:others 被完全挡住。

su - carol cd /srv/project # -bash: cd: /srv/project: Permission denied

因为顶层目录的 others 位是---。

5.4 用 setfacl 处理「例外的一个人」

真实场景里经常出现「只有 carol 需要读 shared,但不想把她加进 devteam 组」的情况。此时不要为了一个人去改组、改目录权限,ACL 更合适:

# 给 carol 单独授予 shared 目录的 r-x setfacl -m u:carol:rx /srv/project/shared setfacl -R -m u:carol:rx /srv/project/shared # 查看 getfacl /srv/project/shared

getfacl输出里会多出user:carol:r-x一行,同时ls -l的权限位末尾会出现一个+号,表示该对象存在 ACL。ACL 的优先级规则是:先看 ACL 中的具体用户项,再看 ACL 的 mask(有效权限),最后才落到传统的 owner/group/others。排查「ACL 加了为什么还是没权限」时,重点看getfacl里的mask行——mask 会把超出它的权限位「裁掉」。

ACL 属于对传统 rwx 模型的扩展,日常维护成本比单纯的组管理高,建议只在确实需要「点对点授权」时使用,并把getfacl的结果纳入配置记录。

6. 常见问题与排查思路

6.1 高频报错对照表

报错现象常见原因解决思路
cd: Permission denied目标目录或路径上某一级目录缺少 x用namei -l 路径逐级检查,补 x
ls: cannot open directory: Permission denied目录缺少 r补 r;若只需访问已知文件,只给 x 也可支撑
ls -l显示大量?目录有 r 无 x,无法stat条目补 x,通常设为r-x(5)
rm: cannot remove: Permission denied父目录缺 w 或 x检查父目录权限,注意与文件自身权限无关
rm: cannot remove: Operation not permitted目录带 Sticky 位且文件不属于当前用户由属主或 root 删除,或按策略调整 Sticky
touch: cannot touch: Permission denied目录缺 w 或 x目录至少需要-wx(3)才能创建文件
bash: ./x.sh: Permission denied缺 x,或所在分区noexec,或 shebang 路径错误chmod +x;findmnt检查挂载选项;检查首行
bash: ./x.sh: /bin/bash^M: bad interpreter文件是 CRLF 换行dos2unix或sed -i 's/\r$//' x.sh
复制后执行位丢失cp未加-p,或scp受目标umask影响用cp -p、rsync -a,或复制后重新chmod
「我在组里为什么不能写」owner 位优先于 group 位,命中即停用id确认组,用ls -l确认 owner 是谁
刚加的组不生效已登录会话未刷新组成员关系重新登录,或newgrp 组名
改完权限仍不生效存在 ACL,mask 截断了权限getfacl查看 mask 并调整

6.2 一条标准排查顺序

遇到权限类问题,建议按下面顺序走一遍,大多数问题 3 分钟内能定位:

  1. 确认身份:id、groups,确认当前用户的实际 uid 和全部附加组,别凭印象。
  2. 确认对象:ls -ld 目标或stat 目标,拿到属主、属组和权限位。
  3. 确认路径:namei -l 绝对路径,检查每一级父目录是否有 x。
  4. 确认 ACL:getfacl 目标,看是否有额外条目或 mask 限制。
  5. 确认挂载:findmnt -T 目标,看是否ro(只读)或noexec。
  6. 确认特殊位:看第 10 位是否有s/S/t/T,以及文件系统是否nosuid。
  7. 确认 SELinux/AppArmor:getenforce、ls -Z、ausearch -m avc -ts recent。如果权限位完全正确但仍被拒绝,多半是强制访问控制在起作用,此时看/var/log/audit/audit.log比反复chmod有效得多。

第 7 步值得单独提醒:在启用了 SELinux 的系统上,权限位没问题却依然Permission denied是常见现象。此时正确的做法是查审计日志并用restorecon修复上下文,而不是无脑chmod 777——后者既不能解决问题,还会扩大攻击面。

6.3 root 能不能无视权限,边界在哪

root(uid 0)在内核判定中会跳过常规的 DAC 检查,所以 root 可以读任何文件、写任何文件,即使权限位是000。这解释了两个现象:一是「root 删不掉文件」几乎不会发生(除非文件系统只读或存在 immutable 属性);二是 root 也不需要文件的 r 权限就能读取内容。

但 root 也有边界,这几点必须知道:

  • 执行文件仍然需要至少一个 x 位。一个644的编译好的二进制,root 也无法直接执行,会报Permission denied。
  • 只读挂载对 root 同样生效。mount -o remount,ro之后 root 也写不进去。
  • chattr +i设置的不可变属性对 root 有效,需要先chattr -i才能修改。
  • 强制访问控制(SELinux 等)对 root 有约束。

这也是为什么权限设计的原则是「最小权限」而不是「给 root 就行」:root 绕过的能力在事故发生时同样会放大损失。日常操作应使用普通账号 +sudo精确授权,而不是长期以 root 身份工作。

7. 最佳实践与工程建议

7.1 建立默认权限基线

把「常见的权限该是多少」固化成团队基线,能避免大量临时判断。推荐一套通用起点:

对象推荐权限说明
普通文档、配置、源码644属主可改,其他人可读
可执行脚本、二进制755保留执行位,其他人只读执行
仅本人可读写的文件600日志、笔记、个人数据
私钥、证书私钥、凭据文件600或400400更严格,连属主都不能改
普通目录755可进入、可列举、他人不可写
团队目录750组内共享,others 拒绝
共享写目录2775加 SGID 保证属组继承
上传/投放目录1730加 Sticky,配合-wx实现可写不可读

配套设置:把系统默认umask调成027,新文件默认640、新目录默认750,比022更符合安全基线。对敏感服务(数据库、密钥管理),umask 077更合适。

7.2 共享目录必须同时解决「属组继承」和「删除越权」

很多团队配共享目录时只做了chmod 777,短期能用,长期留坑。正确做法是两件事一起做:

  • 用SGID(目录上chmod g+s或数字前缀 2)解决属组继承。否则每个人新建的文件属组都是自己的主组,别人写不了,共享名存实亡。
  • 用Sticky(目录上加o+t或数字前缀 1)限制删除,只允许文件属主、目录属主和 root 删除。
  • 用umask 002让新建文件默认664、目录775,组内可写。

三者组合起来的最小可用共享目录模型:

mkdir -p /srv/team chown root:devteam /srv/team chmod 2775 /srv/team # SGID + 组可读写 setfacl -d -m g:devteam:rwx /srv/team # 默认 ACL,让新子目录也继承组权限 setfacl -d -m o::- /srv/team

setfacl -d设置的是默认 ACL,只对目录有效,作用是让该目录下新建的子文件、子目录自动获得对应 ACL 条目。这一条能解决「共享目录里再建子目录,组权限就丢了」的连锁问题。

7.3 权限变更必须当成一次生产变更

权限是线上故障的常见诱因,chmod -R、chown -R这类递归命令一旦打错一个数字或者多打一个斜杠,影响面可能是整个服务目录。建议把它当变更来管理:

  • 先备份权限:变更前记录关键路径的权限快照,出问题能对照恢复。例如find /srv/project -exec stat -c '%a %U %G %n' {} + > /root/perm_backup_$(date +%F).txt。
  • 先在测试环境验证:至少在一台非生产机器或容器里跑通命令,确认影响范围。
  • 拒绝chmod -R 777:它既解决不了 SELinux、ACL、挂载选项引起的问题,又会把写权限扩散到所有用户,属于典型的「用一个安全问题换一个功能问题」。
  • 使用最小范围:能用chmod改单文件就不加-R;能用组权限解决就不加 others 位。
  • 保留回滚手段:变更脚本里同时写好反向命令,并明确谁有权执行。
  • 事后核对:变更后用ls -l、stat、getfacl抽查几个关键路径,确认和预期一致。

7.4 定期审计与自动化检查

手工检查权限不现实,用find做几类常规扫描会比较省心:

# 查找全局可写的普通文件(潜在被篡改风险) find /srv -xdev -type f -perm -0002 -ls # 查找全局可写的目录,且不带 Sticky 位(风险最高) find /srv -xdev -type d -perm -0002 ! -perm -1000 -ls # 查找 SUID 文件 find / -xdev -type f -perm -4000 -ls 2>/dev/null # 查找无属主或无属组的文件 find /srv -xdev \( -nouser -o -nogroup \) -ls

建议把这类扫描写成定时任务或纳入巡检脚本,输出做基线对比,出现新增项时告警。尤其是「全局可写目录 + 无 Sticky」这一类,往往是提权路径的来源。

7.5 需要避开的几个反模式

  • 依赖 others 位做权限控制。给o+w意味着系统上任何用户都能写,正确做法是建组并把人加进去。
  • 用chmod 777解决「服务启动不了」。根因往往是属主不对、上级目录缺 x、SELinux 上下文错误,777只是掩盖症状。
  • 把密钥文件放在可写目录里。文件本身600也没用,只要目录可被别人写,就能通过 rename 替换。
  • 忽略umask的进程作用域。在脚本里写umask 027只在子 shell 生效,想让服务进程也生效,要写进服务配置或启动脚本。
  • 只检查最终目录,不检查路径。/a/b/c中任何一级缺 x,后面全废,namei -l是必备工具。

8. 收尾:一张速查表

把全文压缩成一张能贴在屏幕边上的对照表:

权限位对文件对目录
r读取文件内容列出目录条目名称
w修改文件内容在目录中创建、删除、重命名条目
x作为程序执行(脚本还需 r)进入目录、穿越、访问已知路径的条目
缺 r不能 cat,但可能能重定向覆盖不能ls,但能cd并访问已知文件
缺 w不能改写内容,但可能被删除不能增删条目,但能进能读
缺 x不能执行(root 也需要至少一个 x 位)一切访问失败,ls -l显示?
删除文件与文件自身权限无关取决于父目录的 w+x
常见取值644/755/600755/750/2775/1730

记住一条主线就够用:文件的 rwx 管内容,目录的 rwx 管条目;目录的 x 是通行证,w 是增删权,r 只是列表权。下次再遇到「明明有权限却操作失败」,先跑一遍id、namei -l、getfacl、findmnt这四条命令,绝大多数的 Permission denied 都会当场现形。

实操建议:把第 5 节的共享目录案例在自己的测试机上完整走一遍,尤其是222和444目录那两组反直觉的组合,亲手复现一次比看十遍文字都记得牢。权限这块内容的最佳学习方式就是动手——毕竟它验证起来只需要一条chmod和一次ls。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询