Linux权限管理是系统安全的基石,也是每位运维工程师和后端开发者的必修课。从最基础的UGO(User/Group/Others)模型到现代化的Capabilities机制,Linux权限体系经历了多次演进,每一次都旨在解决前一代模型的局限性。本文将系统性地剖析Linux权限体系的各个层级,配合大量实战命令与配置示例,帮助你在生产环境中构建更安全的权限架构。
一、传统UGO权限模型详解
UGO模型是Linux最基础的权限控制机制,每个文件和目录都有三组权限,分别对应文件所有者(User)、所属组(Group)和其他用户(Others)。每组权限包含读(r)、写(w)、执行(x)三种操作。
1.1 权限的数字与符号表示
Linux权限可以用数字或符号两种方式表示。数字表示法中,读=4、写=2、执行=1,三组权限相加后拼接成一个三位数:
1
2
3
4
5
6
7
8
9
10
11
12 # 查看文件权限
$ ls -l /etc/passwd
-rw-r--r-- 1 root root 3120 Jul 25 10:00 /etc/passwd
# 数字表示:rw-=6, r--=4, r--=4 → 644
# 修改权限为750(rwxr-x---)
$ chmod 750 /opt/myapp
# 符号表示法(更直观)
$ chmod u+rwx,g+rx,o-rwx /opt/myapp
$ chmod a-x script.sh # 所有人移除执行权限
$ chmod g+s /opt/shared # 设置SGID位
对于目录而言,权限含义略有不同:读(r)表示可以列出目录内容,写(w)表示可以在目录中创建或删除文件,执行(x)表示可以进入目录(cd)。这一点常常被新手忽略,导致权限配置不当。
1.2 所有权管理:chown与chgrp
文件的属主和属组通过chown命令修改,在容器化环境和多用户服务器中极为常用:
1
2
3
4
5
6
7
8
9 # 修改属主和属组
$ chown deploy:deploy /opt/myapp -R
# 仅修改属组
$ chgrp docker /var/run/docker.sock
# 递归修改并显示过程
$ chown -Rv www-data:www-data /var/www/html/
changed ownership of '/var/www/html/index.php' from root:root to www-data:www-data
| 场景 | 推荐权限 | 说明 |
|---|---|---|
| Web根目录 | 755 | 所有人可读,仅属主可写 |
| SSH私钥 | 600 | 仅属主可读写,sshd强制要求 |
| 配置文件(含密码) | 640 | 属主读写,属组只读 |
| 可执行脚本 | 750 | 属主可写执行,属组可执行 |
| 上传目录 | 770 | 属组可读写执行 |
二、特殊权限位:SUID、SGID与Sticky Bit
传统UGO模型在某些场景下力不从心。比如普通用户需要修改自己的密码,但密码存储在/etc/shadow中,只有root可读。SUID机制正是为此而生。
2.1 SUID(Set User ID)
当一个可执行文件设置了SUID位,无论谁执行它,进程的有效用户ID(EUID)都会变成文件属主的ID。最经典的例子就是passwd命令:
1
2
3
4
5
6
7
8
9
10
11
12 $ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 Jul 15 09:30 /usr/bin/passwd
# 注意权限位的's',这就是SUID标志
# 设置SUID
$ chmod u+s /usr/bin/myapp
# 或数字方式(在权限前加4)
$ chmod 4755 /usr/bin/myapp
# 查找系统中所有SUID文件(安全审计常用)
$ find / -perm -4000 -type f 2>/dev/null
安全警告:SUID root的程序是提权攻击的高频目标。如果程序存在命令注入或路径穿越漏洞,攻击者可以利用它获取root权限。务必定期审计SUID文件列表,移除不必要的SUID权限。
2.2 SGID(Set Group ID)
SGID应用于可执行文件时,进程的有效组ID变为文件属组。更常见的用途是应用于目录——在SGID目录中创建的新文件会自动继承目录的属组,而非创建者的主组:
1
2
3
4
5
6
7
8
9
10
11
12
13 # 创建共享项目目录
$ mkdir /opt/project-shared
$ chown :developers /opt/project-shared
$ chmod 2770 /opt/project-shared
# 验证SGID位(权限中出现's')
$ ls -ld /opt/project-shared
drwxrws--- 2 root developers 4096 Jul 25 10:00 /opt/project-shared
# 任何developers组成员在此创建的文件都自动属于developers组
$ touch /opt/project-shared/test.txt
$ ls -l /opt/project-shared/test.txt
-rw-r--r-- 1 alice developers 0 Jul 25 10:01 test.txt
这在团队协作目录中非常实用,省去了手动chgrp的麻烦。
2.3 Sticky Bit
Sticky Bit主要应用于公共可写目录,确保用户只能删除自己拥有的文件,即使对目录有写权限。/tmp目录是最典型的例子:
1
2
3
4
5
6
7
8 $ ls -ld /tmp
drwxrwxrwt 25 root root 12288 Jul 25 10:00 /tmp
# 末尾的't'就是Sticky Bit标志
# 设置Sticky Bit
$ chmod +t /shared/uploads
# 或数字方式(前缀加1)
$ chmod 1777 /shared/uploads

