一文搞懂Linux权限体系

描述

一文搞懂Linux权限体系:从777到ACL高级权限控制

一、概述

1.1 从一次线上事故说起

2024年的一个深夜,我被一通紧急电话吵醒。公司的核心业务系统突然无法访问,客户投诉电话已经打爆了客服热线。我火速登录服务器排查,发现问题出在一个看似简单的操作上——某位同事为了"方便",在部署时执行了chmod -R 777 /var/www/html。

这个命令在当时看起来解决了文件访问的问题,但却为攻击者打开了一扇大门。黑客通过上传的恶意脚本获得了执行权限,进而拿下了整台服务器。这次事故让公司损失了近百万,也让我深刻认识到:Linux权限管理绝不是一个可以"随便搞搞"的话题。

从那以后,我花了大量时间深入研究Linux权限体系,从最基础的rwx到ACL、SELinux,再到现代容器环境下的权限控制。今天,我把这些年积累的经验整理成这篇文章,希望能帮助更多运维同学避开那些我曾经踩过的坑。

1.2 为什么权限管理如此重要

在聊具体技术之前,我想先说说为什么我们需要认真对待权限管理。

安全是底线,不是选项。 根据2024年的安全报告统计,超过60%的Linux服务器入侵事件与权限配置不当有关。很多时候,攻击者不需要利用什么高深的0day漏洞,仅仅是利用管理员留下的权限漏洞就能得手。

权限管理是多人协作的基础。 在现代DevOps环境中,一台服务器可能同时运行着多个应用,由不同的团队负责维护。如果没有清晰的权限边界,要么是互相干扰,要么是出了问题找不到责任人。

合规审计的硬性要求。 如果你的公司需要通过ISO 27001、等保三级或者SOC 2认证,权限管理是必查项目。审计师会仔细检查你的服务器上是否存在777权限的文件、是否有不必要的SUID程序、是否启用了SELinux等。

1.3 Linux权限体系的演进

Linux的权限体系并不是一成不变的,它经历了几个重要的发展阶段:

第一代:传统Unix权限(1970s)这是最基础的owner/group/others模型,通过rwx三个权限位控制访问。简单、高效,但灵活性不足。

第二代:特殊权限位(1980s)引入SUID、SGID、Sticky Bit,解决了一些特殊场景的需求,但也带来了新的安全风险。

第三代:ACL访问控制列表(1990s-2000s)POSIX ACL的出现让权限管理更加精细化,可以针对单个用户或组设置权限,不再受限于传统的三元组模型。

第四代:强制访问控制(2000s-至今)SELinux、AppArmor等MAC机制的出现,将权限控制提升到了一个新的高度。即使是root用户,在MAC策略的约束下也不能为所欲为。

第五代:容器与云原生时代(2015-至今)随着Docker、Kubernetes的普及,namespace、cgroup、seccomp等技术与传统权限系统结合,形成了更加复杂但也更加安全的权限控制体系。

1.4 本文的适用场景和环境要求

这篇文章的内容适用于以下场景:

Web应用服务器的权限规划和加固

数据库服务器的安全配置

多用户共享开发环境的权限隔离

容器化应用的权限控制

安全合规审计的准备工作

环境要求:

 

# 操作系统版本(推荐)
RHEL/Rocky Linux/AlmaLinux 9.x
Ubuntu 24.04 LTS
Debian 12

# 内核版本
Linux Kernel 6.1+(支持最新的安全特性)

# 必要的软件包
acl          # ACL支持
attr         # 扩展属性支持
policycoreutils-python-utils  # SELinux工具(RHEL系)
apparmor-utils               # AppArmor工具(Ubuntu/Debian)
auditd       # 审计守护进程

 

二、详细步骤

2.1 准备工作:理解基础权限模型

在动手配置之前,我们需要先把基础概念搞清楚。我见过太多人急于求成,对着网上的命令一顿复制粘贴,结果把系统搞得一团糟。

2.1.1 文件权限的本质

在Linux中,一切皆文件。每个文件都有三组权限:

所有者(Owner)权限:文件创建者默认拥有的权限

所属组(Group)权限:文件所属用户组的成员拥有的权限

其他人(Others)权限:系统上其他所有用户的权限

每组权限包含三个基本操作:

r(read):读取权限

w(write):写入权限

x(execute):执行权限

对于文件和目录,这些权限的含义略有不同:

 

权限  | 对文件的含义           | 对目录的含义
------|------------------------|---------------------------
r     | 查看文件内容           | 列出目录中的文件名
w     | 修改文件内容           | 在目录中创建、删除、重命名文件
x     | 执行文件(脚本/程序)  | 进入目录(cd)

 

这里有个容易混淆的地方:目录的r权限只能让你看到文件名列表,如果没有x权限,你无法读取文件的详细信息(如大小、权限、修改时间),也无法访问目录中的文件内容。

我来演示一下:

 

# 创建测试目录和文件
mkdir /tmp/perm_test
echo"secret content" > /tmp/perm_test/secret.txt

# 只给r权限,不给x权限
chmod 744 /tmp/perm_test
chmod 744 /tmp/perm_test/secret.txt

# 切换到普通用户测试
su - testuser

# 可以看到文件名,但无法看到详细信息
ls /tmp/perm_test
# 输出:secret.txt

ls -l /tmp/perm_test
# 输出:ls: cannot access '/tmp/perm_test/secret.txt': Permission denied

cat /tmp/perm_test/secret.txt
# 输出:cat: /tmp/perm_test/secret.txt: Permission denied

 

2.1.2 数字表示法的换算

很多新手对权限的数字表示法感到困惑,其实换算规则很简单:

 

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

 

所以常见的权限组合:

 

755 = rwxr-xr-x  # 所有者可读写执行,其他人可读和执行
644 = rw-r--r--  # 所有者可读写,其他人只读
700 = rwx------  # 只有所有者可访问
600 = rw-------  # 只有所有者可读写

 

我个人有个记忆口诀:**"7是全开,6是读写,5是读执行,4是只读,0是全关"**。记住这几个数字的含义,其他的都是组合。

2.2 核心配置:权限管理命令详解

