Skip to content

权限管理

Linux 的文件访问控制,核心是 UGO(user/group/other)权限模型,按需再配合 ACL、特殊权限位、扩展属性这几层。权限问题排查起来特别折腾,因为同一个文件可能涉及好几层权限叠加,得一层层剥才能定位"为什么我读不了/写不了/执行不了"。

一、UGO 权限模型

每个文件有三组权限,分别对应三类身份:

身份是谁
User(owner)文件的所有者
Group文件所属组的成员
Other既不是 owner、也不属于 group 的所有人

权限判断的顺序是 owner → group → other,命中即停。这条规则有个容易被忽略的后果:即使你属于文件的 group、group 权限也够,但只要你自己就是 owner,就只看 owner 权限位,group 那部分直接跳过。所以"我在 group 里、group 有权限,但我还是访问不了"这种情况,先确认你是不是 owner。

还有一条更要命的规则:访问文件需要路径上每一级目录都有 x(执行)权限。目录没有 x,就算文件本身权限是 777,你也进不去——因为连目录都进不去,根本到不了文件这一层。新人排查"文件权限明明够了还是打不开",十有八九是上层目录缺 x

ls -l 的输出,以一个日志文件为例:

-rw-r----- 1 nginx adm 1024 May 21 10:00 app.log

逐段拆开:

部分含义
-文件类型,- 表示普通文件
rw-owner(nginx)有读和写权限
r--group(adm)只有读权限
---other 没有任何权限
nginx文件所有者
adm文件所属组

权限位对文件和目录的意义不一样,这是新手最容易搞混的地方:

权限作用于文件作用于目录
r(读)查看文件内容(cat、less 能看)列出目录里的文件名(ls 能列出)
w(写)修改文件内容在目录里创建、删除、重命名文件
x(执行)作为程序运行进入目录(cd 能进)

注意目录的 w 权限:有 w 权限就能删除目录里的任何文件,哪怕这个文件不是你创建的、文件本身权限你也没有——只要目录的 w 对你开放。/tmp 就是典型,所有人都能在里面建文件,默认情况下也能删别人的文件。为了避免这种互相伤害,/tmp 一般会加 Sticky Bit 限制删除。

二、chmod 修改权限

数字模式用三位八进制数表示权限,r=4w=2x=1,加起来组合。背一下 4/2/1 这几个数,看到 755、644 能立刻反应过来是什么权限:

bash
chmod 644 file.txt       # owner 读写,其余只读
chmod 755 script.sh      # owner 全权限,其余读+执行
chmod 750 /opt/app       # owner 和 group 可用,other 无权限
chmod 600 private.key    # 仅 owner 读写

常用组合就那么几个,记一下:

八进制组合权限含义
74+2+1读+写+执行
64+2读+写
54+1读+执行
0无权限

符号模式适合做增量修改,不用算数,加加减减就行:

bash
chmod u+x script.sh         # owner 加执行权限
chmod g+w shared-dir        # group 加写权限
chmod o-r file.txt          # other 去掉读权限
chmod +x deploy.sh          # 所有身份加执行(等同 a+x)

私钥文件(SSH key、TLS 密钥)、含密码的配置文件,权限至少 600。这不是建议,是硬性要求——权限过宽(比如 644、777)时,某些服务会直接拒绝使用并报错。比如 sshd 看到 ~/.ssh/id_rsa 权限是 644,会报 Permissions are too open 然后拒绝认证,SSH 登录直接失败。这种问题排查起来很绕,因为你以为是密钥不对,其实是权限不对。

三、chown 修改所有者

bash
chown nginx:nginx /var/log/nginx/access.log
chown -R app:app /opt/app         # 递归修改整个目录树

chown -Rchmod -R 的递归会影响整个目录树,路径打错的时候破坏面非常大——特别是 /etc/var 这种系统目录,一旦误操作 chown -R 把整个 /var 的属主改了,系统基本就废了。执行递归操作前,先 pwd 确认当前目录,再 ls -ld 确认目标路径,这两步花不了几秒,但能救命。

四、umask 默认权限

umask 决定新建文件和目录时,默认扣掉哪些权限位。它不直接设权限,而是设一个"掩码"——从满权限里减去这个掩码,得到实际权限。

bash
umask             # 查看当前值
umask 022         # 设置为 022

常见 umask 对应的实际权限:

umask新建文件实际权限新建目录实际权限
022644(rw-r--r--)755(rwxr-xr-x)
027640(rw-r-----)750(rwxr-x---)
077600(rw-------)700(rwx------)

注意新建文件默认不会带执行权限(x),因为大多数文件不需要可执行,这也是出于安全考虑——文件最大从 666 开始减。如果服务进程创建出来的日志文件权限跟预期不一样,除了检查程序代码里的权限设置,也要看看启动用户的 umask——很多时候是 umask 没设对,导致日志文件别人读不了,或者权限过宽。

五、ACL 访问控制列表

UGO 模型只能表达三类身份,有时候不够用。比如"文件属于 app 组,但同时想给 deploy 用户单独加个只读权限"——UGO 表达不了这种例外,得靠 ACL。

bash
getfacl file.txt                        # 查看文件 ACL
setfacl -m u:deploy:r file.txt          # 给 deploy 用户加只读
setfacl -m g:ops:rx /opt/app            # 给 ops 组加读和执行
setfacl -x u:deploy file.txt            # 删除 deploy 用户的 ACL 条目
setfacl -b file.txt                     # 清空所有 ACL,回到纯 UGO

ACL 只适合少量例外场景。如果一个系统里到处都在用 ACL 管权限,排查问题的时候只看 ls -l 会漏掉一堆信息——因为 ls -l 不显示完整 ACL,得 getfacl 才能看全。到处都是 ACL 的系统,维护成本会急剧上升,后来人完全理不清权限关系。所以能用 UGO 解决的就用 UGO,ACL 只在万不得已的时候补一刀。

六、SUID、SGID、Sticky Bit

这三个特殊权限位在 ls -l 里会替代通常的 x 位置显示,看到 st 这种字符就是特殊权限。

SUID(Set UID):文件执行时,进程临时获得文件 owner 的权限。

bash
ls -l /usr/bin/passwd
# -rwsr-xr-x  注意 owner 权限位中的 s

/usr/bin/passwd 是最经典的例子:普通用户执行它要修改 /etc/shadow(只有 root 能写),靠的就是 SUID 临时提权——执行期间进程以 root 身份跑,改完 shadow 就退出。SUID 权限安全风险很高,因为它本质上就是"以 owner 身份执行"。系统里出现的 SUID 文件都应该被审计,异常的 SUID 文件(尤其是被攻击者新加的)是提权攻击的常见手段。

SGID(Set GID) 设在目录上时,该目录下新建的文件会自动继承目录的所属组,而不是用创建者的主组:

bash
chmod g+s shared-dir

多人协作的共享目录常用 SGID——所有人往里创建的文件都属于同一个组,组内成员就都能访问,省得一个一个改属组。

Sticky Bit 设在目录上时,只有文件 owner、目录 owner、root 能删除目录里的文件,其他人即使有目录写权限也不能删别人的文件:

bash
ls -ld /tmp
# drwxrwxrwt  注意 other 权限位中的 t
chmod +t shared-dir

/tmp 就是设了 Sticky Bit——所有人都能在里面建临时文件,但不能删别人的。没有 Sticky Bit 的话,/tmp 就是"谁都能删谁的东西",文件随时可能被清掉,没法用。

七、chattr 扩展属性

chattr 设置的属性作用于文件系统层面,比普通权限更底层,root 的常规权限都管不了它。

bash
chattr +i important.conf        # 设为不可修改(immutable)
lsattr important.conf           # 查看扩展属性
chattr -i important.conf        # 去掉不可修改属性

设了 +i(immutable)之后,即便是 root 也不能修改、删除、重命名这个文件,也不能创建硬链接——必须先 chattr -i 去掉属性才能动。这对保护关键配置文件很有用,攻击者拿到 root 也改不了你的核心配置(除非他知道 chattr)。

另一个有用的属性是 +a(append only):只允许往文件追加内容,不能覆盖已有部分,适合保护日志文件不被误删或篡改。

讲一个真实场景:线上自动化部署脚本改配置文件失败,排查半天——文件权限没问题、属主没问题、进程权限也够,最后 lsattr 一看,文件被加了 +i 属性。可能是之前某次安全加固或者临时防篡改设的,后来忘了去掉。所以遇到"明明是 root 却改不动文件"这种诡异情况,lsattr 值得一看,可能是扩展属性在作祟。