说实话,我在运维这行摸爬滚打了十年,见过太多因为权限配置不当导致的生产事故。记得刚入行那会儿,有一次凌晨三点被电话吵醒,线上Web服务全挂了。折腾了两个小时才发现,是新来的同事部署代码时随手敲了个 chmod 777 ,结果被安全扫描器识别为高危漏洞,触发了自动隔离机制。
从那以后,我就特别重视权限管理这块。今天这篇文章,我打算把这些年踩过的坑、总结的经验,一次性讲透。不管你是刚入门的新人,还是想系统梳理知识的老手,相信都能有所收获。
一、概述
1.1 背景介绍:为什么权限管理如此重要
在聊具体技术之前,我想先分享一个真实的安全事故。
2024年底,某知名电商平台发生了一起严重的数据泄露事件。事后调查发现,攻击者利用的并不是什么高深的0day漏洞,而是一个再普通不过的权限配置错误:运维人员在部署新服务时,为了图省事,把数据库备份目录设成了755,而且备份文件的属主还是www-data。攻击者通过Web漏洞拿到webshell后,直接就能读取到完整的数据库备份。
这种事故我见得太多了。权限管理看似简单,实际上是Linux安全体系的基石。配置得好,能构建起坚固的防线;配置不当,就是给攻击者开后门。
1.2 Linux权限体系的演进
Linux的权限体系不是一成不变的,这些年一直在进化:
第一代:传统UGO权限(1970年代)
Unix诞生之初就有的机制,User-Group-Others三层权限模型。简单直接,但灵活性不足。
第二代:特殊权限位(1980年代)
SUID、SGID、Sticky Bit的引入,解决了一些特定场景的需求,但也带来了新的安全风险。
第三代:ACL访问控制列表(1990年代末)
POSIX ACL的引入让权限控制更加精细,可以针对特定用户或组设置权限。
第四代:强制访问控制(2000年代)
SELinux、AppArmor等MAC机制的出现,提供了更强大的安全隔离能力。
第五代:容器与云原生权限(2015年至今)
Namespace、Capabilities、Seccomp等技术的组合应用,适应了容器化和微服务架构的需求。
到了2025年,我们在实际工作中往往需要综合运用这些技术。单纯依赖某一种机制已经不够用了。
1.3 适用场景
这篇文章的内容适用于以下场景:
- Web服务器的目录和文件权限规划
- 数据库服务的安全加固
- 多用户开发环境的权限隔离
- 容器环境下的权限控制
- 合规审计中的权限检查
- CI/CD流程中的权限自动化配置
1.4 环境要求
本文的示例基于以下环境,但大部分内容适用于所有主流Linux发行版:
操作系统:Ubuntu 24.04 LTS / Rocky Linux 9 / Debian 12
内核版本:6.x+
文件系统:ext4 / xfs(支持ACL)
必备工具:coreutils, acl, policycoreutils(SELinux相关)
在开始之前,确认你的系统已经安装了必要的工具:
# Debian/Ubuntu
sudo apt update && sudo apt install -y acl attr policycoreutils
# RHEL/Rocky/AlmaLinux
sudo dnf install -y acl attr policycoreutils-python-utils
二、详细步骤
2.1 准备工作:理解权限的本质
在动手配置之前,我们需要先搞清楚几个基本概念。
2.1.1 文件与目录的本质区别
很多新手会忽略这一点:同样的权限位,对文件和目录的含义是不同的。
对于文件:
- r(读):可以查看文件内容
- w(写):可以修改文件内容
- x(执行):可以作为程序运行
对于目录:
- r(读):可以列出目录内容(ls)
- w(写):可以在目录中创建、删除、重命名文件
- x(执行):可以进入目录(cd),可以访问目录中的文件
这里有个容易踩的坑:目录的x权限。假设有个目录权限是 r-- ,你能用 ls 看到里面有什么文件,但你无法 cd 进去,也无法读取里面任何文件的内容。反过来,如果只有 --x 权限,你可以 cd 进去,也可以访问已知文件名的文件,但无法用 ls 列出目录内容。
我曾经遇到过一个奇怪的问题:用户说他能看到某个目录里有一个配置文件,但 cat 的时候却报权限错误。查了半天才发现,目录有r权限但没有x权限。
2.1.2 查看当前权限
在修改权限之前,先学会查看权限:
# 查看文件权限的几种方式
ls -l filename
ls -la directory/
# 详细信息,包括ACL
ls -la filename
getfacl filename
# 查看文件的完整属性
stat filename
ls -l 的输出解读:
-rwxr-xr-x 1 root root 12345 Jan 06 10:00 script.sh
│├──┼──┼──┤ │ │ │ │ │
│ │ │ │ │ │ │ │ └─ 文件名
│ │ │ │ │ │ │ └─ 修改时间
│ │ │ │ │ │ └─ 文件大小
│ │ │ │ │ └─ 所属组
│ │ │ │ └─ 所有者
│ │ │ └─ 硬链接数
│ │ └─ Others权限(r-x = 5)
│ └─ Group权限(r-x = 5)
└─ User权限(rwx = 7)
文件类型:- 普通文件,d 目录,l 符号链接
2.1.3 权限的数字表示法
这是每个Linux用户必须熟记的换算方式:
r = 4
w = 2
x = 1
组合计算:
rwx = 4 + 2 + 1 = 7
rw- = 4 + 2 + 0 = 6
r-x = 4 + 0 + 1 = 5
r-- = 4 + 0 + 0 = 4
--- = 0 + 0 + 0 = 0
常见的权限组合:
755 = rwxr-xr-x (目录和可执行文件的常用权限)
644 = rw-r--r-- (普通文件的常用权限)
700 = rwx------ (私有目录)
600 = rw------- (私有文件,如SSH密钥)
777 = rwxrwxrwx (危险!除非你真的知道自己在做什么)
2.2 核心配置:基础权限操作
2.2.1 chmod:修改权限
chmod 有两种用法:数字模式和符号模式。
数字模式:
# 设置文件权限为755
chmod 755 script.sh
# 设置目录及其所有内容的权限
chmod -R 755 /var/www/html/
# 只修改目录权限,不影响文件
find /var/www/html -type d -exec chmod 755 {} \;
# 只修改文件权限,不影响目录
find /var/www/html -type f -exec chmod 644 {} \;
符号模式:
符号模式更直观,适合做增量修改:
# u=用户, g=组, o=其他, a=所有
# +=添加, -=删除, ==设置
# 给所有者添加执行权限
chmod u+x script.sh
# 移除其他用户的写权限
chmod o-w file.txt
# 给组添加读写权限
chmod g+rw file.txt
# 设置所有用户只读
chmod a=r file.txt
# 组合操作
chmod u=rwx,g=rx,o=r file.txt
我个人的习惯是:精确设置用数字模式,增量调整用符号模式。
2.2.2 chown:修改所有者
# 修改所有者
chown nginx /var/www/html/index.html
# 同时修改所有者和组
chown nginx:www-data /var/www/html/
# 递归修改
chown -R nginx:www-data /var/www/html/
# 只修改组(等同于chgrp)
chown :www-data /var/www/html/
# 参考另一个文件的属主设置
chown --reference=/etc/nginx/nginx.conf /var/www/html/index.html
2.2.3 chgrp:修改所属组
# 修改文件的组
chgrp developers project/
# 递归修改
chgrp -R developers project/
2.2.4 umask:默认权限掩码
umask 决定了新创建文件和目录的默认权限。这个概念很多人搞不清楚。
# 查看当前umask
umask
# 查看符号表示
umask -S
计算方式:
- 新文件默认权限 = 666 - umask
- 新目录默认权限 = 777 - umask
例如,umask为022时:
- 新文件:666 - 022 = 644(rw-r--r--)
- 新目录:777 - 022 = 755(rwxr-xr-x)
设置umask:
# 临时设置
umask 027
# 永久设置(加入profile)
echo "umask 027" >> ~/.bashrc
# 系统级设置
echo "umask 027" >> /etc/profile
安全建议: 生产服务器建议将umask设为027或077,限制组和其他用户的默认权限。
2.3 启动验证:检查权限配置是否生效
配置完权限后,务必进行验证:
# 1. 检查权限是否正确设置
ls -la /path/to/file
# 2. 用目标用户测试访问
sudo -u nginx cat /var/www/html/index.html
sudo -u nginx ls -la /var/www/html/
# 3. 检查进程的实际权限
ps aux | grep nginx
ls -l /proc/$(pgrep -o nginx)/fd/
# 4. 使用namei查看路径上的所有权限
namei -l /var/www/html/config/database.yml
namei 是个被低估的工具,它能显示路径上每一级目录的权限,特别适合排查“权限看着没问题但就是访问不了”的情况。
$ namei -l /var/www/html/config/database.yml
f: /var/www/html/config/database.yml
drwxr-xr-x root root /
drwxr-xr-x root root var
drwxr-xr-x root root www
drwxr-xr-x nginx www-data html
drwxr-x--- nginx www-data config # 问题在这里!
-rw-r----- nginx www-data database.yml
看到了吗? config 目录的权限是 drwxr-x--- ,其他用户没有任何权限。如果你的应用以其他用户身份运行,就会访问失败。
三、示例代码和配置
3.1 特殊权限位:SUID、SGID、Sticky Bit
除了基本的rwx权限,Linux还有三个特殊权限位。这块内容很重要,但也是安全事故的高发区。
3.1.1 SUID(Set User ID)
当可执行文件设置了SUID位,任何用户执行该文件时,都会以文件所有者的身份运行。
# 查看SUID文件的特征
ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 Jan 06 10:00 /usr/bin/passwd
# ^--- 注意这个s,表示设置了SUID
passwd 命令就是典型的SUID程序。普通用户需要修改 /etc/shadow 文件来更改密码,但这个文件只有root能写。通过SUID机制,普通用户执行 passwd 时会临时获得root权限。
设置和移除SUID:
# 设置SUID
chmod u+s executable
chmod 4755 executable
# 移除SUID
chmod u-s executable
chmod 0755 executable
安全风险: SUID是把双刃剑。如果一个SUID程序存在漏洞,攻击者就可以利用它提权到root。
# 查找系统中所有SUID文件(安全审计必做)
find / -perm -4000 -type f 2>/dev/null
# 查找非标准的SUID文件
find / -perm -4000 -type f -exec ls -la {} \; 2>/dev/null | grep -v -E "(passwd|sudo|ping|mount)"
真实案例: 2024年某个CVE就是利用了一个第三方SUID程序的缓冲区溢出漏洞实现提权。所以定期审计SUID文件非常重要。
3.1.2 SGID(Set Group ID)
SGID对文件和目录有不同的作用:
对可执行文件: 执行时以文件所属组的身份运行。
对目录: 在该目录下创建的新文件会继承目录的组,而不是创建者的主组。
# 查看SGID目录
ls -ld /var/mail
drwxrwsr-x 2 root mail 4096 Jan 06 10:00 /var/mail
# ^--- 注意这个s
实际应用:团队共享目录
这是SGID最常见的应用场景:
# 创建团队共享目录
mkdir /data/team-project
groupadd developers
chown root:developers /data/team-project
chmod 2775 /data/team-project
# 验证效果
sudo -u alice touch /data/team-project/alice-file
ls -l /data/team-project/alice-file
# -rw-rw-r-- 1 alice developers ...
# 注意组是developers,不是alice的主组
设置SGID:
chmod g+s directory/
chmod 2755 directory/
3.1.3 Sticky Bit
Sticky Bit主要用于共享目录,防止用户删除其他人的文件。
# 典型例子:/tmp目录
ls -ld /tmp
drwxrwxrwt 10 root root 4096 Jan 06 10:00 /tmp
# ^--- 注意这个t
/tmp 目录所有人都能写,但因为设置了Sticky Bit,用户只能删除自己创建的文件。
设置Sticky Bit:
chmod +t directory/
chmod 1777 directory/
实际应用场景:
# 创建公共上传目录
mkdir /var/uploads
chmod 1777 /var/uploads
# 用户可以上传文件,但不能删除别人的文件
3.1.4 特殊权限的数字表示
特殊权限位放在三位数字的前面,形成四位数字:
SUID = 4
SGID = 2
Sticky = 1
示例:
4755 = SUID + rwxr-xr-x
2755 = SGID + rwxr-xr-x
1777 = Sticky + rwxrwxrwx
6755 = SUID + SGID + rwxr-xr-x
3.2 ACL高级权限控制
传统的UGO权限模型只能设置一个用户和一个组的权限,这在很多场景下不够用。ACL(Access Control List)解决了这个问题。
3.2.1 ACL基础概念
ACL允许你为任意数量的用户和组设置权限。比如:
- 文件所有者是alice,有rwx权限
- bob用户有rw权限
- charlie用户只有r权限
- developers组有rw权限
- qa组只有r权限
- 其他人没有任何权限
这在传统权限模型中是无法实现的。
3.2.2 检查文件系统是否支持ACL
# 检查挂载选项
mount | grep acl
# 或者查看文件系统特性
tune2fs -l /dev/sda1 | grep -i "default mount options"
现代Linux发行版默认都启用ACL支持。如果需要手动启用:
# 临时启用
mount -o remount,acl /data
# 永久启用(编辑/etc/fstab)
/dev/sda1 /data ext4 defaults,acl 0 2
3.2.3 setfacl:设置ACL
# 为特定用户设置权限
setfacl -m u:bob:rw file.txt
# 为特定组设置权限
setfacl -m g:developers:rwx project/
# 同时设置多个ACL条目
setfacl -m u:alice:rwx,u:bob:rx,g:qa:r file.txt
# 递归设置目录
setfacl -R -m g:developers:rwx project/
# 设置默认ACL(新创建的文件会继承)
setfacl -d -m g:developers:rwx project/
# 移除特定用户的ACL
setfacl -x u:bob file.txt
# 移除所有ACL
setfacl -b file.txt
# 从文件读取ACL规则
setfacl --set-file=acl-rules.txt file.txt
3.2.4 getfacl:查看ACL
# 查看文件ACL
getfacl file.txt
# 输出示例
# file: file.txt
# owner: alice
# group: developers
# user::rw-
# user:bob:rw-
# group::r--
# group:qa:r--
# mask::rw-
# other::---
3.2.5 ACL的mask机制
这是ACL中最容易让人困惑的部分。mask定义了ACL条目的有效权限上限。
# 查看当前mask
getfacl file.txt | grep mask
# 修改mask
setfacl -m m::rx file.txt
假设bob的ACL权限是rwx,但mask是r-x,那么bob的实际有效权限是r-x。
实用技巧: 使用 -n 选项可以防止setfacl自动重新计算mask:
setfacl -n -m u:bob:rwx file.txt
3.2.6 完整的ACL配置示例
这是一个Web项目目录的完整ACL配置:
#!/bin/bash
# 项目目录ACL配置脚本
PROJECT_DIR="/var/www/myapp"
# 基础权限设置
chown -R www-data:www-data "$PROJECT_DIR"
chmod -R 750 "$PROJECT_DIR"
# 开发者组:完全访问
setfacl -R -m g:developers:rwx "$PROJECT_DIR"
setfacl -R -d -m g:developers:rwx "$PROJECT_DIR"
# 运维组:读取和执行
setfacl -R -m g:ops:rx "$PROJECT_DIR"
setfacl -R -d -m g:ops:rx "$PROJECT_DIR"
# 日志目录:运维可写
setfacl -R -m g:ops:rwx "$PROJECT_DIR/logs"
setfacl -R -d -m g:ops:rwx "$PROJECT_DIR/logs"
# 配置目录:只有特定用户可访问
setfacl -m u:deploy:rx "$PROJECT_DIR/config"
setfacl -d -m u:deploy:rx "$PROJECT_DIR/config"
# 上传目录:Web服务可写
chmod 770 "$PROJECT_DIR/uploads"
setfacl -m u:www-data:rwx "$PROJECT_DIR/uploads"
setfacl -d -m u:www-data:rwx "$PROJECT_DIR/uploads"
echo "ACL配置完成"
getfacl "$PROJECT_DIR"
3.3 实际应用案例
3.3.1 案例一:Web应用目录权限
这是我在生产环境中使用的标准配置:
#!/bin/bash
# Web应用安全权限配置
WEB_ROOT="/var/www/production"
WEB_USER="www-data"
WEB_GROUP="www-data"
DEPLOY_USER="deploy"
# 创建目录结构
mkdir -p "$WEB_ROOT"/{app,config,logs,uploads,tmp,cache}
# 基础权限:Web用户只读
chown -R "$DEPLOY_USER":"$WEB_GROUP" "$WEB_ROOT"
find "$WEB_ROOT" -type d -exec chmod 750 {} \;
find "$WEB_ROOT" -type f -exec chmod 640 {} \;
# 可执行文件(如果有)
find "$WEB_ROOT/app/bin" -type f -name "*.sh" -exec chmod 750 {} \; 2>/dev/null
# 可写目录
for dir in logs uploads tmp cache; do
chown "$WEB_USER":"$WEB_GROUP" "$WEB_ROOT/$dir"
chmod 770 "$WEB_ROOT/$dir"
done
# 敏感配置文件
chmod 640 "$WEB_ROOT/config/"*
chown "$DEPLOY_USER":"$WEB_GROUP" "$WEB_ROOT/config/"*
# 使用ACL允许Web进程读取配置
setfacl -m u:"$WEB_USER":r "$WEB_ROOT/config/"*
# 上传目录禁止执行
# 这一步很重要,防止上传webshell
chmod -R -x "$WEB_ROOT/uploads/"* 2>/dev/null
# 在Nginx配置中也要禁止执行PHP
echo "配置完成,验证中..."
ls -la "$WEB_ROOT"
getfacl "$WEB_ROOT/config"
配套的Nginx配置(禁止uploads目录执行):
location ^~ /uploads/ {
# 禁止执行任何脚本
location ~ \.(php|php5|phtml|pl|py|jsp|asp|aspx|cgi|sh)$ {
deny all;
}
# 只允许静态文件
try_files $uri =404;
}
3.3.2 案例二:数据库文件权限
数据库文件的权限配置需要特别谨慎:
#!/bin/bash
# MySQL/MariaDB 安全权限配置
MYSQL_DATA="/var/lib/mysql"
MYSQL_USER="mysql"
MYSQL_GROUP="mysql"
# 数据目录
chown -R "$MYSQL_USER":"$MYSQL_GROUP" "$MYSQL_DATA"
chmod 750 "$MYSQL_DATA"
# 数据库文件
find "$MYSQL_DATA" -type d -exec chmod 750 {} \;
find "$MYSQL_DATA" -type f -exec chmod 640 {} \;
# 配置文件
chmod 640 /etc/mysql/my.cnf
chmod 640 /etc/mysql/mysql.conf.d/*.cnf
chown root:"$MYSQL_GROUP" /etc/mysql/my.cnf
# 日志文件
chmod 640 /var/log/mysql/*.log 2>/dev/null
chown "$MYSQL_USER":"$MYSQL_GROUP" /var/log/mysql/*.log 2>/dev/null
# 备份目录(重要!)
BACKUP_DIR="/backup/mysql"
mkdir -p "$BACKUP_DIR"
chown root:backup "$BACKUP_DIR"
chmod 750 "$BACKUP_DIR"
# 备份文件应该是600权限
备份文件权限的惨痛教训:
我见过太多把数据库备份放在Web可访问目录的情况。曾经有个客户把 mysqldump 的输出直接放在 /var/www/html/backup/ ,而且权限是644。攻击者直接下载了完整的数据库。
正确的做法是:
- 备份目录不要放在Web目录下
- 备份文件权限设为600
- 使用加密备份
- 定期转移到离线存储
3.3.3 案例三:SSH密钥权限
SSH对密钥文件的权限检查非常严格:
#!/bin/bash
# SSH安全权限配置
SSH_DIR="$HOME/.ssh"
# 创建目录
mkdir -p "$SSH_DIR"
# 目录权限必须是700
chmod 700 "$SSH_DIR"
# 私钥必须是600
chmod 600 "$SSH_DIR/id_rsa"
chmod 600 "$SSH_DIR/id_ed25519"
chmod 600 "$SSH_DIR/id_ecdsa"
# 公钥可以是644
chmod 644 "$SSH_DIR/id_rsa.pub"
chmod 644 "$SSH_DIR/id_ed25519.pub"
# authorized_keys必须是600或644
chmod 600 "$SSH_DIR/authorized_keys"
# known_hosts可以是644
chmod 644 "$SSH_DIR/known_hosts"
# config文件
chmod 600 "$SSH_DIR/config"
# 验证
ls -la "$SSH_DIR"
SSH拒绝连接的常见原因:
# 权限过于宽松会被SSH拒绝
# 检查sshd日志
tail -f /var/log/auth.log | grep -i ssh
# 常见错误信息
# "Authentication refused: bad ownership or modes for file"
# "Permissions 0644 for '/home/user/.ssh/id_rsa' are too open"
3.4 SELinux权限控制
说实话,很多运维遇到SELinux的第一反应就是关掉它。但在2025年,特别是在容器化和云原生环境中,SELinux是一个非常强大的安全工具。
3.4.1 SELinux基础概念
SELinux使用强制访问控制(MAC),即使你有传统的Unix权限,也可能被SELinux拒绝。
# 查看SELinux状态
getenforce
sestatus
# 三种模式
# Enforcing:强制执行
# Permissive:只记录不阻止
# Disabled:完全禁用
3.4.2 SELinux上下文
每个文件和进程都有一个安全上下文:
# 查看文件上下文
ls -Z /var/www/html/
# -rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 index.html
# ^用户 ^角色 ^类型 ^级别
# 查看进程上下文
ps auxZ | grep nginx
类型(Type)是最重要的部分。httpd进程只能访问 httpd_sys_content_t 类型的文件。
3.4.3 常用SELinux命令
# 修改文件上下文
chcon -t httpd_sys_content_t /var/www/html/newfile.html
# 恢复默认上下文
restorecon -Rv /var/www/html/
# 查看布尔值
getsebool -a | grep httpd
# 设置布尔值
setsebool -P httpd_can_network_connect on
# 查看AVC拒绝日志
ausearch -m avc -ts recent
audit2why < /var/log/audit/audit.log
# 生成策略模块
audit2allow -a -M mymodule
semodule -i mymodule.pp
3.4.4 容器环境下的SELinux
在Docker和Kubernetes环境中,SELinux提供了额外的隔离层:
# Docker使用SELinux
docker run --security-opt label=type:container_runtime_t ...
# 挂载卷时需要正确的标签
docker run -v /data:/data:Z ... # Z表示私有标签
docker run -v /data:/data:z ... # z表示共享标签
# Kubernetes Pod配置
apiVersion: v1
kind: Pod
spec:
securityContext:
seLinuxOptions:
level: "s0:c123,c456"
3.4.5 SELinux实战:Web服务配置
#!/bin/bash
# SELinux配置:允许Nginx访问自定义目录
WEB_ROOT="/data/www"
# 设置正确的类型
semanage fcontext -a -t httpd_sys_content_t "$WEB_ROOT(/.*)?"
# 应用上下文
restorecon -Rv "$WEB_ROOT"
# 如果需要写入(上传目录)
semanage fcontext -a -t httpd_sys_rw_content_t "$WEB_ROOT/uploads(/.*)?"
restorecon -Rv "$WEB_ROOT/uploads"
# 允许Nginx连接外部网络(如反向代理)
setsebool -P httpd_can_network_connect 1
# 允许Nginx连接数据库
setsebool -P httpd_can_network_connect_db 1
# 验证配置
ls -Z "$WEB_ROOT"
getsebool httpd_can_network_connect
3.5 AppArmor权限控制
AppArmor是另一种MAC机制,在Ubuntu和Debian系统上更常见:
# 查看状态
aa-status
# 查看配置文件
ls /etc/apparmor.d/
# 将程序设为强制模式
aa-enforce /etc/apparmor.d/usr.sbin.nginx
# 将程序设为投诉模式(只记录不阻止)
aa-complain /etc/apparmor.d/usr.sbin.nginx
# 重新加载配置
apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
AppArmor配置示例:
# /etc/apparmor.d/local/usr.sbin.nginx
/var/www/myapp/ r,
/var/www/myapp/** r,
/var/www/myapp/uploads/** rw,
/var/log/nginx/ r,
/var/log/nginx/** rw,
四、最佳实践和注意事项
4.1 权限设计原则
这些年我总结了几条权限配置的核心原则:
4.1.1 最小权限原则(PoLP)
这是安全的黄金法则。任何用户、进程、程序都只应该获得完成工作所需的最小权限。
# 反面教材
chmod 777 /var/www/html/ # 绝对不要这样做
# 正确做法:分析需求,精确授权
# Web服务需要读取静态文件
find /var/www/html -type f -exec chmod 644 {} \;
# Web服务需要进入目录
find /var/www/html -type d -exec chmod 755 {} \;
# 只有上传目录需要写入
chmod 770 /var/www/html/uploads
4.1.2 分离原则
不同的职责使用不同的用户和组:
# 不好的做法:全部使用root
# 好的做法:职责分离
# Web服务运行用户
useradd -r -s /usr/sbin/nologin www-data
# 应用部署用户
useradd -r -s /bin/bash deploy
# 数据库用户
useradd -r -s /usr/sbin/nologin mysql
# 日志收集用户
useradd -r -s /usr/sbin/nologin logcollector
4.1.3 纵深防御
不要依赖单一的安全机制:
# 层层防护
# 第一层:文件权限
chmod 640 /etc/myapp/secrets.yml
chown root:myapp /etc/myapp/secrets.yml
# 第二层:ACL
setfacl -m u:deploy:r /etc/myapp/secrets.yml
# 第三层:SELinux
semanage fcontext -a -t myapp_secret_t "/etc/myapp/secrets.yml"
# 第四层:加密
# 使用SOPS或Vault管理敏感配置
4.2 常见权限配置
以下是各类服务的推荐权限配置:
# ==================== 服务类型权限参考 ====================
# SSH配置
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_*
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/config
chmod 644 ~/.ssh/*.pub
# Web服务器
# 静态文件
find /var/www -type f -exec chmod 644 {} \;
find /var/www -type d -exec chmod 755 {} \;
# 动态脚本
chmod 750 /var/www/app/scripts/*.sh
# 配置文件
chmod 640 /var/www/app/config/*
# 数据库
chmod 750 /var/lib/mysql
chmod 640 /etc/mysql/my.cnf
chmod 640 /var/lib/mysql/*
# 日志目录
chmod 750 /var/log/myapp
chmod 640 /var/log/myapp/*.log
# 证书文件
chmod 600 /etc/ssl/private/*.key
chmod 644 /etc/ssl/certs/*.crt
# Cron任务
chmod 600 /etc/crontab
chmod 700 /var/spool/cron/crontabs/*
# 系统关键文件
chmod 644 /etc/passwd
chmod 640 /etc/shadow # shadow组可读
chmod 644 /etc/group
chmod 640 /etc/gshadow
4.3 安全加固
4.3.1 查找危险权限
#!/bin/bash
# 安全审计脚本
echo "=== 查找全局可写文件 ==="
find / -xdev -type f -perm -0002 -ls 2>/dev/null
echo "=== 查找全局可写目录 ==="
find / -xdev -type d -perm -0002 ! -perm -1000 -ls 2>/dev/null
echo "=== 查找SUID文件 ==="
find / -xdev -type f -perm -4000 -ls 2>/dev/null
echo "=== 查找SGID文件 ==="
find / -xdev -type f -perm -2000 -ls 2>/dev/null
echo "=== 查找无主文件 ==="
find / -xdev \( -nouser -o -nogroup \) -ls 2>/dev/null
echo "=== 查找777权限 ==="
find / -xdev -perm -777 -ls 2>/dev/null
echo "=== 检查敏感文件权限 ==="
for file in /etc/passwd /etc/shadow /etc/group /etc/gshadow; do
ls -l "$file"
done
4.3.2 加固配置
#!/bin/bash
# 系统权限加固
# 限制root登录
chmod 700 /root
# 保护系统配置
chmod 644 /etc/sysctl.conf
chmod 644 /etc/fstab
# 保护引导加载器
chmod 600 /boot/grub/grub.cfg 2>/dev/null
chmod 600 /boot/grub2/grub.cfg 2>/dev/null
# 禁用不必要的SUID
for binary in /usr/bin/chsh /usr/bin/chfn /usr/bin/write; do
if [ -f "$binary" ]; then
chmod u-s "$binary"
fi
done
# 设置sticky bit
chmod +t /tmp
chmod +t /var/tmp
# 确保日志目录安全
chmod 750 /var/log
chown root:adm /var/log
4.4 常见错误及避坑
4.4.1 chmod -R 777的危害
我见过太多人遇到权限问题就用这个命令。这是非常危险的:
# 永远不要这样做!
chmod -R 777 /var/www/
# 为什么危险?
# 1. 任何用户都能修改文件,包括上传webshell
# 2. 攻击者可以替换可执行文件
# 3. 敏感配置暴露
# 4. 违反安全合规要求
正确的问题排查方式:
# 1. 确认是权限问题
ls -la /path/to/file
namei -l /path/to/file
# 2. 确认进程用户
ps aux | grep nginx
id www-data
# 3. 检查SELinux
getenforce
ausearch -m avc -ts recent
# 4. 精确授权
# 只给需要的权限,不要图省事
4.4.2 忘记目录的x权限
# 问题:设置了r权限但进不去目录
chmod 444 /data/config/
# 用户可以ls但无法cd进去,也无法读取里面的文件
# 正确做法
chmod 755 /data/config/ # 目录需要x权限
chmod 644 /data/config/* # 文件不需要x权限
4.4.3 递归修改时不区分文件和目录
# 错误:把所有东西都改成755
chmod -R 755 /var/www/
# 问题:普通文件不应该有执行权限
# 正确做法
find /var/www -type d -exec chmod 755 {} \;
find /var/www -type f -exec chmod 644 {} \;
4.4.4 ACL与传统权限的冲突
# 设置了ACL后,chmod可能不按预期工作
setfacl -m u:bob:rwx file.txt
chmod 600 file.txt
# 检查实际权限
getfacl file.txt
# 会发现ACL条目仍然存在,但mask被修改了
# 正确做法:同时管理基础权限和ACL
setfacl -b file.txt # 先清除ACL
chmod 600 file.txt # 再设置基础权限
五、故障排查和监控
5.1 日志查看
权限相关的问题通常会记录在系统日志中:
# 系统日志
tail -f /var/log/syslog
tail -f /var/log/messages
# 认证日志
tail -f /var/log/auth.log
# SELinux审计日志
tail -f /var/log/audit/audit.log
ausearch -m avc -ts recent
# 应用日志(以Nginx为例)
tail -f /var/log/nginx/error.log
5.2 问题排查流程
当遇到“Permission denied”时,按以下步骤排查:
#!/bin/bash
# 权限问题排查脚本
FILE_PATH="$1"
PROCESS_USER="$2"
if [ -z "$FILE_PATH" ] || [ -z "$PROCESS_USER" ]; then
echo "用法: $0 <文件路径> <进程用户>"
exit 1
fi
echo "=== 1. 检查文件是否存在 ==="
if [ ! -e "$FILE_PATH" ]; then
echo "文件不存在: $FILE_PATH"
exit 1
fi
ls -la "$FILE_PATH"
echo ""
echo "=== 2. 检查路径上的所有权限 ==="
namei -l "$FILE_PATH"
echo ""
echo "=== 3. 检查用户信息 ==="
id "$PROCESS_USER" 2>/dev/null || echo "用户 $PROCESS_USER 不存在"
echo ""
echo "=== 4. 检查ACL ==="
getfacl "$FILE_PATH" 2>/dev/null || echo "无ACL或不支持ACL"
echo ""
echo "=== 5. 检查SELinux ==="
if command -v getenforce &>/dev/null; then
echo "SELinux状态: $(getenforce)"
ls -Z "$FILE_PATH" 2>/dev/null
echo ""
echo "最近的AVC拒绝:"
ausearch -m avc -ts recent 2>/dev/null | tail -20
else
echo "SELinux未安装"
fi
echo ""
echo "=== 6. 检查AppArmor ==="
if command -v aa-status &>/dev/null; then
aa-status 2>/dev/null | head -20
else
echo "AppArmor未安装"
fi
echo ""
echo "=== 7. 检查文件属性 ==="
lsattr "$FILE_PATH" 2>/dev/null || echo "不支持扩展属性"
echo ""
echo "=== 8. 测试访问 ==="
sudo -u "$PROCESS_USER" test -r "$FILE_PATH" && echo "可读" || echo "不可读"
sudo -u "$PROCESS_USER" test -w "$FILE_PATH" && echo "可写" || echo "不可写"
sudo -u "$PROCESS_USER" test -x "$FILE_PATH" && echo "可执行" || echo "不可执行"
5.3 使用auditd监控权限变更
auditd是Linux上最强大的审计工具,可以监控文件权限变更、访问尝试等:
5.3.1 安装和配置auditd
# 安装
sudo apt install auditd audispd-plugins # Debian/Ubuntu
sudo dnf install audit # RHEL/Rocky
# 启动服务
sudo systemctl enable --now auditd
5.3.2 配置审计规则
# 编辑 /etc/audit/rules.d/audit.rules
# 监控关键配置文件的修改
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k sudo_changes
# 监控SSH配置
-w /etc/ssh/sshd_config -p wa -k sshd_config
# 监控权限修改命令
-a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -k permission_changes
-a always,exit -F arch=b64 -S chown -S fchown -S lchown -S fchownat -k ownership_changes
# 监控setfacl
-w /usr/bin/setfacl -p x -k acl_changes
# 监控敏感目录
-w /var/www/html -p wa -k webroot_changes
-w /etc/nginx -p wa -k nginx_config
# 监控sudo使用
-a always,exit -F arch=b64 -S execve -F euid=0 -k root_commands
重新加载规则:
sudo augenrules --load
sudo auditctl -l # 查看当前规则
5.3.3 查看审计日志
# 查看所有权限变更
ausearch -k permission_changes
# 查看今天的变更
ausearch -k permission_changes -ts today
# 查看特定用户的操作
ausearch -ua alice
# 查看特定文件的访问
ausearch -f /etc/shadow
# 生成审计报告
aureport --summary
aureport --file --summary
aureport --auth --summary
5.3.4 完整的审计配置示例
# /etc/audit/rules.d/90-security.rules
# 安全审计规则 - 2025版
# 删除所有已有规则
-D
# 设置缓冲区大小
-b 8192
# 配置失败模式(1=打印警告,2=panic)
-f 1
# ========== 身份和认证 ==========
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/gshadow -p wa -k identity
-w /etc/security/ -p wa -k security_config
-w /etc/pam.d/ -p wa -k pam_config
# ========== 权限变更 ==========
-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>=1000 -F auid!=4294967295 -k owner_mod
-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=4294967295 -k attr_mod
# ========== 特权操作 ==========
-w /etc/sudoers -p wa -k sudo_changes
-w /etc/sudoers.d/ -p wa -k sudo_changes
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -F auid!=4294967295 -k privileged_exec
# ========== 网络配置 ==========
-w /etc/hosts -p wa -k network_config
-w /etc/sysconfig/network -p wa -k network_config
-w /etc/network/ -p wa -k network_config
# ========== 系统启动 ==========
-w /etc/rc.local -p wa -k init
-w /etc/systemd/ -p wa -k systemd_config
# ========== 敏感命令 ==========
-w /usr/bin/wget -p x -k data_exfil
-w /usr/bin/curl -p x -k data_exfil
-w /usr/bin/nc -p x -k data_exfil
-w /usr/bin/ssh -p x -k remote_access
# ========== 使配置不可更改 ==========
-e 2
5.4 监控告警
结合Prometheus和Alertmanager构建权限监控告警系统:
# prometheus-rules.yaml
groups:
- name: permission_alerts
rules:
- alert: SuspiciousSUIDFile
expr: node_file_suid_count > 50
for: 5m
labels:
severity: warning
annotations:
summary: "检测到异常数量的SUID文件"
description: "主机 {{ $labels.instance }} 上SUID文件数量超过50个"
- alert: WorldWritableFiles
expr: node_file_world_writable_count > 10
for: 5m
labels:
severity: critical
annotations:
summary: "检测到全局可写文件"
description: "主机 {{ $labels.instance }} 上存在全局可写文件"
- alert: PermissionChangeDetected
expr: increase(audit_permission_changes_total[5m]) > 100
for: 1m
labels:
severity: warning
annotations:
summary: "检测到大量权限变更"
description: "5分钟内权限变更操作超过100次"
配合自定义的node exporter收集权限指标:
#!/bin/bash
# /opt/scripts/permission_metrics.sh
# 生成权限相关的Prometheus指标
METRICS_FILE="/var/lib/node_exporter/textfile_collector/permissions.prom"
# SUID文件计数
suid_count=$(find / -xdev -type f -perm -4000 2>/dev/null | wc -l)
echo "node_file_suid_count $suid_count" > "$METRICS_FILE"
# SGID文件计数
sgid_count=$(find / -xdev -type f -perm -2000 2>/dev/null | wc -l)
echo "node_file_sgid_count $sgid_count" >> "$METRICS_FILE"
# 全局可写文件计数
world_writable=$(find / -xdev -type f -perm -0002 2>/dev/null | wc -l)
echo "node_file_world_writable_count $world_writable" >> "$METRICS_FILE"
# 无主文件计数
noowner=$(find / -xdev \( -nouser -o -nogroup \) 2>/dev/null | wc -l)
echo "node_file_no_owner_count $noowner" >> "$METRICS_FILE"
设置cron定期运行:
# /etc/cron.d/permission-metrics
*/5 * * * * root /opt/scripts/permission_metrics.sh
六、总结
6.1 要点回顾
写到这里,我想把最核心的知识点再梳理一遍:
基础权限:
- rwx三种权限,数字表示法(4-2-1)
- 文件与目录的权限含义不同
- chmod、chown、chgrp是基本操作
- umask决定默认权限
特殊权限:
- SUID:以文件所有者身份执行
- SGID:以文件所属组身份执行,目录中新文件继承组
- Sticky Bit:防止删除他人文件
- 数字表示:4-2-1放在最前面
ACL权限:
- 突破传统UGO模型的限制
- setfacl设置,getfacl查看
- 默认ACL控制新文件权限
- 注意mask的影响
强制访问控制:
- SELinux:类型强制,上下文标签
- AppArmor:路径配置文件
- 容器环境下的权限隔离
安全原则:
- 最小权限原则
- 分离原则
- 纵深防御
- 永远不要使用777
6.2 进阶方向
如果你想进一步深入权限这个领域,可以关注以下方向:
1. Linux Capabilities
Capabilities可以把root权限拆分成更细粒度的能力,比如:
- CAP_NET_BIND_SERVICE:绑定小于1024的端口
- CAP_SYS_ADMIN:各种系统管理操作
- CAP_DAC_OVERRIDE:绕过文件权限检查
# 查看文件的capabilities
getcap /usr/bin/ping
# 设置capabilities
setcap cap_net_bind_service=+ep /usr/bin/myapp
2. Namespace隔离
Linux Namespace是容器技术的基础:
- User Namespace:用户ID映射
- Mount Namespace:文件系统隔离
- Network Namespace:网络隔离
3. Seccomp
系统调用过滤,限制进程可以使用的系统调用:
# Docker使用seccomp
docker run --security-opt seccomp=/path/to/profile.json ...
4. 不可变基础设施
在云原生时代,越来越多的系统采用只读根文件系统的设计,从架构层面解决权限问题。
6.3 参考资料
以下是我认为值得深入阅读的资料:
- 《The Linux Command Line》- William Shotts
- Red Hat Enterprise Linux Security Guide
- CIS Benchmarks for Linux
- NIST SP 800-123: Guide to General Server Security
- Linux man pages: chmod(1), chown(1), setfacl(1), semanage(8)
- Linux Kernel Documentation: security/
线上资源:
附录
附录A:命令速查表
┌─────────────────────────────────────────────────────────────────────┐
│ 权限管理命令速查表 │
├─────────────────────────────────────────────────────────────────────┤
│ 查看权限 │
│ ls -l file 显示文件权限 │
│ ls -la dir/ 显示目录内容(包括隐藏文件) │
│ ls -Z file 显示SELinux上下文 │
│ stat file 显示文件详细信息 │
│ namei -l /path/to/file 显示路径各级权限 │
│ getfacl file 显示ACL │
├─────────────────────────────────────────────────────────────────────┤
│ 修改权限 │
│ chmod 755 file 数字模式设置权限 │
│ chmod u+x file 符号模式添加权限 │
│ chmod -R 755 dir/ 递归修改 │
│ chown user:group file 修改所有者和组 │
│ chgrp group file 修改组 │
├─────────────────────────────────────────────────────────────────────┤
│ ACL操作 │
│ setfacl -m u:user:rwx file 为用户设置ACL │
│ setfacl -m g:group:rx file 为组设置ACL │
│ setfacl -d -m u:user:rwx dir/ 设置默认ACL │
│ setfacl -x u:user file 移除用户ACL │
│ setfacl -b file 移除所有ACL │
│ setfacl -R -m ... 递归设置ACL │
├─────────────────────────────────────────────────────────────────────┤
│ 特殊权限 │
│ chmod u+s file 设置SUID │
│ chmod g+s dir/ 设置SGID │
│ chmod +t dir/ 设置Sticky Bit │
│ chmod 4755 file SUID + 755 │
│ chmod 2755 dir/ SGID + 755 │
│ chmod 1777 dir/ Sticky + 777 │
├─────────────────────────────────────────────────────────────────────┤
│ SELinux │
│ getenforce 查看SELinux状态 │
│ setenforce 0/1 临时切换模式 │
│ chcon -t type file 修改上下文类型 │
│ restorecon -Rv path 恢复默认上下文 │
│ semanage fcontext 管理文件上下文策略 │
│ setsebool -P bool on 设置布尔值 │
│ ausearch -m avc 查看AVC日志 │
├─────────────────────────────────────────────────────────────────────┤
│ 审计 │
│ auditctl -l 列出审计规则 │
│ ausearch -k keyword 按关键字搜索 │
│ aureport --summary 生成审计报告 │
│ augenrules --load 重载审计规则 │
├─────────────────────────────────────────────────────────────────────┤
│ 查找危险权限 │
│ find / -perm -4000 查找SUID文件 │
│ find / -perm -2000 查找SGID文件 │
│ find / -perm -0002 查找全局可写文件 │
│ find / -perm -777 查找777权限 │
│ find / -nouser 查找无主文件 │
└─────────────────────────────────────────────────────────────────────┘
附录B:权限数字参考表
┌─────────────────────────────────────────────────────────────────────┐
│ 权限数字对照表 │
├───────┬──────────┬─────────────────────────────────────────────────┤
│ 数字 │ 符号 │ 说明 │
├───────┼──────────┼─────────────────────────────────────────────────┤
│ 0 │ --- │ 无权限 │
│ 1 │ --x │ 执行 │
│ 2 │ -w- │ 写入 │
│ 3 │ -wx │ 写入+执行 │
│ 4 │ r-- │ 读取 │
│ 5 │ r-x │ 读取+执行 │
│ 6 │ rw- │ 读取+写入 │
│ 7 │ rwx │ 读取+写入+执行 │
├───────┴──────────┴─────────────────────────────────────────────────┤
│ 常用权限组合 │
├───────┬──────────┬─────────────────────────────────────────────────┤
│ 755 │rwxr-xr-x │ 目录、可执行文件标准权限 │
│ 644 │rw-r--r-- │ 普通文件标准权限 │
│ 700 │rwx------ │ 私有目录 │
│ 600 │rw------- │ 私有文件(SSH密钥等) │
│ 750 │rwxr-x--- │ 组内共享目录 │
│ 640 │rw-r----- │ 组内共享文件 │
│ 775 │rwxrwxr-x │ 团队协作目录 │
│ 664 │rw-rw-r-- │ 团队协作文件 │
│ 1777 │rwxrwxrwt │ 公共目录(如/tmp) │
│ 2775 │rwxrwsr-x │ SGID目录(团队共享) │
│ 4755 │rwsr-xr-x │ SUID可执行文件 │
└───────┴──────────┴─────────────────────────────────────────────────┘
附录C:术语表
| 术语 |
英文全称 |
说明 |
| UGO |
User-Group-Others |
传统Unix权限模型 |
| ACL |
Access Control List |
访问控制列表 |
| MAC |
Mandatory Access Control |
强制访问控制 |
| DAC |
Discretionary Access Control |
自主访问控制 |
| SUID |
Set User ID |
设置用户ID位 |
| SGID |
Set Group ID |
设置组ID位 |
| UID |
User ID |
用户标识符 |
| GID |
Group ID |
组标识符 |
| EUID |
Effective User ID |
有效用户ID |
| EGID |
Effective Group ID |
有效组ID |
| Sticky Bit |
- |
粘滞位 |
| umask |
User file creation mask |
用户文件创建掩码 |
| SELinux |
Security-Enhanced Linux |
安全增强Linux |
| AppArmor |
Application Armor |
应用程序护甲 |
| AVC |
Access Vector Cache |
访问向量缓存 |
| Capabilities |
- |
能力/特权细分 |
| Namespace |
- |
命名空间 |
| Seccomp |
Secure Computing Mode |
安全计算模式 |
| Audit |
- |
审计 |
附录D:常见问题FAQ
Q1: 为什么我用root改了文件权限,普通用户还是访问不了?
A: 检查以下几点:
- 父目录是否有x权限
- 是否启用了SELinux或AppArmor
- 是否有chattr设置的不可变属性
- 使用namei -l检查完整路径
Q2: setfacl设置的权限为什么没生效?
A: 可能的原因:
- 文件系统未启用ACL支持
- mask限制了有效权限
- 基础权限与ACL冲突
Q3: 如何安全地允许Web服务写入某个目录?
A: 推荐方案:
- 创建专门的可写目录
- 设置正确的所有者(Web用户)
- 权限设为770
- 父目录权限设为755
- 配置SELinux允许写入
Q4: 生产环境应该关闭SELinux吗?
A: 强烈不建议。正确的做法是学会使用SELinux。如果实在遇到问题,可以:
- 临时设为Permissive模式排查
- 使用audit2allow生成策略
- 针对性地调整布尔值
Q5: 如何批量修改大量文件的权限而不影响性能?
A: 使用xargs并行处理:
find /path -type f -print0 | xargs -0 -P 4 chmod 644
find /path -type d -print0 | xargs -0 -P 4 chmod 755
写这篇文章花了我不少时间,把这些年踩过的坑、学到的经验都整理了一遍。权限管理看似简单,实际上是个需要持续学习和实践的领域。希望这篇文章能帮助你建立起系统的权限管理知识体系。
如果你在实际工作中遇到权限相关的问题,欢迎在云栈社区留言讨论。毕竟,分享经验、共同进步才是技术人应有的态度。
本文基于2025年1月的技术实践编写,随着Linux内核和发行版的更新,部分命令和配置可能会有变化,请以官方文档为准。