2.2.1 chmod:修改文件权限

chmod是最常用的权限管理命令,支持两种模式:

符号模式:

 

# 基本语法
chmod [ugoa][+-=][rwx] 文件名

# u: user(所有者)  g: group(所属组)  o: others(其他人)  a: all(所有人)
# +: 添加权限  -: 移除权限  =: 设置权限

# 示例
chmod u+x script.sh       # 给所有者添加执行权限
chmod g-w config.ini      # 移除所属组的写权限
chmod o=r public.html     # 设置其他人只有读权限
chmod a+r readme.txt      # 所有人添加读权限
chmod u+x,g=rx,o= app.sh  # 组合操作

 

数字模式:

 

# 基本语法
chmod [权限数字] 文件名

# 示例
chmod 755 script.sh       # rwxr-xr-x
chmod 644 config.ini      # rw-r--r--
chmod 600 id_rsa          # rw-------
chmod 700 .ssh            # rwx------

 

递归操作:

 

# 递归修改目录及其所有内容
chmod -R 755 /var/www/html

# 但这种做法通常是错误的!
# 因为目录需要x权限才能进入,但普通文件通常不需要执行权限
# 正确的做法是分别处理目录和文件

# 只修改目录权限
find /var/www/html -type d -exec chmod 755 {} ;

# 只修改文件权限
find /var/www/html -type f -exec chmod 644 {} ;

 

这是一个非常重要的实践经验。我见过很多人直接chmod -R 755,结果把所有文件都变成了可执行的,这在安全上是不可接受的。

2.2.2 chown:修改文件所有者

 

# 基本语法
chown [用户]:[组] 文件名

# 示例
chown nginx:nginx /var/www/html          # 修改所有者和所属组
chown nginx /var/www/html                 # 只修改所有者
chown :www-data /var/www/html            # 只修改所属组
chown -R nginx:nginx /var/www/html       # 递归修改

# 使用UID和GID
chown 1000:1000 /data/app                # 在容器环境中常用

# 保持引用
chown --reference=/etc/nginx/nginx.conf /etc/nginx/conf.d/  # 复制参考文件的所有者

 

2.2.3 chgrp:修改文件所属组

 

# 基本语法
chgrp [组] 文件名

# 示例
chgrp developers /opt/project
chgrp -R www-data /var/www/html

# 实际工作中,chown命令可以同时修改用户和组,所以chgrp用得相对较少

 

2.3 特殊权限位配置

除了基本的rwx权限,Linux还有三个特殊权限位:SUID、SGID和Sticky Bit。这些权限位功能强大,但如果使用不当,也会带来严重的安全风险。

2.3.1 SUID(Set User ID)

原理: 当一个可执行文件设置了SUID位后,任何用户执行该文件时,都会临时获得文件所有者的权限。

典型应用:/usr/bin/passwd命令就设置了SUID。普通用户修改密码时,需要写入/etc/shadow文件,但这个文件只有root能写。通过SUID,普通用户执行passwd时临时获得root权限,完成密码修改。

 

# 查看passwd的权限
ls -l /usr/bin/passwd
# -rwsr-xr-x. 1 root root 32648 Aug 10 2021 /usr/bin/passwd
# 注意所有者权限位中的's',这就是SUID的标志

# 设置SUID
chmod u+s /path/to/executable
chmod 4755 /path/to/executable  # 4表示SUID

# 移除SUID
chmod u-s /path/to/executable
chmod 0755 /path/to/executable

 

安全警告: SUID是一个非常危险的权限。如果一个设置了SUID的程序存在漏洞,攻击者可能借此获得root权限。我的建议是:

定期审计系统中的SUID程序:find / -perm -4000 -type f 2>/dev/null

对于不必要的SUID程序,移除SUID位

永远不要给shell脚本设置SUID(Linux内核会忽略脚本的SUID位,但这是个好习惯)

2.3.2 SGID(Set Group ID)

SGID根据应用对象不同,有两种效果:

对可执行文件: 执行时临时获得文件所属组的权限。

对目录: 在该目录中创建的新文件会继承目录的所属组,而不是创建者的主组。

 

# 查看SGID标志(所属组权限位中的's')
ls -ld /usr/bin/write
# -rwxr-sr-x. 1 root tty 19544 Aug 10 2021 /usr/bin/write

# 设置SGID
chmod g+s /path/to/dir
chmod 2755 /path/to/dir  # 2表示SGID

# SGID目录的实际应用:团队共享目录
mkdir /opt/project
groupadd developers
chgrp developers /opt/project
chmod 2775 /opt/project

# 现在,任何人在/opt/project中创建的文件,所属组都会是developers

 

我在很多公司都用SGID来设置团队共享目录。它解决了一个常见问题:不同用户创建的文件默认属于各自的主组,其他组员可能无法访问。设置SGID后,所有文件统一属于项目组。

2.3.3 Sticky Bit(粘滞位)

原理: 对于设置了Sticky Bit的目录,即使用户对目录有写权限,也只能删除自己创建的文件,不能删除其他用户的文件。

典型应用:/tmp目录就设置了Sticky Bit。所有用户都能在/tmp中创建文件,但不能删除别人的文件。

 

# 查看/tmp的权限
ls -ld /tmp
# drwxrwxrwt. 18 root root 4096 Jan 7 10:00 /tmp
# 注意最后的't',这就是Sticky Bit的标志

# 设置Sticky Bit
chmod +t /path/to/dir
chmod 1777 /path/to/dir  # 1表示Sticky Bit

# 移除Sticky Bit
chmod -t /path/to/dir

 

2.4 启动验证

配置完权限后,一定要验证效果。我习惯用以下方法:

 

# 1. 使用stat命令查看详细权限信息
stat /var/www/html
# 输出会包含Access、Uid、Gid等详细信息

# 2. 使用namei命令检查路径上的所有权限
namei -l /var/www/html/index.php
# 这会显示路径上每一级目录的权限,帮你找到权限链条中的问题

# 3. 使用sudo -u切换用户测试
sudo -u nginx cat /var/www/html/config.php
sudo -u www-data ls -la /var/www/html

