找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2212

积分

0

好友

320

主题
发表于 昨天 17:56 | 查看: 6| 回复: 0

说实话,我在运维这行摸爬滚打了十年,见过太多因为权限配置不当导致的生产事故。记得刚入行那会儿,有一次凌晨三点被电话吵醒,线上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。攻击者直接下载了完整的数据库。

正确的做法是:

  1. 备份目录不要放在Web目录下
  2. 备份文件权限设为600
  3. 使用加密备份
  4. 定期转移到离线存储

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 参考资料

以下是我认为值得深入阅读的资料:

  1. 《The Linux Command Line》- William Shotts
  2. Red Hat Enterprise Linux Security Guide
  3. CIS Benchmarks for Linux
  4. NIST SP 800-123: Guide to General Server Security
  5. Linux man pages: chmod(1), chown(1), setfacl(1), semanage(8)
  6. 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: 检查以下几点:

  1. 父目录是否有x权限
  2. 是否启用了SELinux或AppArmor
  3. 是否有chattr设置的不可变属性
  4. 使用namei -l检查完整路径

Q2: setfacl设置的权限为什么没生效?

A: 可能的原因:

  1. 文件系统未启用ACL支持
  2. mask限制了有效权限
  3. 基础权限与ACL冲突

Q3: 如何安全地允许Web服务写入某个目录?

A: 推荐方案:

  1. 创建专门的可写目录
  2. 设置正确的所有者(Web用户)
  3. 权限设为770
  4. 父目录权限设为755
  5. 配置SELinux允许写入

Q4: 生产环境应该关闭SELinux吗?

A: 强烈不建议。正确的做法是学会使用SELinux。如果实在遇到问题,可以:

  1. 临时设为Permissive模式排查
  2. 使用audit2allow生成策略
  3. 针对性地调整布尔值

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内核和发行版的更新,部分命令和配置可能会有变化,请以官方文档为准。




上一篇:深入理解CPU如何与硬件交互:从I/O端口、中断机制到DMA
下一篇:优化Pandas条件逻辑:向量化赋值替代np.where嵌套,性能提升4倍
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-11 13:58 , Processed in 0.260634 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表