网站报 403、程序提示 Permission denied、日志写不进去、SFTP 上传失败、同事说"我没权限看这个目录"——这些都是权限问题,而权限问题几乎占运维日常故障的一半以上。奇怪的是,很多人遇到权限报错的应对方式是"上来就 chmod 777",改完确实好了,但那等于把门拆了:任何用户(包括被入侵的进程)都能随意读写你的程序和数据,这是最常见的安全隐患之一。
要真正修好权限问题,得先理解 Linux 权限模型:文件归属于"用户 + 组",权限分 rwx 三档、作用于 owner/group/other 三类主体,还有 SUID、SGID、Sticky 三个特殊位,以及传统模型之外的一套 ACL(访问控制列表)。理解这套模型之后,Permission denied 就不再是玄学——你可以顺着"谁在访问、以什么身份、访问目标是谁的、权限给了谁"这几步,精确地定位到缺失的那一位。
这篇文章按"从基础到进阶"的顺序展开:先讲清 rwx 和 ls 输出怎么读,再讲 chmod/chown/umask 的正确用法,然后是特殊权限位,接着是 ACL 解决"需要给某个特定用户单独授权"的场景,最后给出 Web 服务、共享目录、SFTP 等典型场景的权限方案,以及排查权限问题的标准流程。文中会特别标注那些"看起来很省事、实际上很危险"的操作。
先记住一句总纲:权限报错永远只回答两个问题——"谁是访问者(有效用户/组)"和"目标的权限位允不允许他这么做"。 把这两点查清楚,问题就解了。
一、读懂 ls -l 的输出
权限的所有信息都藏在 ls -l 的第一列里:
ls -l /var/www/html/index.php
# -rw-r--r-- 1 root root 2048 Oct 12 10:00 index.php
# │└┬┘└┬┘└┬┘ │ │
# │ │ │ └── 其他用户权限:r--
# │ │ └───── 所属组权限:r--
# │ └──────── 属主权限:rw-
# └────────── 文件类型:- 普通文件,d 目录,l 符号链接
权限三档的含义:
| 权限 | 对文件 | 对目录 |
|---|---|---|
| r(读) | 读取文件内容 | 列出目录内文件(ls) |
| w(写) | 修改文件内容 | 在目录内创建/删除/重命名文件 |
| x(执行) | 作为程序执行 | 进入目录(cd)、访问目录内文件 |
这里有一个非常关键、也最常被误解的点:对目录来说,x 权限是"能否进入",r 是"能否列出内容",w 是"能否在里面增删文件"。 一个常见困惑是"我有目录的读权限但 cd 不进去",原因就是缺 x;反过来"我能 cd 进去但 ls 看不到内容",是缺 r。而"能进能看但删不掉里面的文件",往往是缺目录的 w。
还有一点:删除一个文件,取决于你对该文件所在目录是否有 w 权限,而不是对文件本身是否有 w。 这解释了很多"这个文件明明是我的,为什么删不掉"的现象——文件可能是别人的,但目录你有写权限,那你其实能删;反之文件是你的但没有目录写权限,就删不掉。
二、chmod:改权限
# 数字法(八进制):r=4 w=2 x=1,三位分别对应 owner/group/other
chmod 644 file # rw-r--r-- 普通文件推荐
chmod 755 script.sh # rwxr-xr-x 可执行脚本/目录推荐
chmod 600 id_rsa # rw------- 私钥必须
chmod 700 ~/.ssh # rwx------ .ssh 目录推荐
# 符号法:u=属主 g=属组 o=其他 a=全部;+加 -减 =设定
chmod u+x script.sh # 给属主加执行权限
chmod g-w shared.txt # 去掉属组的写权限
chmod o= file # 清空其他用户的所有权限
chmod -R g+r /data/share # 递归给目录及其内容加组读权限
# 递归改目录:目录需要 x,文件不需要,最好分开处理
find /var/www/html -type d -exec chmod 755 {} \;
find /var/www/html -type f -exec chmod 644 {} \;
注意 chmod -R 755 目录 会把所有文件也变成可执行,这对普通文件是多余的,还可能带来安全风险。正确做法是像上面那样,目录用 755、文件用 644,分别处理。
三、chown / chgrp:改属主与属组
# 改属主和属组
sudo chown www-data:www-data /var/www/html/index.php
# 只改属主
sudo chown nginx /var/www/html/index.php
# 只改属组
sudo chgrp nginx /var/www/html/index.php
# 递归改(谨慎,先确认路径和 -R 的作用范围)
sudo chown -R www-data:www-data /var/www/html
# 用 --reference 参照另一个文件的属主权限
sudo chown --reference=/var/www/html/index.php /var/www/html/new.php
chown -R 是有风险的操作:如果路径写成 / 或写错挂载点,会改动大量文件的属主,可能导致系统服务无法启动。执行前用 ls -ld 确认目标,并优先限定到具体目录。
四、umask:新建文件的默认权限
新建文件之所以权限是 644 而不是 666,新建目录是 755 而不是 777,都是 umask 在起作用。默认权限 = 基准权限 减去 umask。
- 文件基准是 666(不给执行位),目录基准是 777。
- 常见 umask 是
022:文件 666-022=644,目录 777-022=755。 umask 027:文件 640,目录 750,适合更严格的共享环境。
# 查看当前 umask
umask
# 临时设置(当前会话有效)
umask 027
# 永久设置(用户级)
echo "umask 027" >> ~/.bashrc
# 系统级(RHEL/CentOS/Rocky)
sudo vi /etc/profile # 或 /etc/bashrc
# 系统级(Debian/Ubuntu)
sudo vi /etc/login.defs # 或 /etc/profile
五、特殊权限位:SUID、SGID、Sticky
除了 rwx,还有三个特殊位,它们用 s 和 t 表示:
| 特殊位 | 数字 | 作用对象 | 效果 |
|---|---|---|---|
| SUID | 4000 | 文件 | 执行时以文件属主身份运行(如 passwd) |
| SGID | 2000 | 文件 | 执行时以文件属组身份运行 |
| SGID | 2000 | 目录 | 目录内新建文件继承该目录的属组(共享目录常用) |
| Sticky | 1000 | 目录 | 只有文件属主/目录属主/root 能删除(如 /tmp) |
# 设置 SUID
chmod u+s /usr/local/bin/mytool
# 目录设置 SGID:组内新建文件自动继承目录属组,协作目录必备
sudo chmod 2775 /data/team
# 或
sudo chmod g+s /data/team
# 目录设置 Sticky:防止互相删除对方文件
sudo chmod 1777 /data/shared
# 或
sudo chmod +t /data/shared
# 查找系统中的 SUID/SGID 文件(安全审计常用,排查被植入的后门)
sudo find / -perm -4000 -type f 2>/dev/null
sudo find / -perm -2000 -type f 2>/dev/null
SGID 目录是解决"团队共享目录"的利器:多人往同一目录写文件,如果不设 SGID,每个文件属组是创建者自己的主组,别人可能读不到;设了 SGID 之后,所有新文件自动归到目录属组,权限就统一了。
六、ACL:传统权限不够用时的方案
传统权限只能写"一个属主 + 一个属组",如果场景是"这个目录要允许 alice 和 bob 写,carol 只读,其他人不能访问",传统三位权限就表达不了。这时用 ACL。
前提:文件系统挂载时开启了 ACL(现代发行版默认开启,ext4 和 XFS 都支持)。检查方法:
# 查看挂载选项是否包含 acl
findmnt -o OPTIONS /data | grep acl
# 若没有,需在 /etc/fstab 的挂载选项里加上 acl 后重新挂载
# 安装 ACL 工具(多数系统自带)
sudo yum install -y acl # RHEL/CentOS/Rocky
sudo apt install -y acl # Debian/Ubuntu
# 查看 ACL
getfacl /data/project
# 给用户 alice 读写权限
sudo setfacl -m u:alice:rw /data/project
# 给用户组 dev 读执行权限
sudo setfacl -m g:dev:rx /data/project
# 递归设置,并设置默认 ACL(目录内新建文件自动继承)
sudo setfacl -R -m u:alice:rwX /data/project
sudo setfacl -d -m u:alice:rwX /data/project
# 删除某条 ACL
sudo setfacl -x u:alice /data/project
# 清空所有扩展 ACL
sudo setfacl -b /data/project
setfacl -R 里的 X(大写)很实用:它表示"只给目录和本来就有执行权限的文件加 x",避免把普通文件误设成可执行。
用 getfacl 看时,如果输出里有 mask 和额外的 user:alice:rw- 行,说明 ACL 生效了。注意 mask 会限制 ACL 条目的最大权限:如果 mask 是 r--,即使你给 alice 设了 rw,实际也只剩 r。这是"设了 ACL 却不生效"最常见的原因,需要同时调整 mask 或用 setfacl -m m:rw 修正。
七、典型场景的权限方案
Web 站点目录(Nginx + PHP-FPM):
# 目录 755、文件 644、属主 root、属组 web 用户
sudo chown -R root:www-data /var/www/html
sudo find /var/www/html -type d -exec chmod 755 {} \;
sudo find /var/www/html -type f -exec chmod 644 {} \;
# 需要写的目录(上传、缓存)单独授权
sudo chown -R www-data:www-data /var/www/html/uploads
sudo chmod 755 /var/www/html/uploads
协作共享目录:
sudo mkdir -p /data/team
sudo chown root:devteam /data/team
sudo chmod 2775 /data/team # SGID:新建文件继承 devteam 组
sudo setfacl -d -m g:devteam:rwx /data/team # 默认 ACL 兜底
SSH 目录与密钥:权限不对 SSH 会直接拒绝使用密钥,这是硬性要求。
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
临时交换目录(类似 /tmp):
sudo chmod 1777 /data/tmp # Sticky 位,防止互删
风险提示:
chmod 777会让任何用户(包括被入侵的低权限进程)都能读写甚至替换文件,是提权攻击的常见跳板,生产环境应避免。chown -R写错路径可能破坏系统文件属主导致无法登录。setfacl -R会递归改动大量文件。这些操作建议先用--dry-run(部分工具支持)或先在ls -ld确认目标,重要操作前做快照。回滚方式:权限可用备份的getfacl输出通过setfacl --restore恢复,属主改动无系统级回滚,只能逐项恢复。
八、几个高频误区的澄清
在收尾前,把几个新手最容易搞混的点集中澄清一下,能省下不少排查时间。
误区一:文件属主是我,我就一定能删。 不对。删除文件看的是"所在目录"的写权限。在 Sticky 目录(如 /tmp)里,即使目录对所有人可写,也只有文件属主、目录属主和 root 能删。很多人觉得"Sticky 位很矛盾",其实它恰恰是为了在"人人可写"的临时目录里保护各人文件不被互删。
误区二:chmod 777 能一劳永逸。 短期看能消掉报错,但等于对所有用户、包括被入侵的低权限进程放开了写权限。更糟的是,如果这个目录里有可被 Web 访问的脚本,777 还给了攻击者写入并执行代码的机会。遇到权限报错,正确姿势是"先查清楚访问者是谁、需要什么权限",再做精确授权。
误区三:看到 Permission denied 就只查最终文件。 很多时候卡在路径中间的某一级目录(缺 x),最终文件的权限反而是对的。这时 namei -l 比一层层 ls 高效得多。
误区四:服务报权限错,就改运行用户的权限。 更常见的原因是服务运行用户和文件属主不匹配。比如文件属主是 root、属组是 www-data,而 Nginx 以 www-data 运行、只需要读,这时权限 644 完全够用,根本不用改属主。定位时先确认进程以哪个用户运行,再看该用户对目标有没有权限。
误区五:改完不验证。 权限变更后应当以"实际访问者"的身份复现一次操作,比如 sudo -u www-data test -r /var/www/html/index.php,而不是用自己的 root 身份去试——root 几乎什么都能做,用它验证毫无意义。
九、排查权限问题的标准流程
遇到 Permission denied,按下面的顺序查,通常三步内定位:
# 1. 确认访问者是谁(当前用户与所属组)
id
groups
# 2. 确认目标文件的属主、属组、权限(含 ACL)
ls -l /path/to/target
getfacl /path/to/target
# 3. 确认目标所在目录链上的每一级都有 x 权限(for 目录)
namei -l /path/to/target # 逐级列出路径上每一层的权限,非常好用
# 4. 如果是服务进程报错,确认进程以哪个用户运行
ps -eo user,pid,cmd | grep nginx
# 查看服务配置里的 user 指令(Nginx 是 /etc/nginx/nginx.conf)
namei -l 这个命令值得单独记住:它会沿着路径把每一级目录的权限都列出来。很多时候问题不在最终文件,而在中间某一级目录缺少 x 权限,namei 一眼就能看出来。
常见问题
Q1:ls -l 里有 + 号是什么意思?
表示该文件设有扩展 ACL。用 getfacl 查看具体条目。
Q2:为什么我能读文件但不能 cd 进它的目录?
目录缺 x 权限。r 只允许列目录,x 才允许进入和访问目录内文件。
Q3:设了 ACL 却不生效?
检查 mask。mask 会限制所有 ACL 条目的权限上限,需要 setfacl -m m:rwx 调整,或注意 setfacl 是否会自动重算 mask。
Q4:chmod 777 能解决权限问题吗?
能"解决"报错,但同时也把安全边界拆了。正确做法是找到真正需要的用户/组,精确授权(chmod 或 setfacl),而不是放开给所有人。
Q5:数据库目录应该给什么权限?
数据库数据目录一般设为 700 且属主为数据库运行用户(如 mysql),不允许其他用户访问,因为里面有业务数据。
总结
- 权限问题只需回答两件事:访问者是谁,目标权限位是否允许。
id+ls -l+getfacl三件套最快定位。 - 目录的
r是列内容、x是进入、w是增删文件;删除文件看的是目录写权限,不是文件本身。 - 目录用 755、文件用 644、私钥用 600,是最通用的安全基线,避免无脑 777。
- 团队共享目录用 SGID(
chmod 2775)让新文件继承组;公共交换目录用 Sticky(chmod 1777)防互删。 - 传统权限表达不了的"给指定用户单独授权"用 ACL,别忘检查 mask 是否限制了实际权限。
namei -l能逐级展示路径权限,是排查"到底是哪一级卡住"的高效工具。
企业QQ咨询