# 4. 使用getfacl查看ACL(如果使用了ACL)
getfacl /var/www/html

 

三、示例代码和配置

3.1 Web应用目录权限配置

这是我在工作中最常见的场景。一个典型的Web应用需要考虑:运行用户、上传目录、日志目录、配置文件等不同位置的权限。

 

#!/bin/bash
# web_permission_setup.sh
# Web应用权限配置脚本

# 定义变量
WEB_ROOT="/var/www/myapp"
WEB_USER="nginx"
WEB_GROUP="www-data"
UPLOAD_DIR="${WEB_ROOT}/uploads"
LOG_DIR="/var/log/myapp"
CONFIG_DIR="${WEB_ROOT}/config"

# 1. 创建目录结构
mkdir -p ${WEB_ROOT}/{public,config,uploads,storage}
mkdir -p ${LOG_DIR}

# 2. 设置基本所有权
chown -R ${WEB_USER}:${WEB_GROUP}${WEB_ROOT}
chown -R ${WEB_USER}:${WEB_GROUP}${LOG_DIR}

# 3. 设置目录权限
# 目录需要执行权限才能进入
find ${WEB_ROOT} -type d -exec chmod 755 {} ;

# 4. 设置文件权限
# 普通文件不需要执行权限
find ${WEB_ROOT} -type f -exec chmod 644 {} ;

