云计算百科
云计算领域专业知识百科平台

【Linux 修炼录】告别 Permission denied:一文讲透 Linux 权限体系与底层逻辑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

Linux 修炼录:权限

在前几篇文章中,我们先后学习了 Linux 的基本文件操作(pwd、ls、cd、mkdir、touch、rm、cp、mv 等),以及文件内容的查看与定位工具。

在这些基础指令之上,一旦进入真实的多用户环境或服务器运维场景,一个绕不开的核心问题便随之而来:不同的用户在系统中各自能碰哪些文件、不能碰哪些文件?系统又是如何判定并限制这些行为的?

Linux 作为典型的多用户、多任务操作系统,依赖一套严格的权限管理机制来划分用户的操作边界
本篇围绕用户分类、文件类型、权限属性、归属修改、默认掩码(umask)以及防误删的粘滞位(Sticky Bit),结合终端实操,梳理 Linux 权限的底层逻辑与日常用法。


一、用户分类与身份管理

在讨论具体文件的权限之前,首先要弄清操作文件的主体——用户。

Linux 将系统中的用户大体划分为两类:

  • 超级管理员(root):拥有系统的最高控制权,不受普通权限机制的约束,可以创建、修改、删除系统中的绝大多数资源。
  • 普通用户:受权限机制的严格约束,通常只能在自己的家目录(/home/username)以及被授权的目录和文件内进行操作。
  • 普通用户之间的隔离,正是通过权限来规范的。

    1. 新建用户与初始化(Ubuntu)

    在多用户实战测试中,我们通常需要一个辅助账号。以 Ubuntu 系统为例,新建用户可以使用 adduser 命令。如果当前不是 root 用户,命令前需要加上 sudo 提权:

    # root 用户直接执行
    adduser username

    # 普通用户借助 sudo 执行
    sudo adduser username

    以创建名为 vir 的测试用户为例:

    info: Adding user `vir' ...
    info: Selecting UID/GID from range 1000 to 59999 ...
    info: Adding new group `
    vir' (1004) …
    info: Adding new user `vir'
    (1004) with group `vir (1004)' ...
    info: Creating home directory `
    /home/vir' …
    info: Copying files from `/etc/skel'
    ...
    New password: ← 输入密码(终端无回显是正常的)
    Retype new password: ← 再次输入密码
    passwd: password updated successfully
    Changing the user information for vir
    Enter the new value, or press ENTER for the default
    Full Name []: ← 个人信息回车跳过即可
    Room Number []:
    Work Phone []:
    Home Phone []:
    Other []:
    Is the information correct? [Y/n] Y ← 输入 Y 确认
    info: Adding new user `vir' to supplemental / extra groups `users' ...

    用户创建完成后,系统会自动完成以下初始化工作:

    • 为其分配唯一的 UID(用户标识)和 GID(组标识);
    • 创建与用户名同名的初始用户组;
    • 在 /home/ 下创建用户专属的家目录,并从模板目录 /etc/skel 拷贝基础配置文件。

    ⚠️ 终端细节提示:

    • Linux 终端在输入密码时不显示任何字符、星号或光标变动,这是防止旁人窥探密码长度的安全设计,正常输入后直接回车即可;
    • UID 和 GID 通常从 1000(或 CentOS 中的 1000/500)开始分配,1000 以下的编号一般预留给操作系统内置的系统服务账号。

    2. 用户切换:su 与 su –

    在终端中切换用户身份可以使用 su(switch user):

    su username # 切换用户,但保留当前的环境变量与工作目录
    su – username # 切换用户,并同时切换到目标用户的家目录和登录环境


    二、权限管理体系

    1. 识别文件类型

    在终端中执行 ls -l 时,输出结果最左侧的一串字符直接决定了该文件的属性。其中首个字符专门用来标识文件类型:

    类型标识符说明常见场景示例
    普通文件 – 包含数据、文本或二进制指令的文件 .txt、.c、.sh、编译后的可执行文件
    目录 d 包含文件名与对应 inode 映射关系的目录文件 /etc、/home、各类文件夹
    符号链接 l 类似于 Windows 的快捷方式,指向另一个路径 /usr/bin/python -> python3
    块设备 b 提供带缓存、按固定数据块随机读写访问的硬件接口 磁盘设备 /dev/sda、分区 /dev/sda1
    字符设备 c 提供无缓冲、按字符流顺序顺序读写的硬件接口 终端设备 /dev/tty、空设备 /dev/null
    命名管道 p 管道文件,用于本地不同进程之间通信(FIFO) mkfifo 创建的通信文件
    套接字 s 用于网络或本地进程间双向通信的端点 MySQL 套接字 /run/mysqld/mysqld.sock

    在实战环境下观察 ls -l 的回显:

    观察截图中红框标注的首字符:

    • mydir 最开头的字符是 d,说明它是一个目录;
    • my.txt 和 test.c 最开头的字符是 -,说明它们是普通文本/源码文件。

    2. 权限属性与访问者类型

    紧随文件类型字符之后的 9 个字符,即为该文件的访问权限位。

    这 9 个字符固定分为三组,每组 3 个字符,按从左到右的顺序严格对应三类不同的访问者角色:

  • 拥有者(Owner / User,简写 u):文件或目录的创建者或当前所有者;
  • 所属组(Group,简写 g):文件所属的用户组,组内的所有用户共享该组设定的权限;
  • 其他人(Other,简写 o):既不是文件拥有者,也不属于文件所属组的任何其他系统用户。
  • 每组权限由读、写、执行三个基础操作位构成:

    权限符号权限名称八进制权重对普通文件的含义对目录文件的含义
    r 读(Read) 4 允许读取并查看文件内容(如 cat、head) 允许读取目录列表,查看目录内文件名(如 ls)
    w 写(Write) 2 允许修改、覆写或追加文件内容 允许在目录内新建、移动、重命名或删除文件
    x 执行(Execute) 1 允许将该文件作为可执行程序或脚本载入运行 允许进入(cd)该目录,以及访问其内部文件的元数据
    – 无权限 0 在该位置不具备对应操作权限 在该位置不具备对应操作权限

    核心辨析:普通文件与目录的权限差异

    • 目录的 x 权限至关重要:若对目录缺乏 x 权限,哪怕拥有 r 权限,用户也只能勉强看到目录内有哪些文件名,却无法 cd 进去,也无法读取文件的大小、权限等元数据(执行 ls -l 会出现大量问号 ?)。
    • 删除文件的权限在目录,而不在文件自身:由于文件名与其存储位置记录在父目录的映射表中,因此“能否删除一个文件”,完全取决于用户对该文件所在父目录是否具有 w 权限,与该文件自身是否只读无关。

    在权限位右侧,ls -l 还会依次列出:
    硬链接数、文件拥有者、文件所属组、文件大小、最后修改时间、文件名。

    因为所有不属于“拥有者”也不属于“所属组”的用户都归为 Other,所以列表项中无需单独指定 Other 的用户名。

    3. 实例解析:访问者的权限命中与拦截

    以测试环境中的文件结构为例:

    观察当前目录 mydir 下三个项目的权限与归属:

    • 目录 lib:权限 drwxrwxr-x,拥有者 lzh,所属组 lzh;
    • 普通文件 log.txt:权限 -rw-rw-r–,拥有者 lzh,所属组 lzh;
    • 目录 sever:权限 drwxrwxr-x,拥有者 lzh,所属组 lzh。

    此时,如果切换到之前新建的普通用户 vir:

    • vir 不是拥有者 lzh;
    • vir 此时也尚未加入 lzh 组;
    • 因此系统在权限匹配时,会判定 vir 属于 Other。

    根据权限位:

    • lib 对 Other 的权限为 r-x(允许浏览、允许 cd 进入,但不允许在其内新建或删除文件);
    • log.txt 对 Other 的权限为 r–(允许读取,禁止修改写入)。

    我们在终端中切换到 vir 进行验证:

    核对终端输出与批注:

  • vir 执行 cd lib/ 并运行 ls -l,成功列出了目录中的两个文件 code.c 和 mystring.h(截图标注“可以进入目录”);
  • 随后 vir 尝试执行删除操作 rm -f code.c,终端立即报错:rm: cannot remove 'code.c': Permission denied(截图标注“没有删除权限”)。
  • 这印证了前述机制:vir 对 lib 只有 r-x,缺少 w 权限,因此无法更改 lib 目录的数据表,删除操作被系统拦截。


    三、权限修改指令

    在掌握了权限的判定规则后,当协同开发或服务部署需要调整访问边界时,可以通过以下三个核心命令完成权限与所有权的变更。

    1. chmod:更改文件或目录权限

    chmod(change mode)用于修改文件或目录的访问权限。

    ⚠️ 操作权限约束:在 Linux 中,**只有文件拥有者(Owner)与超级管理员(root)**才拥有修改文件权限的资格。

    常用选项:

    • -R:递归处理(Recursive),将指定目录下所有的子目录及内部文件一并修改权限。

    修改权限主要有两种表达模式:数字模式与符号模式。

    a. 数字模式(八进制法)

    数字模式将每种角色的权限值进行累加(r=4, w=2, x=1),形成一个 0~7 的数字,三组角色组合为三位数字(ugo):

    chmod [三位八进制数字] 文件或目录

    数字组合对应权限符号典型适用场景
    755 rwxr-xr-x 可执行脚本、系统程序、各级公开访问的目录
    644 rw-r–r– 最常见的普通文本文件、源代码、文档配置(拥有者可编辑,其他人只读)
    600 rw——- 机密配置文件、SSH 私钥文件(仅拥有者可读写,隔绝一切外部访问)
    700 rwx—— 个人私有脚本或私有目录
    777 rwxrwxrwx 开放全部读写执行权限(存在严重安全隐患,生产环境需严格规避)

    b. 符号模式(字符加减法)

    符号模式通过直接指定角色、操作符和权限字符来精细调整:

    chmod [操作对象] [操作符] [权限字符] 文件或目录

    • 操作对象:u(属主)、g(属组)、o(其他人)、a(所有人,all,等价于 ugo)
    • 操作符:+(增加权限)、-(移除权限)、=(赋予精确权限,其余位置重置为无)
    • 权限字符:r、w、x

    chmod u+x script.sh # 仅为属主增加可执行权限
    chmod go-w config.ini # 移除属组和其他人的写权限
    chmod a+r file.txt # 为所有用户添加读权限(等同于 chmod +r file.txt)
    chmod u=rwx,go=rx mydir # 精确设定属主 rwx,属组及其他人 rx

    c. chmod 实战演示

    基于前文 vir 用户被拦截的场景,我们通过拥有者 lzh 给 lib 目录的 Other 角色赋予写权限:

    观察截图中的操作演进:

  • lzh 查看当前 lib 的权限为 drwxrwxr-x(红框标注),Other 只有读和进入权限;
  • 执行 chmod o+w -R lib(红框标注),递归为 Other 添加写权限;
  • 再次查看 ls -l,lib 权限已变为 drwxrwxrwx(红框标注);
  • 切换为 vir 用户后进入 lib,再次执行 rm -f code.c,删除命令顺利通过,code.c 从列表中移除。
  • 紧接着,在具备 wx 权限的 lib 目录中,vir 也可以创建属于自己的新文件:

    截图中 vir 执行 touch vircode.c(红框标注),生成的 vircode.c 其拥有者和所属组自然为 vir:vir(红框标注)。


    2. chown:修改文件拥有者与提权实战

    chown(change owner)用于改变文件或目录的所有者。

    chown [选项] 新拥有者 文件名
    chown [选项] 新拥有者:新所属组 文件名 # 可顺带修改所属组

    ⚠️ 为什么普通用户不能把自己的文件 chown 给别人?
    在 Linux 默认安全策略下,普通用户即使是文件的拥有者,也不能将文件赠送给其他用户。因为一旦允许随意 chown,恶意用户可以占用大量磁盘配额后将文件所有者改成他人转嫁配额,或制造恶意程序栽赃。因此,执行 chown 必须由超级管理员(root)操作,或通过 sudo 提权执行。

    我们观察 vir 用户直接尝试修改所有者时的系统反馈:

    截图中:

  • vir 执行 chown vir mystring.h,系统拒绝:chown: changing ownership of 'mystring.h': Operation not permitted;
  • vir 尝试利用 sudo chown vir mystring.h 提权,系统给出警告:vir is not in the sudoers file。
  • 原因剖析:Ubuntu 默认只有处于 sudo 用户组中的成员才具备提权资质,新创建的 vir 尚未被加入该组。

    解决方案:切换回具备管理员权限的账号 lzh,将 vir 追加到 sudo 组中:

    核对截图中的修复流程:

  • su lzh 切换回管理员用户;
  • 执行 sudo usermod -aG sudo vir(红框标注,-aG 代表向附加组追加,避免覆盖原有附加组);
  • 执行 newgrp sudo 刷新用户组凭证(或重新登录终端);
  • 切回 vir 后测试 sudo whoami,终端正确打印出 root,表明 vir 已成功获得管理员提权凭证。
  • 随后再次修改文件归属:

    vir 执行 sudo chown vir mystring.h 后,ls -l 显示该文件的拥有者已从 lzh(红框标注)成功变更为 vir(红框标注)。


    3. chgrp 与组权限匹配实战

    chgrp(change group)用于修改文件或目录的所属组。

    chgrp [选项] 组名 文件或目录

    在多用户协作中,组权限常常引发困惑:“我和张三都在同一个用户组里,为什么我还是无法访问他的文件?”

    我们先查看当前系统下 vir 和 lzh 各自所在的组:

    vir@VM-4-15-ubuntu:~/Linux/02_permission/mydir/lib$ groups vir
    vir : vir sudo users
    vir@VM-4-15-ubuntu:~/Linux/02_permission/mydir/lib$ groups lzh
    lzh : lzh sudo users docker

    两个用户都加入了 sudo 和 users 组。但如果查看 mydir 下的文件属性:

    这些文件的所属组是 lzh。系统在进行权限判定时,检查的是:“当前访问者所在的所有组中,是否包含该文件所指定的所属组”。vir 虽然在 users 组,但不在 lzh 组,因此文件的“组权限”对 vir 完全不生效,vir 依然只能被当作 Other。

    我们通过一个反向示例深入验证所属组的作用机制:

    观察截图操作:

  • vir 创建的文件 vircode.c,其权限被设置为 -rw-rw—- 1 vir vir(红框标注),属主有读写、属组有读写,但 Other 为 —(毫无权限);
  • 此时 lzh 用户尝试读取:cat vircode.c,系统直接拦截并报错:cat: vircode.c: Permission denied。因为 lzh 不在 vir 组中,被落入 Other 规则。
  • 若要让 lzh 在不破坏 Other 隔离的前提下访问该文件,正规做法是将 lzh 加入文件的所属组 vir:

    lzh 运行 sudo usermod -aG vir lzh,再用 groups lzh 确认,其附加组中已顺利包含 vir(红框标注)。

    用户重新登录终端加载新的组身份后,再次发起访问:

    此时 vircode.c 的权限依然是严格的 -rw-rw—-(红框标注,Other 依然无权访问)。但系统判定 lzh 属于该文件的所属组 vir,从而匹配了第 4~6 位的组读权限,cat vircode.c 成功输出了文件内容:hello linux。


    四、默认权限生成机制:umask

    在 Linux 中,每次新建文件或目录时,我们并没有手动为其指定权限,但系统总会赋予一个初值(如目录通常是 755,普通文件通常是 644)。这个默认值并非随意生成,而是由 umask(用户掩码) 决定的。

    1. 起始权限与 umask 概念

    系统对新建项目预设了起始权限:

    • 普通文件:由于通常不需要直接执行,出于安全考虑,系统预设的最大起始权限为 666(即 rw-rw-rw-,默认剥离执行权限 x);
    • 目录文件:因为目录必须具备 x 权限才能进入浏览,系统预设的最大起始权限为 777(即 rwxrwxrwx)。

    umask 的作用就像一张筛子,其数值代表在创建时需要从起始权限中剥离掉的权限位。

    2. 严谨的数学逻辑:按位与非

    常见教程中常把计算简化为“起始权限减去 umask”,但在特定非对称掩码下,算术减法可能得出错误结论。在内核层面上,真实的运算逻辑是按位与非(Bitwise AND NOT):

    最终权限=起始权限 & (∼umask)\\text{最终权限} = \\text{起始权限} \\ \\& \\ (\\sim\\text{umask})最终权限=起始权限 & (∼umask)

    即:将 umask 取反之后,与起始权限进行二进制“与”运算
    凡是在 umask 对应位置为 1 的位,最终权限中一律被清洗置 0。

    以常规的 umask = 022 为例:

    • 022 的二进制表示为:000 010 010,代表组和其他人的写权限(w)必须被拿掉;
    • 文件最终权限:666 & ~022 = 644(rw-r–r–);
    • 目录最终权限:777 & ~022 = 755(rwxr-xr-x)。

    3. 实战查看与验证

    在当前 Shell 终端中直接运行 umask 即可查看当前掩码:

    截图中输出为 0002(第一位是特殊权限掩码,后三位代表普通掩码 002)。

    根据计算规则,当 umask = 002 时:

    • 文件最终权限:666 & ~002 = 664(rw-rw-r–,属主与属组均可读写,其他人只读);
    • 目录最终权限:777 & ~002 = 775(rwxrwxr-x,属主与属组具备完整权限,其他人不可写)。

    我们新建项目进行验证:

    截图中执行 mkdir test/ 与 touch temp.txt:

    • temp.txt 权限为 -rw-rw-r–(664,红框标注);
    • test 目录权限为 drwxrwxr-x(775,红框标注);
    • 实际结果与位运算推导完全吻合。

    如需调整掩码:

    # 临时调整(仅在当前 Shell 会话中生效)
    umask 022

    # 永久调整:写入用户的 Shell 环境配置文件
    echo "umask 022" >> ~/.bashrc
    source ~/.bashrc


    五、共享目录与防误删防线:粘滞位(Sticky Bit)

    回顾前面学到的目录权限机制:一个用户能否删除目录下的文件,取决于该用户对该目录是否具备 w 权限。

    这引发了一个经典的系统安全难题:
    在多人协作服务器中,往往需要一个公共临时共享目录(例如系统的 /tmp)。为了让所有人都能在里面存取文件,这个目录必须向所有人开放写权限(drwxrwxrwx)。
    但是,一旦目录对 Other 开放了 w 权限,任何普通用户都可以随心所欲地删掉其他任何人创建的文件。

    为了直观体会这一隐患,我们来看一组连续测试。

    1. 多层权限递进实验

    lzh 创建了目录 ldir,并在其中放入了文件 mycode.c。此时 vir 用户尝试强行删除该目录:

    截图中 vir 执行 rm -rf ./ldir/,系统报错:rm: cannot remove './ldir/mycode.c': Permission denied(红框标注)。
    原因在于此时 ldir 的 Other 权限仅为 r-x,没有写权限,vir 无法清理其内部的文件。

    随后,拥有者 lzh 给 ldir 加上了 Other 的写权限:

    截图中 lzh 执行 chmod o+w ldir 后,ldir 权限变为 drwxrwxrwx。
    此时 vir 再次尝试 rm -rf ./ldir/,终端依然报错:rm: cannot remove './ldir/': Permission denied(红框标注)。

    核心剖析:为什么目录本身无法被删?
    rm -rf ./ldir/ 是一个复合操作:

  • 先删除 ldir 内部的子项 mycode.c —— 由于 ldir 已经是 777 权限,这一步原本可以成功;
  • 接着必须从 ldir 的**上层父目录(即 mydir)**中把 ldir 这个目录节点抹去。
    由于此时父目录 mydir 对 Other 依然没有写权限,因此最后一步被坚决拦截!
  • 只有当拥有者连同父目录 mydir 一并开放 Other 写权限后:

    父目录 mydir 加上 w 权限后(截图标注“父目录加上w权限之后就能够删除当前目录本身!”),vir 再次运行 rm -rf ./ldir/,目录连同内部所有文件瞬间被彻底删除。

    这就把安全隐患彻底暴露出来了:只要共享目录具有写权限,任意用户的劳动成果都面临被他人恶意删除或误删的风险。

    2. 粘滞位的作用与机制

    为了彻底解决“公共目录可自由创建、但不可随意互删”的矛盾,Linux 引入了特殊权限——粘滞位(Sticky Bit)。

    粘滞位核心保护规则:
    当一个目录被赋予粘滞位后,即使该目录对所有人开放了写权限(w),该目录下的文件和子目录也只能由以下三类人删除或重命名:

  • 超级管理员(root);
  • 该共享目录的拥有者;
  • 该文件自身的拥有者。
    除此之外的任何其他人,哪怕拥有目录的 w 权限,也无权删除他人的文件。
  • 设置粘滞位使用 chmod +t(八进制模式为第一位赋予 1,如 chmod 1777 目录):

    chmod +t 目录名

    在测试环境中给共享目录赋予粘滞位:

    观察截图中的关键变化:

  • lzh 执行 chmod +t ./ldir/(红框标注),ldir 权限最末尾的 x 变成了 t(状态显示为 drwxrwxrwt);
  • 随后 vir 再次尝试跨权限删除:rm -rf ./ldir/;
  • 终端的报错信息由原本的 Permission denied 变为了 Operation not permitted(红框标注),删除行为被粘滞位机制精准制止!
  • 生产环境典范:
    查看 Linux 根目录下的 /tmp 目录:

    ls -ld /tmp
    # 输出:drwxrwxrwt … /tmp

    系统中的所有程序和普通用户都可以在 /tmp 存放临时缓存,但任何人只能清理自己生成的文件,绝不可能误删系统的关键套接字或其他用户的临时数据。这正是粘滞位在现代 Linux 系统中最基础、也是最重要的工程落地。


    六、总结与权限自查心法

    整篇梳理下来,Linux 权限的本质是一套层次分明、就近匹配的访问控制规则
    日常遇到权限报错时,可以按照以下逻辑快速自查:

  • 确定身份层次:
    • 优先检查访问者是文件的 Owner、Group 成员 还是 Other。系统按该顺序命中第一类匹配项后立即应用对应权限,后续权限位不再参与计算。
  • 区分操作对象:
    • 操作文件时,读(cat)需要文件的 r,改写(vim)需要文件的 w,运行需要文件的 x;
    • 增删文件、重命名或移动文件时,首要检查的是其所在父目录的 w 和 x 权限,而非文件自身。
  • 把关新建与共享:
    • 检查默认权限是否符合预期,通过 umask 调整掩码;
    • 遇到公共共享目录,及时配置粘滞位(chmod +t),避免多人协作下的误删风险。
  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » 【Linux 修炼录】告别 Permission denied:一文讲透 Linux 权限体系与底层逻辑
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!