路线图

权限系统

星辉 2026-07-02 阅读 7 min 1,321 字 路线图
权限系统 封面

Linux 运维基础 · 第四章 目标:理解 Linux 权限模型,能排查和修复权限导致的故障


1. 概述#

运维最常听到的报错之一就是 Permission denied。Linux 权限系统虽然只有 r/w/x 三位,但文件和目录的含义不同,加上 SUID、Sticky bit 这些特殊位,很多人一直处于"猜着用"的状态。

本章一次性讲清楚:rwx 对文件和目录分别意味着什么、数字和符号两种设置方式、特殊权限位的实际用途、umask 默认权限规则,以及超越 ugo 三组的两个进阶机制——ACL(精细授权)和 capabilities(替代 SUID 的最小权限)。


2. rwx 权限#

2.1 权限数字#

bash
ls -l app.log
# -rw-r--r-- 1 root root 1024 Jul  1 10:00 app.log
# ^^^^^^^^^^  权限
#  |  |  |
#  |  |  └── 其他人(other)的权限:r--
#  |  └───── 所属组(group)的权限:r--
#  └──────── 所有者(owner)的权限:rw-

权限字段拆解:

text
-  rw-  r--  r--
│  │││  │││  │││
│  │││  │││  └└└── other 权限
│  │││  └└└──── group 权限
│  └└└───────── owner 权限
└────────────── 文件类型(- 普通文件, d 目录, l 链接)

数字换算

权限二进制数字
r--1004
-w-0102
--x0011
rw-1104+2=6
r-x1014+1=5
---0000
rwx1114+2+1=7
bash
# 数字速记
chmod 755 script.sh    # rwxr-xr-x(owner 全权限,其他读+执行)
chmod 644 config.conf   # rw-r--r--(owner 可写,其他只读)
chmod 600 private.key   # rw-------(仅 owner 可读写)
chmod 777 public/       # rwxrwxrwx(所有人全权限——危险!)

2.2 文件 vs 目录的 rwx 含义#

同一个 r/w/x,对文件和目录的含义完全不同。

权限对文件对目录
r (读)可以读取文件内容(cat、less)可以列出目录中的文件名(ls)
w (写)可以修改文件内容(vim、echo >)可以在目录中创建/删除/重命名文件
x (执行)可以执行该文件(./script.sh)可以进入目录(cd),可以访问目录内文件
无 r不能读内容不能 ls,但如果有 x 可以 cd 进入
无 w不能修改不能增删文件,哪怕文件本身有 w 权限
无 x不能执行不能 cd 进入,目录内所有文件都访问不到

目录权限的典型陷阱#

bash
# 场景:目录 777 但无法进入
mkdir testdir
chmod 666 testdir      # 有 rw 但没有 x
ls testdir              # 能列出
cd testdir              # Permission denied  ← 缺 x

# 场景:有 x 没有 r(经典的 711 目录,如 /home/用户名)——能"穿过"目录访问已知文件,但列不出目录
# 注意:必须以「其他用户」身份测试。owner 自己用的是 owner 位(这里 rwx),照样能 ls
chmod 711 testdir
echo hi > testdir/known.txt
sudo -u nobody ls testdir             # Permission denied  ← other 无 r,列不出文件名
sudo -u nobody cat testdir/known.txt  # 成功  ← other 有 x 能穿过目录,known.txt 自身可读

# 场景:目录只读(r-x)——已存在文件的内容照样能改,改不了的是「目录条目」
chmod 555 folder/               # 你是 owner,但去掉了目录的 w
cd folder/
echo "update" > existing.txt    # 成功!改已有文件内容只看文件自己的 w,与目录的 w 无关
echo "new"    > brandnew.txt    # Permission denied ← 新建文件要写目录条目,需目录 w
rm existing.txt                 # Permission denied ← 删除同理,动的是目录不是文件

权限按"就近的那一类"判定,不叠加:内核检查顺序是 owner → group → other,匹配到哪一类就只用那一类的权限,不会往后蹭。所以会出现反直觉的情况:文件 ----rw-rw-(owner 无权限、group/other 可读写),文件的 owner 反而读不了自己的文件——他属于 owner 类,直接用 ---,不会降级去用 group/other 的权限。上面 711 目录必须换个用户测,同样是这个原因。

