Linux 文件权限与所有权:chmod 到底在改什么

「chmod 777 一下就好了」是服务器上最常见的危险操作。理解了权限模型,你就知道该给什么、不该给什么。

一、rwx 是什么

bash
ls -l index.html
# -rw-r--r-- 1 www-data www-data 3166 Sep 17 10:00 index.html
# ↑↑↑↑↑↑↑↑↑
# ││││││││└─ 其他用户 other:r--
# ││││││└── 同组 group:r--
# │││└──── 所有者 user:rw-
# └└─────── 文件类型:- 普通文件,d 目录,l 软链

三组,每组三个字符:

分组作用对象
user (u)文件所有者
group (g)所属用户组
other (o)其他所有人

对文件r 能读内容,w 能改内容,x 能当程序执行。 对目录r 能列出里面有啥,w 能增删文件,x 能「进入」这个目录。

目录必须有 x。只有 r 没有 x,你能看到文件名但打不开,会报 Permission denied —— 这是 403 的常见原因之一。

二、数字表示法

把 rwx 当成三位二进制:

权限二进制
---0000
--x0011
-w-0102
-wx0113
r--1004
r-x1015
rw-1106
rwx1117

所以 chmod 644 file = 所有者 rw-、组 r--、其他 r--;chmod 755 dir = 所有者 rwx、其他 r-x。

常用组合:

模式适用
644普通文件(配置、网页)
755目录、可执行脚本
600私钥、含密码的配置文件
700私人脚本目录
777永远不要用

三、chmod 与 chown

两个完全不同的东西:

bash
chmod  修改「权限位」(rwx)
chown  修改「归谁所有」(user:group)

# 改权限
chmod 644 index.html
chmod u+x deploy.sh          # 只给所有者加执行位
chmod -R 755 /var/www/baize  # 递归改目录

# 改归属
sudo chown www-data:www-data /var/www/baize
sudo chown -R baize:baize /home/baize/.ssh
sudo chgrp www-data /var/www/baize   # 只改组

符号写法 u+x / g-w / o=r 比数字更精确,不会误伤其他位。递归 -R 前先想清楚:目录要 755、文件要 644,一把 chmod -R 755 会让所有文件都变成可执行。

四、网站目录的标准配置

bash
# 目录 755,文件 644,属主 www-data
find /var/www/baize -type d -exec chmod 755 {} \;
find /var/www/baize -type f -exec chmod 644 {} \;
sudo chown -R www-data:www-data /var/www/baize

# 需要写入的目录(上传、缓存)单独放宽
chmod 775 /var/www/baize/uploads

五、umask:新建文件的默认权限

bash
umask          # 查看,常见 022
umask 0022     # 设置

算法是「满权限减去 umask」:文件满权限 666、目录 777。

umask新建文件新建目录
022644755
002664775
077600700

写进 ~/.bashrc 只对当前用户生效;要全局生效写 /etc/profile

六、三个特殊位

数字前缀作用
setuid4执行时以所有者身份运行(如 passwd
setgid2目录下新建文件继承组;执行时以组身份运行
sticky1目录下只有文件所有者能删自己的文件(如 /tmp
bash
chmod 2755 /srv/shared      # setgid,协作目录常用
chmod 1777 /tmp             # sticky
ls -ld /tmp                 # drwxrwxrwt  末尾的 t 就是 sticky

# 找出系统上所有 setuid 文件(安全审计会看)
find / -perm -4000 -type f 2>/dev/null

七、排错:Permission denied 怎么查

bash
# 逐级看路径上每一层的权限(这个命令很好用)
namei -l /var/www/baize/index.html

# 确认 nginx 以哪个用户跑
grep "^user" /etc/nginx/nginx.conf
ps aux | grep nginx | head -3

# 以目标用户身份测试能否读取
sudo -u www-data test -r /var/www/baize/index.html && echo OK || echo FAIL

# SELinux/AppArmor 也会挡(Debian 默认不开 SELinux)
getenforce

ACL(访问控制列表) 用于更细粒度的授权:

bash
setfacl -m u:baize:rwx /srv/shared   # 单独给某人权限
getfacl /srv/shared

记住三条就够:文件 644、目录 755、密钥 600。剩下的都是这几条的变体。