Appearance
权限管理
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=4、w=2、x=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 读写常用组合就那么几个,记一下:
| 八进制 | 组合 | 权限含义 |
|---|---|---|
| 7 | 4+2+1 | 读+写+执行 |
| 6 | 4+2 | 读+写 |
| 5 | 4+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 -R 和 chmod -R 的递归会影响整个目录树,路径打错的时候破坏面非常大——特别是 /etc、/var 这种系统目录,一旦误操作 chown -R 把整个 /var 的属主改了,系统基本就废了。执行递归操作前,先 pwd 确认当前目录,再 ls -ld 确认目标路径,这两步花不了几秒,但能救命。
四、umask 默认权限
umask 决定新建文件和目录时,默认扣掉哪些权限位。它不直接设权限,而是设一个"掩码"——从满权限里减去这个掩码,得到实际权限。
bash
umask # 查看当前值
umask 022 # 设置为 022常见 umask 对应的实际权限:
| umask | 新建文件实际权限 | 新建目录实际权限 |
|---|---|---|
022 | 644(rw-r--r--) | 755(rwxr-xr-x) |
027 | 640(rw-r-----) | 750(rwxr-x---) |
077 | 600(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,回到纯 UGOACL 只适合少量例外场景。如果一个系统里到处都在用 ACL 管权限,排查问题的时候只看 ls -l 会漏掉一堆信息——因为 ls -l 不显示完整 ACL,得 getfacl 才能看全。到处都是 ACL 的系统,维护成本会急剧上升,后来人完全理不清权限关系。所以能用 UGO 解决的就用 UGO,ACL 只在万不得已的时候补一刀。
六、SUID、SGID、Sticky Bit
这三个特殊权限位在 ls -l 里会替代通常的 x 位置显示,看到 s、t 这种字符就是特殊权限。
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 值得一看,可能是扩展属性在作祟。