2.3 目录 rwx 权限组合速查#

权限数字lscd读文件创建/删除文件
---0----
--x1-++(知道文件名)-
-w-2----(无意义组合)
r--4+---
r-x5+++-
rwx7++++

3. chmod / chown / chgrp#

3.1 chmod — 修改权限#

符号方式(易读)#

bash
chmod u+x script.sh          # owner 加执行权限
chmod g-w config.conf        # group 去掉写权限
chmod o-r private.key        # other 去掉读权限
chmod a+r public.txt         # all(所有人)加读权限
chmod u=rwx,g=rx,o= script.sh  # 精确设置

符号含义:

符号含义
uuser(所有者)
ggroup(所属组)
oother(其他人)
aall(所有人,等同于 ugo)
+添加权限
-移除权限
=精确设置为

数字方式(简洁)#

bash
chmod 755 script.sh          # rwxr-xr-x
chmod 644 config.conf        # rw-r--r--
chmod 600 private.key        # rw-------
chmod 400 public_key.pub     # r--------(只读保护)
chmod 700 ~/.ssh             # rwx------(SSH 目录安全要求)
chmod -R 755 /var/www/       # 递归修改(谨慎:普通文件不该都有 x)
chmod -R u=rwX,go=rX /var/www/  # 推荐:大写 X 只给「目录」和「本就可执行的文件」加 x,普通文件不会被误加执行位

目录和文件分开设权限#

bash
# 目录设 755,文件设 644(常见组合)
find /var/www -type d -exec chmod 755 {} \;
find /var/www -type f -exec chmod 644 {} \;

3.2 chown — 修改所有者#

bash
chown deploy app.log                   # 改所有者
chown deploy:deploy app.log            # 同时改所有者和组
chown -R deploy:deploy /opt/myapp/     # 递归修改

3.3 chgrp — 修改所属组#

bash
chgrp www-data /var/www/html/          # 改所属组
chgrp -R www-data /var/www/            # 递归修改

4. umask:默认权限#

4.1 是什么#

umask 控制新建文件和目录的默认权限。它是一个"掩码":从最大权限里清除 umask 中置位的权限位(本质是按位运算 最大权限 AND (NOT umask)),不是算术减法

  • 文件最大权限是 666(rw-rw-rw-,新建文件默认不带执行位)
  • 目录最大权限是 777(rwxrwxrwx)

别把它当减法:umask 022 这类"整齐"的值,减法和清位算出来一样(666−022=644),容易让人误以为是减法。但遇到 umask 027:文件 666 减 027,个位 6−7 要借位,算术根本得不出合法结果;正确做法是清位——umask 027 = ----w-rwx,从 rw-rw-rw- 里把 group 的 w、other 的 rwx 全清掉 → rw-r----- = 640。同理 umask 023 减法得 643(rw-r---wx,other 竟带执行位,明显荒谬),清位才得正确的 644

4.2 常见 umask 值的效果#

umask新建文件权限新建目录权限说明
022644 (rw-r--r--)755 (rwxr-xr-x)默认,owner 可写,其他人只读
002664 (rw-rw-r--)775 (rwxrwxr-x)同组可写
077600 (rw-------)700 (rwx------)最严格,仅 owner 可访问
027640 (rw-r-----)750 (rwxr-x---)同组可读,其他人完全不可访问

4.3 查看和设置#

bash
# 查看当前值
umask
# 输出 0022(第一位是特殊权限,看后三位 022)

# 临时修改
umask 027                     # 当前 Shell 会话有效

# 永久设置:写入 shell 配置文件
echo "umask 027" >> ~/.bashrc

# 系统级设置
# /etc/profile 或 /etc/bashrc 中修改

4.4 实战验证#

bash
# 当前 umask 022 下
touch testfile
mkdir testdir
ls -ld testfile testdir
# -rw-r--r-- 1 user user  0 Jul  1 10:00 testfile    ← 644(从 666 清除 022 置位的权限)
# drwxr-xr-x 2 user user 40 Jul  1 10:00 testdir     ← 755(从 777 清除 022 置位的权限)

