权限与用户管理
第6章:权限与用户管理
Linux 的权限体系是其多用户设计的基石。从最基础的 rwx 三位权限,到 SetUID/SetGID 特殊位,再到 POSIX ACL 的细粒度控制,最终到 Linux capabilities 对 root 全能的精细拆分——每一层都有其存在的理由。本章从内核视角讲清每个机制的工作原理,帮助你写出安全的系统配置,理解为什么生产环境中权限配置失当是最常见的安全漏洞来源之一。
1. 权限模型基础:rwx 三组
ls -l 输出解读
$ ls -l /etc/passwd
-rw-r--r-- 1 root root 2847 Apr 10 12:00 /etc/passwd
│└┬┘└┬┘└┬┘ │ └──┘ └──┘
│ │ │ │ │ 所有者 所属组
│ │ │ │ 硬链接数
│ │ │ └── other 权限(其他用户)
│ │ └───── group 权限(所属组成员)
│ └──────── user 权限(所有者)
└────────── 文件类型:- 普通文件 / d 目录 / l 符号链接
/ c 字符设备 / b 块设备 / p 管道 / s 套接字
权限位含义
| 权限 | 字母 | 八进制值 | 对文件的含义 | 对目录的含义 |
|---|---|---|---|---|
| 读 | r | 4 | 可以读取文件内容(cat/less) | 可以列出目录内容(ls) |
| 写 | w | 2 | 可以修改文件内容 | 可以在目录中创建/删除文件 |
| 执行 | x | 1 | 可以作为程序执行 | 可以进入目录(cd)并访问其中文件 |
| 无权限 | - | 0 | 无对应权限 | 无对应权限 |
# 八进制权限速查表
# rwx = 4+2+1 = 7
# rw- = 4+2+0 = 6
# r-x = 4+0+1 = 5
# r-- = 4+0+0 = 4
# -wx = 0+2+1 = 3
# -w- = 0+2+0 = 2
# --x = 0+0+1 = 1
# --- = 0+0+0 = 0
# 常见权限组合含义
755 → rwxr-xr-x 普通可执行文件/目录(所有人可读可进入,仅owner可写)
644 → rw-r--r-- 普通配置文件(所有人可读,仅owner可写)
600 → rw------- SSH 私钥、数据库密码文件
640 → rw-r----- 敏感配置(同组可读)
777 → rwxrwxrwx 危险!任何人可读写执行,生产环境禁止使用
700 → rwx------ 用户私有脚本/目录
750 → rwxr-x--- 程序目录(同组可读执行,其他人无权限)
2. chmod:修改权限
# 八进制模式(最常用)
chmod 755 script.sh # 设置为 rwxr-xr-x
chmod 644 config.conf # 设置为 rw-r--r--
chmod 600 ~/.ssh/id_rsa # SSH 私钥必须是 600
chmod 777 /tmp/shared # 危险,避免使用
# 符号模式(更直观,适合脚本中细粒度调整)
chmod u+x script.sh # 给所有者加执行权限
chmod g-w file.txt # 去掉组写权限
chmod o=r file.txt # 设置 other 权限为只读
chmod a+r file.txt # 所有人加读权限(a = all = ugo)
chmod u+x,g+r,o-x file.sh # 组合操作
# 递归修改(-R)
chmod -R 755 /var/www/html/ # 递归设置目录
chmod -R u+X /srv/data/ # 大写 X:仅对已有执行位的文件,或目录加执行
# 参考另一个文件的权限
chmod --reference=/etc/passwd /etc/shadow_backup
大写 X 的妙用:
chmod -R a+X dir/中的大写X只对目录和已具有执行权限的文件添加执行位,不会把普通文本文件变成可执行。这是批量修复目录权限时的标准用法。
3. chown / chgrp
# chown:修改所有者(和可选的所属组)
chown alice file.txt # 只改所有者
chown alice:developers file.txt # 同时改所有者和组
chown :developers file.txt # 只改组(等同 chgrp)
chown alice: file.txt # 改所有者,组设为 alice 的主组
# 递归修改
chown -R www-data:www-data /var/www/html/
chown -R nginx: /etc/nginx/
# 参考另一个文件的所有者
chown --reference=/etc/passwd /etc/newfile
# chgrp:专门修改所属组
chgrp developers project/
chgrp -R docker /var/run/docker.sock # 让 docker 组可以使用 socket
4. 特殊权限位:SetUID / SetGID / 粘滞位
SetUID(SUID):以所有者身份执行
当文件设置了 SUID 位,任何用户执行该文件时,进程的有效 UID(eUID)会暂时变为文件所有者(通常是 root)。passwd 命令正是利用这一机制让普通用户修改 /etc/shadow。
# 查看 SUID 文件
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root ... /usr/bin/passwd
# ^── s 表示 SUID 已设置(原位置是 x)
# 设置 SUID(八进制前加 4)
chmod 4755 /usr/local/bin/mytool # rwsr-xr-x
chmod u+s /usr/local/bin/mytool # 符号方式
# 查找系统中所有 SUID 文件(安全审计)
find / -perm -4000 -type f 2>/dev/null
SetGID(SGID):目录继承组
对目录设置 SGID 后,目录内新建的文件会自动继承目录的所属组,而非创建者的主组。这对团队共享目录非常有用。对可执行文件设置 SGID 则类似 SUID,进程的有效 GID 变为文件所属组。
# 为共享目录设置 SGID
mkdir /srv/team-files
chown :developers /srv/team-files
chmod 2775 /srv/team-files # 2 = SGID
# drwxrwsr-x → 新文件自动属于 developers 组
chmod g+s /srv/team-files # 符号方式
# 查找系统中所有 SGID 文件
find / -perm -2000 -type f 2>/dev/null
粘滞位(Sticky Bit):/tmp 目录的秘密
对目录设置粘滞位后,目录中的文件只有文件所有者或 root 才能删除,即使其他用户对目录有写权限。/tmp 就是这样工作的——所有人可以创建文件,但不能删除别人的文件。
ls -ld /tmp
# drwxrwxrwt ... ← t 表示粘滞位(原 x 位置)
chmod 1777 /tmp # 1 = 粘滞位
chmod +t /shared-dir # 符号方式
5. umask:默认权限掩码
umask 决定新建文件和目录的默认权限。计算公式:实际权限 = 最大权限 - umask。文件最大权限是 666(不允许默认可执行),目录最大权限是 777。
# 查看当前 umask
umask # 输出如 0022
umask -S # 符号形式:u=rwx,g=rx,o=rx
# umask 计算示例
# umask=022:文件=666-022=644,目录=777-022=755(最常见)
# umask=027:文件=666-027=640,目录=777-027=750(安全服务常用)
# umask=077:文件=666-077=600,目录=777-077=700(最严格,home dir)
# 临时设置 umask
umask 027
# 永久设置(写入 .bashrc 或 /etc/profile)
echo "umask 022" >> ~/.bashrc
# 验证效果
umask 027
touch newfile.txt && ls -l newfile.txt # 应为 -rw-r-----
mkdir newdir && ls -ld newdir # 应为 drwxr-x---
6. sudo 完全指南
/etc/sudoers 格式
# 始终使用 visudo 编辑 sudoers(语法检查,防止锁死)
visudo
visudo -f /etc/sudoers.d/myapp # 编辑独立文件
# sudoers 基本语法:
# 用户 主机=(运行身份) 命令
# 示例(写入 /etc/sudoers 或 /etc/sudoers.d/ 下的文件)
# alice 在所有主机上,以 root 身份执行所有命令,需要密码
alice ALL=(ALL:ALL) ALL
# bob 不需要密码执行 systemctl restart nginx
bob ALL=(root) NOPASSWD: /bin/systemctl restart nginx
# developers 组可以执行所有命令(需要密码)
%developers ALL=(ALL) ALL
# 使用 Cmnd_Alias 将多个命令分组
Cmnd_Alias WEBOPS = /bin/systemctl restart nginx, /bin/systemctl reload nginx, /usr/bin/tail -f /var/log/nginx/*.log
webteam ALL=(root) NOPASSWD: WEBOPS
# 限制 sudo 只能在特定主机使用
alice webserver01=(root) ALL
# 查看自己的 sudo 权限
sudo -l
安全警告: 永远不要在 sudoers 中给予对 vim/less/more/find/awk/python 等工具的无限制 NOPASSWD 权限——这些工具可以轻易 shell 逃逸(在 vim 中执行
:!/bin/bash)从而获得完整 root 权限,称为 GTFOBins 漏洞。
# sudo 常用用法
sudo command # 以 root 执行
sudo -u postgres psql # 以 postgres 用户执行
sudo -i # 切换到 root 的交互式 login shell
sudo -s # 切换到 root 的非 login shell
sudo -l # 列出当前用户的 sudo 权限
sudo -l -U alice # 列出 alice 的 sudo 权限(需要 root)
sudo -k # 清除缓存的凭证(下次需要重新输密码)
sudo -v # 刷新凭证(重置超时计时器)
7. su vs sudo
| 对比点 | su | sudo |
|---|---|---|
| 需要的密码 | 目标用户的密码 | 自己的密码 |
| 审计日志 | 有限(/var/log/auth.log) | 详细(/var/log/sudo.log) |
| 最小权限 | 切换到完整用户权限 | 可精细控制允许的命令 |
| 共享密码 | root 密码需要被多人知道 | 每人用自己密码 |
| 推荐程度 | 不推荐(现代运维已淘汰) | 推荐 |
# su - 与 su 的区别:
su postgres # 切换用户,但保留当前环境变量(PATH 等)
su - postgres # 切换用户,同时加载目标用户的完整环境(login shell)
# 以特定用户执行单条命令
su -c "pg_dump mydb" postgres
# sudo 等价方式(推荐)
sudo -u postgres pg_dump mydb # 无需知道 postgres 密码
8. 用户管理
/etc/passwd 与 /etc/shadow 文件格式
# /etc/passwd(所有人可读)
# 格式:用户名:密码占位:UID:GID:备注:家目录:Shell
alice:x:1001:1001:Alice Smith:/home/alice:/bin/bash
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
# /etc/shadow(仅 root 可读,存储加密密码)
# 格式:用户名:加密密码:最后修改日期:...
alice:$6$salt$hash...:19100:0:99999:7:::
# /etc/group
# 格式:组名:密码:GID:成员列表
developers:x:1010:alice,bob,carol
useradd / usermod / userdel
# useradd:创建用户
useradd -m -s /bin/bash alice # -m 创建家目录,-s 设置 Shell
useradd -m -s /bin/bash -G sudo,docker alice # 加入附加组
useradd -r -s /sbin/nologin nginx # 创建系统账户(-r,无登录 Shell)
useradd -u 1500 -g developers alice # 指定 UID 和主组
# 设置密码
passwd alice # 交互式设置
echo "alice:SecurePass123" | chpasswd # 批量设置(脚本中)
openssl passwd -6 "SecurePass" | xargs -I{} passwd alice # 生成 SHA-512 hash
# usermod:修改已存在用户
usermod -aG docker alice # -a 追加到附加组(不加 -a 会替换所有组)
usermod -aG sudo alice # 加入 sudo 组
usermod -s /bin/zsh alice # 修改 Shell
usermod -l newname alice # 重命名用户
usermod -L alice # 锁定账户(密码字段加 !)
usermod -U alice # 解锁账户
# userdel:删除用户
userdel alice # 只删除账户,保留家目录
userdel -r alice # 同时删除家目录和邮箱目录
# groupadd / groupmod / groupdel
groupadd developers
groupadd -g 2000 devops # 指定 GID
groupmod -n devteam developers # 重命名组
groupdel devteam
# 查看当前用户所属组
id
groups
id alice # 查看 alice 的 UID/GID/组
9. POSIX ACL:细粒度权限控制
传统 Unix 权限只支持 owner/group/other 三组,无法表达"用户 charlie 对这个文件有读权限,但 charlie 不属于该文件的 group"。POSIX ACL 填补了这个空白。
# 安装(Debian/Ubuntu)
apt install acl
# getfacl:查看 ACL
getfacl /srv/project/
# # file: srv/project/
# # owner: alice
# # group: developers
# user::rwx ← 所有者
# group::r-x ← 所属组
# other::--- ← 其他人
# mask::rwx ← 有效权限掩码
# setfacl:设置 ACL
setfacl -m u:charlie:r-- /srv/project/readme.txt # 给特定用户读权限
setfacl -m g:ops:rwx /srv/project/ # 给特定组完整权限
setfacl -m o::--- /srv/project/ # 禁止其他人访问
setfacl -x u:charlie /srv/project/readme.txt # 删除某用户的 ACL
setfacl -b /srv/project/readme.txt # 删除所有 ACL
# 默认 ACL(目录下新建文件自动继承)
setfacl -d -m u:charlie:r-- /srv/project/ # -d 设置默认 ACL
setfacl -d -m g:ops:rwx /srv/project/
# mask:ACL 有效权限上限
# 组和命名用户的最终权限 = ACL 权限 AND mask
setfacl -m mask::r-- /srv/project/ # 所有人最多只读
# 复制 ACL 到另一个文件/目录
getfacl source-dir | setfacl --set-file=- target-dir
# ls -l 中 + 号表示文件有 ACL
ls -l /srv/project/readme.txt
# -rw-r--r--+ 1 alice developers ...
# ^── 这个 + 表示有额外 ACL
10. Linux Capabilities:精细拆分 root 权限
传统 Unix 权限非此即彼:要么是 root(全能),要么是普通用户(受限)。Linux capabilities 将 root 的特权拆分成约 40 个独立能力,可以精细授予,避免"为了绑定 80 端口就给整个程序 root 权限"的问题。
| Capability | 含义 | 典型用途 |
|---|---|---|
| CAP_NET_BIND_SERVICE | 绑定 1024 以下端口 | Web 服务器监听 80/443 |
| CAP_NET_RAW | 使用原始套接字 | ping, tcpdump |
| CAP_SYS_PTRACE | 跟踪任意进程 | strace, gdb |
| CAP_SYS_ADMIN | 大量系统管理操作 | mount, 名字空间操作 |
| CAP_DAC_OVERRIDE | 绕过文件权限检查 | 备份工具 |
| CAP_KILL | 向任意进程发信号 | 系统监控工具 |
| CAP_CHOWN | 修改任意文件的所有者 | 文件管理服务 |
| CAP_SETUID | 切换任意 UID | 需要模拟用户的服务 |
# 安装工具
apt install libcap2-bin
# getcap:查看文件的 capability
getcap /usr/bin/ping
# /usr/bin/ping cap_net_raw=ep
# e=effective p=permitted i=inherited
# setcap:授予 capability(需要 root)
# 让 node 程序可以监听 80 端口而不需要 root
setcap cap_net_bind_service=+ep /usr/bin/node
# 让自定义工具可以使用 ping(原始套接字)
setcap cap_net_raw=+ep /usr/local/bin/myping
# 移除 capability
setcap -r /usr/bin/node
# 查看进程的 capability(当前 shell)
cat /proc/$$/status | grep Cap
# CapInh: 0000000000000000
# CapPrm: 0000000000000000
# CapEff: 0000000000000000
# CapBnd: 000001ffffffffff
# CapAmb: 0000000000000000
# 解码 capability 十六进制值
capsh --decode=000001ffffffffff
# 实战:nginx 以普通用户启动监听 80 端口
useradd -r -s /sbin/nologin nginx
setcap cap_net_bind_service=+ep /usr/sbin/nginx
# nginx.conf 中 user nginx; 不再需要先以 root 启动
为什么 capabilities 比 SetUID 更安全? SetUID root 意味着整个程序在执行期间拥有完整 root 权限——一旦程序有漏洞,攻击者可以获得全部 root 能力。Capabilities 只授予最小必要权限,即使程序被攻击,攻击者获得的权限也受到严格限制。这是最小权限原则(Principle of Least Privilege)的体现。
11. 文件属性:chattr / lsattr
即使是 root,也可以被 chattr 设置的文件属性限制。这提供了超越传统权限系统的额外保护层。
# lsattr:查看文件扩展属性
lsattr /etc/passwd
# ----i--------e-- /etc/passwd
# ^ i=immutable,不可修改
# chattr:设置/清除扩展属性
chattr +i /etc/passwd # 不可修改(连 root 也不能 rm/mv/write)
chattr -i /etc/passwd # 解除不可修改
chattr +a /var/log/app.log # 只能追加(append-only),不能覆盖/删除
chattr -a /var/log/app.log # 解除只追加
# 常用属性
# i (immutable):任何人(包括 root)都不能修改、删除、重命名、创建硬链接
# a (append-only):只能追加,不能覆盖或删除(日志文件安全保护)
# e (extent format):使用 extent 存储(ext4 默认,通常不需要手动设置)
# u (undeletable):删除时保留内容,可恢复
# 实战:保护关键系统文件
chattr +i /etc/resolv.conf # 防止被 NetworkManager 自动覆盖
chattr +i /etc/hosts # 防止误修改
chattr +a /var/log/secure # 安全日志只追加,防篡改
chattr +i 的注意事项: 设置 immutable 后,包括 root 在内的所有人都无法修改该文件,
apt upgrade等系统更新也会失败。在设置之前确认这是你的意图,并记录哪些文件设置了该属性(用lsattr -R /etc/ 2>/dev/null | grep "\-\-\-\-i"审计)。
上一章
← 第5章:进程与作业控制
下一章
第7章:网络工具全解 →