三、POSIX ACL:更细粒度的访问控制
传统UGO模型只有三组权限,无法满足复杂的多用户协作场景。比如需要让某个特定用户读取日志文件,但又不想开放给所有人。POSIX ACL(Access Control List)解决了这个问题,允许为单个用户或组设置独立的权限条目。
3.1 启用与查看ACL
大多数现代Linux发行版默认支持ACL。首先确认文件系统挂载时启用了acl选项(ext4/xfs通常默认开启):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 # 检查文件系统是否支持ACL
$ tune2fs -l /dev/sda1 | grep 'Default mount options'
Default mount options: user_xattr acl
# 如果没有,需要在/etc/fstab中添加acl选项并重新挂载
/dev/sda1 / ext4 defaults,acl 0 1
# 安装ACL工具(Debian/Ubuntu)
$ apt install acl
# CentOS/RHEL
$ yum install acl
# 查看文件ACL
$ getfacl /var/log/app.log
# file: var/log/app.log
# owner: root
# group: root
user::rw-
group::r--
other::---</p>
3.2 设置ACL规则
setfacl命令用于添加和修改ACL条目,这是ACL最强大的能力:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 # 给用户alice授予读写权限
$ setfacl -m u:alice:rw /var/log/app.log
# 给组developers授予只读权限
$ setfacl -m g:developers:r /var/log/app.log
# 再次查看ACL
$ getfacl /var/log/app.log
# file: var/log/app.log
# owner: root
# group: root
user::rw-
user:alice:rw-
group::r--
group:developers:r--
mask::rw-
other::---
注意输出中的
1 | mask |
行——它是ACL权限的有效上限。任何用户或组的ACL权限都不能超过mask。修改mask会影响所有ACL条目的有效权限:
1
2
3
4
5
6
7
8
9
10 # 设置mask(限制最大权限为只读)
$ setfacl -m m::r /var/log/app.log
# 此时alice的权限虽然是rw-,但有效权限降为r--
# 递归设置目录ACL(对已有文件生效)
$ setfacl -R -m g:qa:rx /opt/app/
# 设置默认ACL(新建文件自动继承)
$ setfacl -d -m g:qa:rx /opt/app/
# 之后在/opt/app/下创建的任何文件,qa组都自动拥有rx权限
默认ACL(default ACL)是目录级别的设置,新建的子文件和子目录会继承这些规则。这在多团队共享目录的场景中极为重要。
四、Linux Capabilities:精细化权限分割
SUID root程序存在根本性安全问题——它获得的是完整的root权限,而不是执行特定操作所需的最小权限。一个只需要绑定80端口的Web服务器,如果用SUID root方式运行,一旦被攻破,攻击者就拥有了整个系统的控制权。
Linux Capabilities将root权限拆分成数十个细粒度的能力单元,进程可以只获取所需的那一小部分特权,而非全部root权限。
4.1 常用Capabilities一览
| Capability | 作用 | 典型场景 |
|---|---|---|
| CAP_NET_BIND_SERVICE | 绑定1024以下端口 | Web服务器绑定80/443 |
| CAP_NET_RAW | 使用原始套接字 | ping、tcpdump |
| CAP_SYS_ADMIN | 系统管理操作(权限过大) | 挂载文件系统 |
| CAP_DAC_OVERRIDE | 绕过文件读写权限检查 | 备份软件读取受保护文件 |
| CAP_KILL | 向其他进程发送信号 | 进程管理工具 |
| CAP_CHOWN | 修改文件属主 | 特权文件操作 |
| CAP_SETUID | 切换进程UID | 认证服务 |
4.2 使用setcap赋予程序能力
1
2
3
4
5
6
7
8
9
10
11
12 # 允许nginx以普通用户身份绑定80端口
$ setcap 'cap_net_bind_service=+ep' /usr/sbin/nginx
# 允许某个抓包工具使用原始套接字
$ setcap 'cap_net_raw,cap_net_admin=+ep' /usr/local/bin/mycapture
# 查看程序当前的capabilities
$ getcap /usr/sbin/nginx
/usr/sbin/nginx = cap_net_bind_service+ep
# 移除capability
$ setcap -r /usr/sbin/nginx
这是替代SUID的最佳实践。以Nginx为例,传统做法是用root启动nginx主进程(绑定80端口后再drop权限给worker),而使用setcap后,整个nginx进程都可以非root身份运行。
4.3 Docker容器中的Capabilities管理
Docker默认只赋予容器一小部分capabilities,这是容器安全的重要保障。你可以根据需要添加或移除:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 # 运行容器时添加capability
$ docker run --cap-add=NET_ADMIN -d myapp
# 移除默认capability(更安全)
$ docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE -d myapp
# 查看进程的capabilities
$ cat /proc/$(pgrep -f nginx)/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000000000000400
CapEff: 0000000000000400
CapBnd: 00000000a80425fb
# 使用capsh解析capability位图
$ capsh --decode=0000000000000400
0x0000000000000400=cap_net_bind_service
在Kubernetes中,SecurityContext提供了同样的控制能力:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
containers:
- name: app
image: myapp:latest
securityContext:
runAsNonRoot: true
runAsUser: 1000
capabilities:
add: ["NET_BIND_SERVICE"]
drop: ["ALL"]
readOnlyRootFilesystem: true