# 5. 配置文件加强保护
# 配置文件只允许所有者读写
chmod 600 ${CONFIG_DIR}/*.php
chmod 600 ${CONFIG_DIR}/.env

# 6. 上传目录特殊处理
# 禁止执行任何脚本
chmod 755 ${UPLOAD_DIR}
# 在nginx配置中也要禁用PHP执行(后面会讲)

# 7. 日志目录
chmod 750 ${LOG_DIR}
chmod 640 ${LOG_DIR}/*.log 2>/dev/null

# 8. 可执行脚本(如果有)
chmod 750 ${WEB_ROOT}/bin/*.sh 2>/dev/null

# 9. 使用SGID确保新文件属于正确的组
chmod g+s ${UPLOAD_DIR}
chmod g+s ${LOG_DIR}

echo"权限配置完成!"

# 验证
echo"=== 验证权限设置 ==="
ls -la ${WEB_ROOT}
ls -la ${CONFIG_DIR}
ls -la ${UPLOAD_DIR}

 

配合Nginx配置,禁止上传目录执行脚本:

 

# /etc/nginx/conf.d/myapp.conf

server {
    listen 80;
    server_name myapp.example.com;
    root /var/www/myapp/public;

    # 禁止uploads目录执行PHP
    location ^~ /uploads/ {
        location ~ .php$ {
            deny all;
            return 403;
        }
    }

    # 禁止访问隐藏文件和配置文件
    location ~ /. {
        deny all;
        return 404;
    }

    location ~ /config/ {
        deny all;
        return 404;
    }
}

 

3.2 数据库文件权限配置

数据库文件的权限配置需要特别谨慎,因为这里存储着最敏感的数据。

 

#!/bin/bash
# mysql_permission_setup.sh
# MySQL数据库权限配置脚本

# MySQL数据目录
MYSQL_DATA="/var/lib/mysql"
MYSQL_LOG="/var/log/mysql"
MYSQL_CONFIG="/etc/mysql"
MYSQL_USER="mysql"
MYSQL_GROUP="mysql"

# 1. 数据目录 - 只有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 {} ;

# 2. 日志目录
chown -R ${MYSQL_USER}:${MYSQL_GROUP}${MYSQL_LOG}
chmod 750 ${MYSQL_LOG}
chmod 640 ${MYSQL_LOG}/*.log 2>/dev/null

# 3. 配置文件
# my.cnf应该是root拥有,mysql可读
chown root:${MYSQL_GROUP}${MYSQL_CONFIG}/my.cnf
chmod 640 ${MYSQL_CONFIG}/my.cnf

# conf.d目录下的配置文件
chown root:${MYSQL_GROUP}${MYSQL_CONFIG}/conf.d/*
chmod 640 ${MYSQL_CONFIG}/conf.d/*

# 4. Socket文件目录
mkdir -p /var/run/mysqld
chown ${MYSQL_USER}:${MYSQL_GROUP} /var/run/mysqld
chmod 755 /var/run/mysqld

# 5. 备份目录 - 更严格的权限
BACKUP_DIR="/backup/mysql"
mkdir -p ${BACKUP_DIR}
chown root:${MYSQL_GROUP}${BACKUP_DIR}
chmod 750 ${BACKUP_DIR}

echo"MySQL权限配置完成!"

 

3.3 SSH密钥权限配置

SSH对密钥文件的权限要求非常严格。权限配置不正确,SSH会直接拒绝使用这些密钥。

 

#!/bin/bash
# ssh_permission_setup.sh
# SSH密钥权限配置脚本

SSH_DIR="$HOME/.ssh"

# 1. .ssh目录
chmod 700 ${SSH_DIR}

# 2. 私钥文件 - 只有所有者可读
chmod 600 ${SSH_DIR}/id_rsa
chmod 600 ${SSH_DIR}/id_ed25519
chmod 600 ${SSH_DIR}/id_ecdsa
# 处理所有私钥
find ${SSH_DIR} -name "id_*" ! -name "*.pub" -exec chmod 600 {} ;

# 3. 公钥文件
chmod 644 ${SSH_DIR}/*.pub

# 4. authorized_keys - 非常重要
chmod 600 ${SSH_DIR}/authorized_keys

# 5. known_hosts
chmod 644 ${SSH_DIR}/known_hosts

# 6. config文件
chmod 600 ${SSH_DIR}/config

# 对于服务器端的SSH配置
# /etc/ssh/sshd_config
sudo chmod 600 /etc/ssh/sshd_config
sudo chmod 600 /etc/ssh/ssh_host_*_key
sudo chmod 644 /etc/ssh/ssh_host_*_key.pub

echo"SSH权限配置完成!"

# 验证
echo"=== 验证SSH权限 ==="
ls -la ${SSH_DIR}

 

3.4 ACL高级权限配置

当传统的owner/group/others模型无法满足需求时,就需要使用ACL了。

场景示例: 一个项目目录需要让开发组有读写权限,运维组有只读权限,而某个特定的外包人员需要只读特定子目录。

 

#!/bin/bash
# acl_setup.sh
# ACL权限配置示例

PROJECT_DIR="/opt/project"
DEV_GROUP="developers"
OPS_GROUP="operations"
CONTRACTOR="john_contractor"

# 1. 首先确保文件系统支持ACL
# 检查挂载选项
mount | grep " / " | grep acl
# 如果没有,需要重新挂载或修改/etc/fstab

# 2. 设置基本权限
chown -R root:${DEV_GROUP}${PROJECT_DIR}
chmod 750 ${PROJECT_DIR}

# 3. 为开发组设置默认ACL
# -d: 设置默认ACL,对新建文件生效
# -m: 修改ACL
setfacl -R -m g:${DEV_GROUP}:rwx ${PROJECT_DIR}
setfacl -R -d -m g:${DEV_GROUP}:rwx ${PROJECT_DIR}

# 4. 为运维组设置只读权限
setfacl -R -m g:${OPS_GROUP}:rx ${PROJECT_DIR}
setfacl -R -d -m g:${OPS_GROUP}:rx ${PROJECT_DIR}

# 5. 为外包人员设置特定目录的权限
setfacl -m u:${CONTRACTOR}:rx ${PROJECT_DIR}/docs
setfacl -R -m u:${CONTRACTOR}:rx ${PROJECT_DIR}/docs

# 6. 查看ACL设置
echo"=== ACL配置结果 ==="
getfacl ${PROJECT_DIR}

# 7. 备份ACL配置(很重要!)
getfacl -R ${PROJECT_DIR} > /backup/project_acl_backup.txt

# 8. 恢复ACL配置
# setfacl --restore=/backup/project_acl_backup.txt

 

ACL常用命令速查:

 

# 查看文件的ACL
getfacl /path/to/file

# 设置用户ACL
setfacl -m urwx /path/to/file

# 设置组ACL
setfacl -m grx /path/to/file

# 设置默认ACL(对目录中新建的文件生效)
setfacl -d -m urwx /path/to/dir

# 递归设置ACL
setfacl -R -m urwx /path/to/dir

# 移除特定用户的ACL
setfacl -x u:username /path/to/file

# 移除所有ACL
setfacl -b /path/to/file

# 复制ACL到另一个文件
getfacl file1 | setfacl --set-file=- file2

 

3.5 SELinux配置实战

SELinux是红帽系Linux发行版的默认强制访问控制系统。很多管理员因为它"太麻烦"而直接禁用,这是非常不明智的做法。

 

#!/bin/bash
# selinux_web_setup.sh
# SELinux配置示例 - Web应用场景

# 1. 检查SELinux状态
getenforce
# Enforcing: 强制模式,违反策略会被阻止
# Permissive: 宽容模式,违反策略只记录不阻止
# Disabled: 禁用

# 2. 查看当前SELinux策略
sestatus

# 3. 设置Web目录的SELinux上下文
# httpd_sys_content_t: 允许httpd读取
# httpd_sys_rw_content_t: 允许httpd读写
WEB_ROOT="/var/www/myapp"

# 设置静态内容为只读
semanage fcontext -a -t httpd_sys_content_t "${WEB_ROOT}(/.*)?"

# 设置uploads目录为可写
semanage fcontext -a -t httpd_sys_rw_content_t "${WEB_ROOT}/uploads(/.*)?"

# 应用上下文
restorecon -Rv ${WEB_ROOT}

# 4. 查看文件的SELinux上下文
ls -Z ${WEB_ROOT}

# 5. 临时修改上下文(重启或restorecon后会失效)
chcon -t httpd_sys_rw_content_t ${WEB_ROOT}/temp

# 6. 常见的布尔值设置
# 允许httpd连接网络(如调用外部API)
setsebool -P httpd_can_network_connect on

# 允许httpd连接数据库
setsebool -P httpd_can_network_connect_db on

# 允许httpd发送邮件
setsebool -P httpd_can_sendmail on

# 允许httpd执行CGI脚本
setsebool -P httpd_enable_cgi on

# 7. 查看所有httpd相关的布尔值
getsebool -a | grep httpd

# 8. 排查SELinux拒绝问题
# 查看审计日志
ausearch -m avc -ts recent

# 使用audit2why分析原因
ausearch -m avc -ts recent | audit2why

# 生成允许规则(谨慎使用!)
ausearch -m avc -ts recent | audit2allow -M myapp_policy
semodule -i myapp_policy.pp

echo"SELinux配置完成!"

 

SELinux常用上下文类型:

 

httpd_sys_content_t      - Web服务器只读内容
httpd_sys_rw_content_t   - Web服务器可读写内容
httpd_sys_script_exec_t  - Web服务器可执行脚本
mysqld_db_t              - MySQL数据库文件
postgresql_db_t          - PostgreSQL数据库文件
ssh_home_t               - SSH配置文件
user_home_t              - 用户主目录内容
tmp_t                    - 临时文件
var_log_t                - 日志文件

 

3.6 AppArmor配置实战

Ubuntu和Debian系统默认使用AppArmor。相比SELinux,AppArmor的配置更加直观。

 

#!/bin/bash
# apparmor_setup.sh
# AppArmor配置示例

# 1. 检查AppArmor状态
aa-status

# 2. 查看加载的配置文件
ls /etc/apparmor.d/

# 3. 创建自定义配置文件
cat > /etc/apparmor.d/usr.local.bin.myapp << 'EOF'
#include 

/usr/local/bin/myapp {
#include 
#include 

# 允许读取配置文件
  /etc/myapp/** r,
  /etc/myapp/config.ini r,

# 允许读写数据目录
  /var/lib/myapp/** rw,
  /var/lib/myapp/ r,

# 允许读写日志
  /var/log/myapp/** rw,
  /var/log/myapp/ r,

# 允许执行自身
  /usr/local/bin/myapp mr,

# 允许网络访问
  network inet stream,
  network inet dgram,

# 拒绝所有其他访问
  deny /home/** rw,
  deny /root/** rw,
  deny /etc/passwd w,
  deny /etc/shadow rw,
}
EOF

# 4. 检查配置文件语法
apparmor_parser -p /etc/apparmor.d/usr.local.bin.myapp

# 5. 加载配置文件
apparmor_parser -r /etc/apparmor.d/usr.local.bin.myapp

# 6. 设置为强制模式
aa-enforce /usr/local/bin/myapp

# 7. 临时设置为宽容模式(用于调试)
aa-complain /usr/local/bin/myapp

# 8. 查看AppArmor日志
journalctl -xe | grep apparmor
dmesg | grep apparmor

 

3.7 容器环境权限配置

在2025年,容器已经成为应用部署的主流方式。容器环境的权限控制有其特殊性。

 

# docker-compose.yml 安全配置示例
version:'3.8'

services:
webapp:
    image:myapp:latest
    user:"1000:1000"# 不使用root用户运行

    # 只读文件系统
    read_only:true

    # 挂载必要的可写目录
    volumes:
      -./config:/app/config:ro        # 配置文件只读
      -webapp_data:/app/data:rw       # 数据目录可写
      -webapp_logs:/var/log/app:rw    # 日志目录可写

    # tmpfs用于临时文件
    tmpfs:
      -/tmp:size=100M,mode=1777
      -/run:size=10M,mode=755

    # 安全选项
    security_opt:
      -no-new-privileges:true   # 禁止提权
      -seccomp:default          # 使用默认的seccomp配置

    # 移除不必要的能力
    cap_drop:
      -ALL

    # 只添加必要的能力
    cap_add:
      -NET_BIND_SERVICE# 如果需要绑定低端口

    # AppArmor配置(如果使用)
    # security_opt:
    #   - apparmor:docker-default

volumes:
webapp_data:
webapp_logs:

 

Kubernetes Pod安全配置:

 

# pod-security.yaml
apiVersion:v1
kind:Pod
metadata:
name:secure-app
spec:
securityContext:
    runAsNonRoot:true
    runAsUser:1000
    runAsGroup:1000
    fsGroup:1000
    seccompProfile:
      type:RuntimeDefault

containers:
-name:app
    image:myapp:latest

    securityContext:
      allowPrivilegeEscalation:false
      readOnlyRootFilesystem:true
      capabilities:
        drop:
          -ALL

    volumeMounts:
    -name:tmp
      mountPath:/tmp
    -name:data
      mountPath:/app/data
    -name:config
      mountPath:/app/config
      readOnly:true

volumes:
-name:tmp
    emptyDir:
      medium:Memory
      sizeLimit:100Mi
-name:data
    persistentVolumeClaim:
      claimName:app-data-pvc
-name:config
    configMap:
      name:app-config
      defaultMode:0400# 只读权限

 

四、最佳实践和注意事项

4.1 权限设计原则

经过多年的实践,我总结出以下权限设计原则:

4.1.1 最小权限原则

这是安全领域最重要的原则之一:只授予完成任务所必需的最小权限。

 

# 错误示范 - 图方便
chmod 777 /var/www/html
chmod -R 777 /opt/app

# 正确做法 - 精确控制
chmod 755 /var/www/html
chmod 644 /var/www/html/*.php
chmod 600 /var/www/html/config.php

 

4.1.2 职责分离原则

不同的任务应该由不同的用户/组来执行,避免权限过度集中。

 

# 创建专用用户和组
groupadd webapp
useradd -r -g webapp -s /sbin/nologin webapp

groupadd dbbackup
useradd -r -g dbbackup -s /sbin/nologin dbbackup

# Web应用使用webapp用户运行
# 数据库备份使用dbbackup用户执行

 

4.1.3 纵深防御原则

不要依赖单一的安全措施,应该建立多层防御。

 

# 第一层:文件系统权限
chmod 600 /etc/myapp/secrets.conf

# 第二层:SELinux/AppArmor
semanage fcontext -a -t myapp_secret_t "/etc/myapp/secrets.conf"

# 第三层:使用加密存储敏感信息
# 不要明文存储密码,使用vault或加密配置

# 第四层:审计监控
auditctl -w /etc/myapp/secrets.conf -p rwa -k secrets_access

 

4.2 常见安全加固措施

4.2.1 审计SUID/SGID程序

 

#!/bin/bash
# audit_suid_sgid.sh
# 审计SUID/SGID程序

echo"=== SUID程序 ==="
find / -perm -4000 -type f 2>/dev/null | whileread file; do
    ls -l "$file"
    rpm -qf "$file" 2>/dev/null || dpkg -S "$file" 2>/dev/null || echo"未知来源"
    echo"---"
done

echo"=== SGID程序 ==="
find / -perm -2000 -type f 2>/dev/null | whileread file; do
    ls -l "$file"
    rpm -qf "$file" 2>/dev/null || dpkg -S "$file" 2>/dev/null || echo"未知来源"
    echo"---"
done

# 移除不必要的SUID
# chmod u-s /usr/bin/unnecessary_suid_program

 

4.2.2 查找全局可写文件

 

#!/bin/bash
# find_world_writable.sh
# 查找全局可写文件和目录

echo "=== 全局可写目录(不含Sticky Bit)==="
find / -type d -perm -0002 ! -perm -1000 2>/dev/null

echo "=== 全局可写文件 ==="
find / -type f -perm -0002 2>/dev/null

echo "=== 无主文件 ==="
find / -nouser -o -nogroup 2>/dev/null

 

4.2.3 限制umask

 

# /etc/profile 或 /etc/bashrc
umask 027  # 新建文件默认权限640,目录750

# 对于安全要求更高的环境
umask 077  # 新建文件默认权限600,目录700

# 在systemd服务中设置
# /etc/systemd/system/myapp.service
[Service]
UMask=0027

 

4.3 常见错误和教训

4.3.1 错误一:滥用777权限

这是最常见也是最危险的错误。我曾经遇到过一个客户,他们的生产服务器上有超过10万个777权限的文件。

错误做法:

 

# "反正能用就行"
chmod 777 /var/www/html
chmod -R 777 /opt/app

 

正确做法:

 

# 分析实际需求
# Web服务器运行用户是nginx
chown -R nginx:nginx /var/www/html
find /var/www/html -type d -exec chmod 755 {} ;
find /var/www/html -type f -exec chmod 644 {} ;
# 需要写入的目录单独处理
chmod 775 /var/www/html/uploads

 

4.3.2 错误二:忽略路径上的权限

很多人只关注目标文件的权限,忽略了路径上每一级目录的权限。

 

# 文件权限正确
chmod 644 /opt/app/data/config/secrets.json

# 但是目录权限错了
chmod 777 /opt/app/data  # 任何人都能进入并创建文件

# 正确做法:检查整个路径
namei -l /opt/app/data/config/secrets.json

 

4.3.3 错误三:复制文件不检查权限

使用cp、scp等命令复制文件时,可能会改变文件的权限和所有者。

 

# 错误:复制后不检查权限
cp /root/app.conf /etc/myapp/

# 正确:复制时保留权限,或者复制后重新设置
cp -p /root/app.conf /etc/myapp/     # -p保留权限
# 或者
cp /root/app.conf /etc/myapp/
chown root:myapp /etc/myapp/app.conf
chmod 640 /etc/myapp/app.conf

 

4.3.4 错误四:误解SELinux拒绝

很多人遇到SELinux拒绝就直接禁用,这是非常不明智的。

 

# 错误做法
setenforce 0
# 在/etc/selinux/config中设置SELINUX=disabled

# 正确做法
# 1. 查看SELinux拒绝原因
ausearch -m avc -ts recent | audit2why

# 2. 根据原因采取措施
# 如果是上下文问题
restorecon -Rv /path/to/file

# 如果需要开启布尔值
setsebool -P httpd_can_network_connect on

# 3. 只有在确认安全的情况下才生成自定义策略

 

4.4 性能考虑

权限检查会带来一定的性能开销,虽然通常可以忽略,但在高性能场景下需要注意:

 

# ACL会比传统权限检查慢一些
# 如果不需要ACL的灵活性,使用传统权限更高效

# SELinux在enforce模式下会进行额外的检查
# 确保策略配置正确,避免不必要的deny日志

# 对于高频访问的文件,权限设计要简单直接
# 避免复杂的ACL规则链

 

五、故障排查和监控

5.1 日志查看

权限问题的排查首先要看日志:

 

# 1. 系统日志
journalctl -xe
tail -f /var/log/messages
tail -f /var/log/syslog

# 2. 应用日志
tail -f /var/log/nginx/error.log
tail -f /var/log/apache2/error.log
tail -f /var/log/php-fpm/error.log

# 3. SELinux审计日志
ausearch -m avc -ts today
ausearch -m avc -c httpd  # 查找httpd相关的拒绝

# 4. AppArmor日志
journalctl -xe | grep apparmor
dmesg | grep apparmor

# 5. 使用audit2why分析SELinux拒绝原因
ausearch -m avc -ts today | audit2why

 

5.2 问题排查流程

当遇到"Permission denied"错误时,我通常按以下流程排查:

 

#!/bin/bash
# permission_troubleshoot.sh
# 权限问题排查脚本

TARGET=$1# 要检查的文件或目录

if [ -z "$TARGET" ]; then
    echo"用法: $0 <文件或目录>"
    exit 1
fi

echo"=== 第一步:检查文件是否存在 ==="
ls -la "$TARGET" 2>&1

echo""
echo"=== 第二步:检查路径上所有目录的权限 ==="
namei -l "$TARGET"

echo""
echo"=== 第三步:检查ACL ==="
getfacl "$TARGET" 2>&1

echo""
echo"=== 第四步:检查SELinux上下文 ==="
ls -Z "$TARGET" 2>&1

echo""
echo"=== 第五步:检查SELinux状态 ==="
getenforce

echo""
echo"=== 第六步:最近的SELinux拒绝 ==="
ausearch -m avc -ts recent 2>&1 | head -50

echo""
echo"=== 第七步:检查AppArmor状态 ==="
aa-status 2>&1 | head -20

echo""
echo"=== 第八步:检查文件系统挂载选项 ==="
# 检查是否有noexec、nosuid等选项
df "$TARGET" | tail -1 | awk '{print $1}' | xargs -I {} mount | grep {}

echo""
echo"=== 第九步:检查文件属性 ==="
lsattr "$TARGET" 2>&1

 

5.3 使用auditd监控权限变更

auditd是Linux系统审计的核心工具,可以监控文件访问和权限变更。

 

#!/bin/bash
# audit_setup.sh
# 配置auditd监控权限相关事件

# 1. 安装auditd
yum install -y audit audit-libs  # RHEL系
apt install -y auditd audispd-plugins  # Debian系

# 2. 启动auditd服务
systemctl enable auditd
systemctl start auditd

# 3. 配置审计规则
cat >> /etc/audit/rules.d/permissions.rules << 'EOF'
# 监控权限变更命令
-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -k perm_change
-a always,exit -F arch=b64 -S chown,fchown,fchownat,lchown -F auid>=1000 -F auid!=unset -k owner_change
-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -k attr_change

# 监控敏感文件访问
-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /etc/ssh/sshd_config -p wa -k sshd_config

# 监控SUID/SGID变更
-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F perm=04000 -k suid_change
-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F perm=02000 -k sgid_change

# 监控特定目录
-w /var/www/html -p wa -k web_content
-w /etc/myapp/ -p wa -k app_config
EOF

# 4. 重新加载规则
augenrules --load
auditctl -l

# 5. 查看审计日志
ausearch -k perm_change -ts today
ausearch -k passwd_changes -ts today

# 6. 生成审计报告
aureport --summary
aureport --file --start today

 

5.4 监控告警配置

结合Prometheus和AlertManager实现权限异常告警:

 

# prometheus/rules/permission_alerts.yml
groups:
-name:permission_alerts
rules:
-alert:SuidFileCreated
    expr:increase(audit_suid_changes_total[5m])>0
    for:0m
    labels:
      severity:critical
    annotations:
      summary:"SUID文件被创建或修改"
      description:"在主机 {{ $labels.instance }} 上检测到SUID文件变更"

-alert:SensitiveFileAccessed
    expr:increase(audit_sensitive_file_access_total[5m])>5
    for:0m
    labels:
      severity:warning
    annotations:
      summary:"敏感文件被频繁访问"
      description:"文件 {{ $labels.file }} 在5分钟内被访问超过5次"

-alert:PermissionChangeSpike
    expr:increase(audit_permission_changes_total[10m])>100
    for:5m
    labels:
      severity:warning
    annotations:
      summary:"权限变更操作异常增多"
      description:"10分钟内权限变更操作超过100次,可能存在异常"

 

使用自定义脚本导出审计指标:

 

#!/bin/bash
# audit_exporter.sh
# 导出审计指标供Prometheus采集

METRICS_FILE="/var/lib/node_exporter/audit_metrics.prom"

# SUID变更计数
suid_changes=$(ausearch -k suid_change -ts today 2>/dev/null | grep -c "type=SYSCALL")
echo"audit_suid_changes_total $suid_changes" > $METRICS_FILE

# 权限变更计数
perm_changes=$(ausearch -k perm_change -ts today 2>/dev/null | grep -c "type=SYSCALL")
echo"audit_permission_changes_total $perm_changes" >> $METRICS_FILE

# 敏感文件访问计数
sensitive_access=$(ausearch -k passwd_changes -k shadow_changes -k sudoers_changes -ts today 2>/dev/null | grep -c "type=SYSCALL")
echo"audit_sensitive_file_access_total $sensitive_access" >> $METRICS_FILE

 

5.5 实时权限监控脚本

 

#!/bin/bash
# realtime_permission_monitor.sh
# 实时监控关键目录的权限变化

WATCH_DIRS="/etc /var/www /opt/app"
BASELINE_FILE="/var/lib/permission_baseline.txt"
ALERT_EMAIL="security@example.com"

# 生成基线
generate_baseline() {
    echo"生成权限基线..."
    for dir in$WATCH_DIRS; do
        find $dir -type f -printf"%p %m %u %g
" 2>/dev/null
        find $dir -type d -printf"%p %m %u %g
" 2>/dev/null
    done | sort > $BASELINE_FILE
    echo"基线已保存到 $BASELINE_FILE"
}

# 检查变化
check_changes() {
    CURRENT_FILE=$(mktemp)

    for dir in$WATCH_DIRS; do
        find $dir -type f -printf"%p %m %u %g
" 2>/dev/null
        find $dir -type d -printf"%p %m %u %g
" 2>/dev/null
    done | sort > $CURRENT_FILE

    DIFF=$(diff $BASELINE_FILE$CURRENT_FILE)

    if [ -n "$DIFF" ]; then
        echo"检测到权限变化:"
        echo"$DIFF"

        # 发送告警
        echo"权限变化告警: $DIFF" | mail -s "权限变化告警"$ALERT_EMAIL
    fi

    rm -f $CURRENT_FILE
}

case"$1"in
    baseline)
        generate_baseline
        ;;
    check)
        check_changes
        ;;
    *)
        echo"用法: $0 {baseline|check}"
        exit 1
        ;;
esac

 

六、总结

6.1 要点回顾

经过这篇文章的学习,我们掌握了Linux权限体系的完整知识:

基础权限:

rwx三种基本权限,对文件和目录的含义不同

数字表示法:r=4, w=2, x=1,权限数字是各位相加

chmod、chown、chgrp三个核心命令

递归操作时要区分文件和目录

特殊权限:

SUID:执行时获得文件所有者权限,安全风险高

SGID:对目录设置时新文件继承目录的组

Sticky Bit:防止用户删除他人文件

ACL高级权限:

setfacl/getfacl命令实现精细化权限控制

默认ACL对新建文件生效

记得备份ACL配置

强制访问控制:

SELinux:基于安全上下文的强制访问控制

AppArmor:基于路径的配置文件更直观

不要因为"麻烦"而禁用,学会正确配置

容器环境:

不要用root运行容器

使用只读文件系统

最小化capabilities

启用seccomp

监控审计:

auditd记录权限相关操作

建立权限基线并定期检查

配置告警及时发现异常

6.2 安全事故案例总结

回顾文章开头提到的事故,以及我这些年遇到的其他案例,有几点深刻教训:

永远不要图方便用777。 这是血的教训,没有例外。

SELinux/AppArmor是最后一道防线。 即使前面的防线都被突破,MAC机制仍可能阻止攻击者。

定期审计比事后补救更重要。 每周花一小时检查权限配置,可以避免未来无数小时的救火。

权限管理是团队协作的事。 制定规范、培训团队、代码审查,缺一不可。

6.3 进阶方向

如果你想在权限管理方面继续深入,可以关注以下方向:

Linux Capabilities深入学习:比传统的root权限更细粒度的能力控制

Seccomp和沙箱技术:限制进程可以执行的系统调用

eBPF安全应用:新一代的Linux安全监控技术

零信任架构:在云原生环境下的权限设计理念

PAM模块开发:自定义认证和授权逻辑

6.4 参考资料

Red Hat Enterprise Linux Security Guide (2024)

Ubuntu Security Documentation

SELinux Project Wiki: https://selinuxproject.org

AppArmor Documentation: https://gitlab.com/apparmor/apparmor/-/wikis

Linux Audit Documentation: https://github.com/linux-audit

CIS Benchmark for Linux: https://www.cisecurity.org/benchmark/distribution_independent_linux

NIST SP 800-123: Guide to General Server Security

附录

附录A:命令速查表

 

# === 基础权限命令 ===
chmod 755 file                    # 设置权限为rwxr-xr-x
chmod u+x file                    # 给所有者添加执行权限
chmod g-w file                    # 移除组的写权限
chmod o=r file                    # 设置其他人只有读权限
chmod -R 755 dir                  # 递归设置目录权限
chown user:group file             # 修改所有者和组
chown -R user:group dir           # 递归修改所有者
chgrp group file                  # 修改所属组

# === 特殊权限命令 ===
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 Bit + 777

# === ACL命令 ===
getfacl file                      # 查看ACL
setfacl -m urwx file        # 设置用户ACL
setfacl -m grx file        # 设置组ACL
setfacl -d -m urwx dir      # 设置默认ACL
setfacl -R -m urwx dir      # 递归设置ACL
setfacl -x u:user file            # 移除用户ACL
setfacl -b file                   # 移除所有ACL
getfacl -R dir > backup.acl       # 备份ACL
setfacl --restore=backup.acl      # 恢复ACL

# === SELinux命令 ===
getenforce                        # 查看SELinux状态
setenforce 1                      # 设置为强制模式
setenforce 0                      # 设置为宽容模式
sestatus                          # 查看详细状态
ls -Z file                        # 查看文件安全上下文
chcon -t httpd_sys_content_t file # 临时修改上下文
restorecon -Rv dir                # 恢复默认上下文
semanage fcontext -l              # 列出上下文映射
semanage fcontext -a -t type"path(/.*)?"# 添加上下文映射
getsebool -a                      # 列出所有布尔值
setsebool -P bool on              # 设置布尔值
ausearch -m avc -ts today         # 查看AVC拒绝
audit2why < /var/log/audit/audit.log  # 分析拒绝原因
audit2allow -M policy < avc.log   # 生成允许策略

# === AppArmor命令 ===
aa-status                         # 查看状态
aa-enforce /path/to/profile       # 设置强制模式
aa-complain /path/to/profile      # 设置宽容模式
aa-disable /path/to/profile       # 禁用配置文件
apparmor_parser -r /etc/apparmor.d/profile  # 重新加载配置

# === 审计命令 ===
auditctl -l                       # 列出审计规则
auditctl -w /path -p rwxa -k key  # 添加监控规则
ausearch -k key -ts today         # 搜索审计日志
aureport --summary                # 生成审计报告
augenrules --load                 # 加载规则文件

# === 查找命令 ===
find / -perm -4000 -type f        # 查找SUID文件
find / -perm -2000 -type f        # 查找SGID文件
find / -perm -0002 -type f        # 查找全局可写文件
find / -nouser -o -nogroup        # 查找无主文件

 

附录B:常用权限配置参考

 

# === Web应用 ===
/var/www/html           755   nginx:nginx    # Web根目录
/var/www/html/*.php     644   nginx:nginx    # PHP文件
/var/www/html/config    750   nginx:nginx    # 配置目录
/var/www/html/uploads   755   nginx:nginx    # 上传目录(SGID)
/var/www/html/.env      600   nginx:nginx    # 环境配置

# === SSH ===
~/.ssh                  700   user:user      # SSH目录
~/.ssh/id_rsa           600   user:user      # 私钥
~/.ssh/id_rsa.pub       644   user:user      # 公钥
~/.ssh/authorized_keys  600   user:user      # 授权密钥
~/.ssh/config           600   user:user      # SSH配置
/etc/ssh/sshd_config    600   root:root      # SSHD配置

# === 数据库 ===
/var/lib/mysql          750   mysql:mysql    # MySQL数据目录
/etc/mysql/my.cnf       640   root:mysql     # MySQL配置
/var/log/mysql          750   mysql:mysql    # MySQL日志

# === 系统关键文件 ===
/etc/passwd             644   root:root      # 用户信息
/etc/shadow             000   root:root      # 密码哈希
/etc/sudoers            440   root:root      # sudo配置
/etc/crontab            644   root:root      # 计划任务

# === 日志文件 ===
/var/log                755   root:root      # 日志目录
/var/log/messages       640   root:root      # 系统日志
/var/log/secure         600   root:root      # 安全日志

 

附录C:SELinux常用上下文类型

 

类型                          说明
------------------------------------------------------------
httpd_sys_content_t           Web服务器只读内容
httpd_sys_rw_content_t        Web服务器可读写内容
httpd_sys_script_exec_t       Web服务器可执行脚本
httpd_log_t                   Web服务器日志
mysqld_db_t                   MySQL数据库文件
mysqld_log_t                  MySQL日志
postgresql_db_t               PostgreSQL数据库文件
ssh_home_t                    SSH配置文件
sshd_key_t                    SSH主机密钥
user_home_t                   用户主目录内容
admin_home_t                  管理员主目录
tmp_t                         临时文件
var_log_t                     日志文件
etc_t                         配置文件
bin_t                         可执行文件
lib_t                         库文件
container_file_t              容器文件(Podman/Docker)
svirt_sandbox_file_t          容器沙箱文件

 

附录D:常见错误排查

错误信息 可能原因 排查方法
Permission denied 文件权限不足 ls -l , namei -l
Operation not permitted SELinux拒绝 ausearch -m avc
cannot execute binary file 缺少执行权限或架构不匹配 file , chmod +x
Text file busy 文件正在执行 lsof , fuser
Read-only file system 文件系统只读 mount , remount

附录E:术语表

术语 英文 说明
DAC Discretionary Access Control 自主访问控制,传统的owner/group/others模型
MAC Mandatory Access Control 强制访问控制,如SELinux、AppArmor
ACL Access Control List 访问控制列表,扩展的权限机制
SUID Set User ID 设置用户ID,执行时临时获得所有者权限
SGID Set Group ID 设置组ID
Sticky Bit - 粘滞位,防止删除他人文件
umask User file creation mask 文件创建掩码,控制新建文件的默认权限
Context Security Context 安全上下文,SELinux的核心概念
Boolean SELinux Boolean SELinux布尔值,控制策略行为的开关
Capability Linux Capability Linux能力,细粒度的root权限分解
Namespace Linux Namespace Linux命名空间,容器隔离的基础

写在最后

权限管理看似简单,实则博大精深。从最基础的rwx到复杂的SELinux策略,每一层都有其存在的意义。作为运维工程师,我们的职责不仅是让系统"能用",更要让它"安全"。

希望这篇文章能帮助你建立起完整的权限知识体系。如果你在实践中遇到问题,欢迎交流讨论。记住,在安全面前,没有"图方便"这回事。

祝工作顺利,系统安全!

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分