5. 特殊权限位 SUID / SGID / Sticky Bit#

除了标准的 rwx 三位之外,还有三个特殊权限位。

5.1 SUID(Set User ID)— s 在 owner 的执行位上#

bash
# 普通用户的 passwd 命令
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root 59976 Jan 1 12:00 /usr/bin/passwd
#    ^ 这里是 s 而不是 x

# 含义:任何人执行这个文件时,都以文件所有者(root)的身份运行
# 所以普通用户可以用 passwd 改自己的密码(需要写 /etc/shadow,root 才有权限)

数字表示:在第一组权限前加 4

bash
chmod 4755 /usr/bin/myprogram    # 加 SUID
chmod u+s /usr/bin/myprogram     # 符号方式

安全注意:SUID 是常见攻击面,定期审计系统中的 SUID 文件:

bash
find / -perm -4000 -type f 2>/dev/null
# 检查有没有不该有 SUID 的可疑文件

SUID 对脚本无效:现代 Linux 内核忽略脚本(带 #!/bin/bash 之类 shebang 的脚本)的 SUID/SGID 位,只对二进制可执行文件生效。这是安全设计——脚本能被解释器参数、环境变量等多种方式劫持提权。想让脚本以特权运行,正确做法是用 sudo 配规则(见第五章),或写一个二进制 wrapper,而不是给脚本 chmod u+s

5.2 SGID(Set Group ID)— s 在 group 的执行位上#

bash
ls -l /usr/bin/write
# -rwxr-sr-x 1 root tty ...
#        ^ SGID

# 含义:
#   - 对可执行文件:以文件所属组的身份运行
#   - 对目录:在此目录下创建的新文件继承目录的组所有者

实用场景:共享目录(组内协作)

bash
# 项目目录
mkdir /opt/project
chgrp developers /opt/project
chmod 2775 /opt/project          # 2 = SGID
# 或
chmod g+s /opt/project

# 现在所有在 /opt/project 下创建的文件都属于 developers 组
# 团队成员都能读写

5.3 Sticky Bit — t 在 other 的执行位上#

bash
ls -ld /tmp
# drwxrwxrwt 20 root root 4096 Jul  1 10:00 /tmp
#          ^ t

# 含义:只有文件所有者和 root 才能删除目录中的文件
# 即使目录是 777,A 用户也删不了 B 用户的文件

数字表示:在第三组权限前加 1

bash
chmod 1777 /tmp     # 等同于 chmod +t /tmp

6. 实战#

6.1 普通用户改密码的秘密#

bash
# 现象:heiye(普通用户)能改自己的密码
heiye@server:~$ passwd
Changing password for heiye.
Current password:
New password:
Retype new password:
passwd: password updated successfully

# 原理:/usr/bin/passwd 有 SUID 位
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root 59976 Jan 1 12:00 /usr/bin/passwd

# passwd 以 root 权限运行 → 能写入 /etc/shadow(root 才能写)
ls -l /etc/shadow
# -rw-r----- 1 root shadow 1024 Jul 1 10:00 /etc/shadow
# heiye 直接写 /etc/shadow 肯定报 Permission denied
# 但通过 passwd 就行——因为是 root 在代劳

6.2 排查"用不了"的权限问题#

bash
# 故障:nginx 报 "403 Forbidden"
# Step 1:检查文件和目录权限
ls -l /var/www/html/index.html
# -rw------- 1 root root 1024 ...    ← nginx 以 www-data 运行,读不了

# Step 2:修权限
chmod 644 /var/www/html/index.html

# 还不行?检查目录的 x
ls -ld /var/www/html
# drwx------ 2 root root 4096 ...    ← 目录没有 x,nginx 进不去

chmod 755 /var/www/html

# 还不行?检查上级目录
ls -ld /var/www
ls -ld /var
# 每一级目录都要有 x 权限,才能一路访问到目标文件

6.3 批量修复 Web 项目权限#

bash
# 常见要求:目录 755,文件 644,所有者为 www-data
chown -R www-data:www-data /var/www/myapp/
find /var/www/myapp -type d -exec chmod 755 {} \;
find /var/www/myapp -type f -exec chmod 644 {} \;

# 如果某些子目录需要写权限(上传目录等)
chmod 775 /var/www/myapp/uploads/

7. ACL:超越 ugo 三组的精细授权#

传统 rwx 只能对"所有者 / 组 / 其他人"三类设权限。想让某个特定用户、或好几个不同的组对同一个文件有各自不同的权限,ugo 就不够用了——这时用 ACL(Access Control List)。主流发行版(Rocky / Ubuntu)默认文件系统已支持 ACL。

bash
# 查看 ACL(带 ACL 的文件,ls -l 权限位末尾会多一个 + 号)
getfacl report.xlsx

# 给用户 alice 单独授予读写(不动其他人的权限)
setfacl -m u:alice:rw report.xlsx

# 给组 auditors 授予只读
setfacl -m g:auditors:r report.xlsx

# 递归给目录设 ACL,并设「默认 ACL」——之后目录里新建的文件自动继承
setfacl -R -m  u:alice:rwX /data/shared
setfacl -R -m d:u:alice:rwX /data/shared    # d: = default,只对将来新建的文件生效

# 删除某一条 ACL / 清空全部 ACL
setfacl -x u:alice report.xlsx
setfacl -b report.xlsx

mask 与有效权限getfacl 输出里的 mask:: 行是所有"具名用户/组"权限的上限。如果你 setfacl 明明给了 rw 却不生效,先看是不是 mask 把它压成了 r。


8. Linux capabilities:替代 SUID 的最小权限#

SUID 是"全有或全无"——一个 SUID root 程序一旦被攻破,攻击者就拿到完整 root。capabilities 把 root 的能力切成几十个细粒度的权限单元(如"绑定 <1024 的端口""修改任意文件属主"),只给进程它真正需要的那一个,风险面小得多。

bash
# 查看文件被赋予的 capabilities
getcap /usr/bin/ping
# /usr/bin/ping cap_net_raw=ep     ← ping 只需要发原始包的能力,并不需要整个 root

# 经典场景:让非 root 程序绑定 80/443 等特权端口(原本需要 root 或 SUID)
setcap 'cap_net_bind_service=+ep' /opt/myapp/bin/server
# 之后普通用户跑 server 也能监听 80 端口,而它并不具备任何其它 root 权限

# 去掉文件上的 capabilities
setcap -r /opt/myapp/bin/server

# 查看某进程 / 自己当前拥有的 capabilities(需要 libcap 工具包)
getpcaps $$
capsh --print

常见 capability 速览:

capability能力
cap_net_bind_service绑定 1024 以下的特权端口
cap_net_raw使用 raw socket(ping、抓包)
cap_chown修改任意文件的属主
cap_dac_override绕过文件的读/写/执行权限检查
cap_sys_time修改系统时钟

优先用 capabilities,别再随手 chmod u+s:能用 setcap 精确授权就不要给二进制加 SUID root。审计时用 getcap -r / 2>/dev/null 列出全系统带 capabilities 的文件。


9. 小结#

知识点一句话记忆
rwx 文件 vs 目录文件:读内容/写内容/执行;目录:列文件/增删文件/进入
数字权限4=读 2=写 1=执行;755(目录) + 644(文件) 是最常见的组合
umask从最大权限清除 umask 置位的权限位(非算术减法);022→文件644、目录755
SUID以文件所有者身份执行,s 位在第一组;对脚本无效,只对二进制生效
SGID目录下新文件继承组所有者,s 位在第二组
Sticky Bit只有文件所有者能删自己的文件,t 位在第三组(如 /tmp)
chownchown user:group file 同时改所有者和组
ACL超越 ugo 的精细授权,setfacl -m u:alice:rwgetfacl 查看
capabilities拆分 root 能力做最小授权,setcap cap_net_bind_service=+ep,优先于 SUID

上一章:三、文件操作 下一章:五、用户与组管理