五、生产环境权限安全实践
5.1 最小权限原则
最小权限原则要求每个进程和用户只拥有完成任务所需的最少权限。具体实践包括:
- 服务专用用户:每个服务创建独立的系统用户,禁止使用root运行应用进程。例如Nginx使用www-data,MySQL使用mysql用户。
- 权限分离:运维操作使用sudo而非直接root登录,并通过sudoers配置限制可执行的命令。
- 文件系统隔离:不同服务的数据目录权限严格隔离,避免横向渗透。
- 定期审计:定期检查SUID/SGID文件、异常权限变更、sudo使用记录。
5.2 sudoers安全配置
1
2
3
4
5
6
7
8
9
10
11
12
13 # 编辑sudoers(务必使用visudo)
$ visudo
# 允许deploy用户重启nginx,无需密码
deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart nginx
# 允许ops组执行特定命令,需要密码
%ops ALL=(root) /usr/bin/systemctl restart app*, /usr/bin/journalctl -u app*
# 禁止使用sudo su/sudo bash等提权操作
Defaults !root_sudo
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Defaults use_pty # 强制分配伪终端,防日志篡改
5.3 权限审计脚本
以下是一个实用的权限审计脚本,可定期运行以发现安全隐患:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 #!/bin/bash
# security-audit.sh - 定期权限审计
REPORT="/var/log/security-audit-$(date +%Y%m%d).log"
echo "=== SUID/SGID 文件检查 ===" > $REPORT
find / -perm -4000 -type f 2>/dev/null >> $REPORT
find / -perm -2000 -type f 2>/dev/null >> $REPORT
echo -e "\n=== 全局可写目录检查 ===" >> $REPORT
find / -writable -type d 2>/dev/null | grep -v '^/proc\|^/sys\|^/dev\|^/tmp' >> $REPORT
echo -e "\n=== 空密码用户检查 ===" >> $REPORT
awk -F: '($2==""){print $1}' /etc/shadow >> $REPORT
echo -e "\n=== SSH配置检查 ===" >> $REPORT
grep -E 'PermitRootLogin|PasswordAuthentication|AllowUsers' /etc/ssh/sshd_config >> $REPORT
echo -e "\n=== 异常cron任务 ===" >> $REPORT
for user in $(cut -f1 -d: /etc/passwd); do
crontab -u $user -l 2>/dev/null | grep -v '^#' | grep -v '^$' >> $REPORT
done
echo "Audit complete: $REPORT"
5.4 文件完整性监控
对于关键系统文件,建议使用AIDE(Advanced Intrusion Detection Environment)进行完整性监控:
1
2
3
4
5
6
7
8
9
10
11
12 # 安装AIDE
$ apt install aide
# 初始化数据库(首次运行,建立基线)
$ aideinit
$ cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# 定期检查(对比基线,发现篡改)
$ aide --check
# 更新基线(确认变更合法后)
$ aide --update
六、总结
Linux权限体系是一个多层次的安全框架,每一层都在弥补前一层的不足:
- UGO模型提供了基础的属主/属组/其他权限分离,适用于简单场景。
- 特殊权限位(SUID/SGID/Sticky)解决了特权程序执行和共享目录管理的问题,但也引入了安全风险。
- POSIX ACL突破了三组权限的限制,支持为单个用户或组设置独立权限,是团队协作场景的利器。
- Capabilities将root权限精细化拆分,是现代容器安全和最小权限实践的核心机制。
在生产环境中,建议遵循「最小权限+纵深防御」的原则:服务以专用用户运行、用setcap替代SUID、用ACL实现精细授权、用sudoers限制管理操作、定期审计权限配置。这些措施组合起来,能显著降低系统被入侵后的横向移动风险。权限管理不是一次性配置,而是需要持续维护和审计的动态过程——理解每一层机制的原理,才能在面对真实威胁时做出正确的决策。
汤不热吧