说实话,Linux 权限这块我踩过不少坑。记得刚入行那会儿,有次为了图省事直接 chmod 777 -R /var/www,结果被老大骂了一顿——"你这是把大门敞开让小偷随便进啊!"
Linux 的权限体系是整个系统安全的基石。从最基础的 rwx 三权分立,到 SUID/SGID 这些"特权通道",再到 ACL 细粒度控制,最后是 SELinux/AppArmor 这种强制访问控制,形成了一套层层递进的安全防护网。
2026 年了,随着容器化和云原生的普及,权限管理变得更加复杂。你不仅要管好宿主机的权限,还得搞清楚容器内的 UID 映射、Rootless 容器这些新玩意儿。这篇文章就把这些年我在生产环境中积累的经验都掏出来,希望能帮你少走弯路。
# 检查系统版本cat /etc/os-release# 检查内核版本(需要 5.4+ 以获得完整特性支持)uname -r# 检查文件系统类型df -Th# 检查是否支持 ACLgrep -i acl /boot/config-$(uname -r) 2>/dev/null || zcat /proc/config.gz 2>/dev/null | grep -i acl# 检查 SELinux 状态(RHEL 系)getenforce 2>/dev/null || echo"SELinux not installed"# 检查 AppArmor 状态(Debian 系)aa-status 2>/dev/null || echo"AppArmor not installed"# RHEL/CentOS 系列sudo dnf install -y acl attr policycoreutils-python-utils setools-console# Debian/Ubuntu 系列sudo apt updatesudo apt install -y acl attr apparmor-utils auditd先来复习一下基础,这块很多人以为自己懂了,其实细节上还是会犯错。
# 权限的数字表示法# r=4, w=2, x=1# 755 = rwxr-xr-x = 所有者全权限,组和其他人只读+执行# 查看文件权限的详细信息ls -la /etc/passwd# -rw-r--r-- 1 root root 2847 Jan 15 10:23 /etc/passwd# │├─┤├─┤├─┤# │ │ │ └── 其他用户权限 (r--)# │ │ └───── 所属组权限 (r--)# │ └──────── 所有者权限 (rw-)# └────────── 文件类型 (-=普通文件, d=目录, l=链接)# 修改权限的两种方式# 方式一:数字法(推荐,精确控制)chmod 644 config.yml # rw-r--r--chmod 755 script.sh # rwxr-xr-xchmod 600 .ssh/id_rsa # rw------- (私钥必须是600!)# 方式二:符号法(适合微调)chmod u+x script.sh # 给所有者加执行权限chmod g-w config.yml # 去掉组的写权限chmod o= secret.txt # 清空其他用户的所有权限chmod a+r public.html # 所有人加读权限我踩过的坑:有次部署应用,死活启动不了,查了半天发现是配置文件权限给成了 000。所以现在我养成习惯,部署脚本里一定会加权限检查。
目录的权限和文件不太一样,这点很多新手会搞混:
# 目录权限含义:# r - 可以列出目录内容(ls)# w - 可以在目录中创建/删除文件# x - 可以进入目录(cd)和访问目录中的文件# 实验:创建测试目录mkdir -p /tmp/perm_test && cd /tmp/perm_test# 场景1:只有 r 没有 xmkdir test_r && chmod 444 test_recho"hello" > test_r/file.txt 2>/dev/null # 会失败ls test_r/ # 能看到文件名,但看不到详情cat test_r/file.txt # 失败,因为没有 x 权限# 场景2:只有 x 没有 rmkdir test_x && chmod 111 test_xecho"hello" > test_x/file.txtls test_x/ # 失败,不能列目录cat test_x/file.txt # 成功!知道文件名就能访问# 场景3:生产环境常用配置chmod 755 /var/www/html # Web 目录,所有人可读可进入chmod 750 /app/config # 配置目录,组内可读,其他人禁止chmod 700 /root/.ssh # SSH 目录,仅所有者可访问这三个特殊权限是很多安全问题的根源,但用好了也是利器:
# SUID (Set User ID) - 数字表示:4xxx# 执行文件时,临时获得文件所有者的权限# 典型例子:passwd 命令需要修改 /etc/shadow,普通用户执行时临时获得 root 权限ls -la /usr/bin/passwd# -rwsr-xr-x 1 root root 68208 Mar 14 2023 /usr/bin/passwd# ^-- 注意这个 s,表示设置了 SUID# 设置 SUIDchmod u+s /usr/local/bin/myappchmod 4755 /usr/local/bin/myapp# SGID (Set Group ID) - 数字表示:2xxx# 对文件:执行时临时获得文件所属组的权限# 对目录:在该目录下创建的文件自动继承目录的所属组(团队协作神器!)# 团队共享目录配置mkdir -p /data/team_projectchown root:developers /data/team_projectchmod 2775 /data/team_project# 现在 developers 组的成员在这里创建的文件,所属组都是 developers# Sticky Bit - 数字表示:1xxx# 只对目录有效:目录中的文件只能被文件所有者或 root 删除# 典型例子:/tmp 目录ls -ld /tmp# drwxrwxrwt 15 root root 4096 Jan 20 10:00 /tmp# ^-- 注意这个 t,表示设置了 Sticky Bit# 设置 Sticky Bitchmod +t /data/sharedchmod 1777 /data/shared安全警告:SUID 是安全审计的重点关注对象。定期检查系统中的 SUID 文件:
# 查找所有 SUID 文件find / -perm -4000 -type f 2>/dev/null# 查找所有 SGID 文件find / -perm -2000 -type f 2>/dev/null# 生产环境建议:建立基线,定期对比find / -perm -4000 -type f 2>/dev/null | sort > /var/log/suid_baseline_$(date +%Y%m%d).txt# 修改所有者chown nginx /var/www/html/index.html# 修改所有者和所属组chown nginx:www-data /var/www/html/index.html# 只修改所属组chgrp www-data /var/www/html/index.html# 或者chown :www-data /var/www/html/index.html# 递归修改(谨慎使用!)chown -R nginx:www-data /var/www/html/# 只修改符合条件的文件(更安全的做法)find /var/www/html -type f -exec chown nginx:www-data {} \;find /var/www/html -type d -exec chown nginx:www-data {} \;# 参考符号链接的目标(默认不跟随)chown -h nginx:www-data /var/www/html/link # 修改链接本身chown nginx:www-data /var/www/html/link # 修改链接指向的文件传统的 rwx 权限有个致命缺陷:只能设置一个所有者和一个所属组。如果你想让用户 A 有读写权限,用户 B 只有读权限,用户 C 完全没权限,传统权限就搞不定了。这时候 ACL 就派上用场了。
# 查看文件的 ACLgetfacl /data/project/config.yml# 输出示例:# file: data/project/config.yml# owner: root# group: developers# user::rw-# user:alice:rw- # alice 用户有读写权限# user:bob:r-- # bob 用户只有读权限# group::r--# group:ops:rw- # ops 组有读写权限# mask::rw-# other::---# 设置用户 ACLsetfacl -m u:alice:rw /data/project/config.yml # 给 alice 读写权限setfacl -m u:bob:r /data/project/config.yml # 给 bob 只读权限# 设置组 ACLsetfacl -m g:ops:rw /data/project/config.yml # 给 ops 组读写权限# 删除特定 ACLsetfacl -x u:alice /data/project/config.yml # 删除 alice 的 ACLsetfacl -x g:ops /data/project/config.yml # 删除 ops 组的 ACL# 删除所有 ACL(恢复到传统权限)setfacl -b /data/project/config.yml# 递归设置 ACLsetfacl -R -m u:alice:rwX /data/project/ # X 表示仅对目录设置执行权限# 设置默认 ACL(新创建的文件自动继承)setfacl -d -m u:alice:rw /data/project/ # 目录下新文件自动给 alice 读写权限setfacl -d -m g:developers:rwx /data/project/ # 目录下新文件自动给 developers 组全权限ACL 的 mask 机制:这是个容易被忽略但很重要的概念。mask 定义了 ACL 权限的上限。
# 查看当前 maskgetfacl /data/project/config.yml | grep mask# 设置 mask(限制所有 ACL 的最大权限)setfacl -m m::r /data/project/config.yml# 即使 alice 设置了 rw 权限,实际生效的也只有 r(被 mask 限制)# 注意:chmod 会影响 mask!chmod 640 /data/project/config.yml# 这会把 mask 设置为 r--,可能导致 ACL 权限失效我的经验:ACL 虽然强大,但也增加了复杂度。我的建议是:
#!/bin/bash# 文件名:check_permissions.sh# 用途:验证关键目录和文件的权限配置RED='\033[0;31m'GREEN='\033[0;32m'YELLOW='\033[1;33m'NC='\033[0m'check_permission() {local path=$1local expected_perm=$2local expected_owner=$3local expected_group=$4if [[ ! -e "$path" ]]; thenecho -e "${RED}[FAIL]${NC}$path does not exist"return 1filocal actual_perm=$(stat -c "%a""$path")local actual_owner=$(stat -c "%U""$path")local actual_group=$(stat -c "%G""$path")local status=0if [[ "$actual_perm" != "$expected_perm" ]]; thenecho -e "${RED}[FAIL]${NC}$path permission: expected $expected_perm, got $actual_perm" status=1fiif [[ "$actual_owner" != "$expected_owner" ]]; thenecho -e "${RED}[FAIL]${NC}$path owner: expected $expected_owner, got $actual_owner" status=1fiif [[ "$actual_group" != "$expected_group" ]]; thenecho -e "${YELLOW}[WARN]${NC}$path group: expected $expected_group, got $actual_group"fiif [[ $status -eq 0 ]]; thenecho -e "${GREEN}[OK]${NC}$path"fireturn$status}echo"=== Permission Check Report ==="echo"Date: $(date)"echo""# 检查关键系统文件echo"--- System Files ---"check_permission "/etc/passwd""644""root""root"check_permission "/etc/shadow""640""root""shadow"check_permission "/etc/ssh/sshd_config""600""root""root"# 检查 SSH 目录echo""echo"--- SSH Directory ---"check_permission "$HOME/.ssh""700""$USER""$USER"check_permission "$HOME/.ssh/authorized_keys""600""$USER""$USER" 2>/dev/nullcheck_permission "$HOME/.ssh/id_rsa""600""$USER""$USER" 2>/dev/null# 检查 Web 目录(如果存在)if [[ -d "/var/www/html" ]]; thenecho""echo"--- Web Directory ---" check_permission "/var/www/html""755""www-data""www-data"fiecho""echo"=== Check Complete ==="#!/bin/bash# 文件名:audit_suid.sh# 用途:审计系统中的 SUID/SGID 文件BASELINE_FILE="/var/log/suid_baseline.txt"CURRENT_FILE="/tmp/suid_current_$(date +%Y%m%d).txt"# 生成当前 SUID 文件列表find / -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null | sort > "$CURRENT_FILE"if [[ -f "$BASELINE_FILE" ]]; thenecho"=== SUID/SGID File Changes ==="echo""# 新增的 SUID 文件 new_files=$(comm -13 "$BASELINE_FILE""$CURRENT_FILE")if [[ -n "$new_files" ]]; thenecho"!!! NEW SUID/SGID FILES DETECTED !!!"echo"$new_files"echo""fi# 删除的 SUID 文件 removed_files=$(comm -23 "$BASELINE_FILE""$CURRENT_FILE")if [[ -n "$removed_files" ]]; thenecho"--- Removed SUID/SGID files ---"echo"$removed_files"echo""fiif [[ -z "$new_files" && -z "$removed_files" ]]; thenecho"No changes detected since last baseline."fielseecho"No baseline file found. Creating baseline..." cp "$CURRENT_FILE""$BASELINE_FILE"echo"Baseline created at $BASELINE_FILE"echo""echo"Current SUID/SGID files:" cat "$CURRENT_FILE"fiSELinux 是强制访问控制(MAC)的实现,比传统权限更加严格。很多人遇到问题就直接 setenforce 0 关掉,这是非常不好的习惯。
# 查看 SELinux 状态getenforce# Enforcing = 强制模式(生产环境推荐)# Permissive = 宽容模式(只记录不阻止,调试用)# Disabled = 禁用sestatus# 查看详细状态# 临时切换模式(重启后失效)sudo setenforce 0 # 切换到 Permissivesudo setenforce 1 # 切换到 Enforcing# 永久修改(需要重启)sudo vim /etc/selinux/config# SELINUX=enforcing# 查看文件的 SELinux 上下文ls -Z /var/www/html/# -rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 index.html# └─────────────────────────────────────────┘# SELinux 上下文# 修改文件的 SELinux 上下文# 临时修改(重新标记后会恢复)chcon -t httpd_sys_content_t /var/www/html/newfile.html# 永久修改(推荐)semanage fcontext -a -t httpd_sys_content_t "/data/web(/.*)?"restorecon -Rv /data/web/# 查看 SELinux 布尔值getsebool -a | grep httpd# 设置布尔值(允许 httpd 连接网络)setsebool -P httpd_can_network_connect onSELinux 故障排查:
# 查看 SELinux 拒绝日志sudo ausearch -m avc -ts recent# 使用 audit2why 分析原因sudo ausearch -m avc -ts recent | audit2why# 生成允许规则(谨慎使用!)sudo ausearch -m avc -ts recent | audit2allow -M mypolicysudo semodule -i mypolicy.pp# 实用技巧:临时切换到 Permissive 模式测试sudo setenforce 0# 测试功能是否正常# 如果正常,说明是 SELinux 问题sudo setenforce 1# 然后根据日志修复 SELinux 配置# 查看 AppArmor 状态sudo aa-status# 查看特定程序的配置文件cat /etc/apparmor.d/usr.sbin.nginx# AppArmor 模式# enforce - 强制模式# complain - 投诉模式(只记录不阻止)# disabled - 禁用# 切换到投诉模式(调试用)sudo aa-complain /usr/sbin/nginx# 切换到强制模式sudo aa-enforce /usr/sbin/nginx# 禁用特定配置sudo aa-disable /usr/sbin/nginx# 重新加载配置sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx# 查看日志sudo dmesg | grep apparmorsudo journalctl -k | grep apparmor自定义 AppArmor 配置示例:
# /etc/apparmor.d/usr.local.bin.myapp#include <tunables/global>/usr/local/bin/myapp {#include <abstractions/base>#include <abstractions/nameservice># 可执行文件 /usr/local/bin/myapp mr,# 配置文件(只读) /etc/myapp/** r,# 数据目录(读写) /var/lib/myapp/** rw,# 日志目录 /var/log/myapp/** w,# 临时文件 /tmp/myapp-* rw,# 网络访问 network inet stream, network inet dgram,# 拒绝其他所有访问 deny /etc/shadow r, deny /etc/passwd w,}2026 年了,不懂容器权限基本没法干活。这块坑特别多,我来详细说说。
# Docker 默认以 root 运行,这是个安全隐患docker run -it ubuntu whoami# root# 指定用户运行(推荐)docker run -it --user 1000:1000 ubuntu whoami# 会显示 uid=1000# 在 Dockerfile 中指定用户# Dockerfile 示例cat << 'EOF' > DockerfileFROM ubuntu:22.04# 创建非 root 用户RUN groupadd -r appgroup && useradd -r -g appgroup appuser# 设置工作目录权限WORKDIR /appCOPY --chown=appuser:appgroup . .# 切换到非 root 用户USER appuserCMD ["./myapp"]EOFUID 映射问题:容器内的 UID 和宿主机是共享的!
# 容器内 UID 1000 = 宿主机 UID 1000# 这可能导致权限问题# 查看容器内进程的实际 UIDdocker run -d --name test nginxps aux | grep nginx# 你会看到宿主机上显示的是 UID,不是用户名# 解决方案:使用 User Namespace(Rootless 容器)# Docker Rootless 模式dockerd-rootless-setuptool.sh install# 验证 Rootless 模式docker info | grep -i rootlessKubernetes 中的权限控制:
# Pod Security Context 示例apiVersion:v1kind:Podmetadata:name:secure-podspec:securityContext:runAsNonRoot:truerunAsUser:1000runAsGroup:1000fsGroup:1000containers:-name:appimage:myapp:latestsecurityContext:allowPrivilegeEscalation:falsereadOnlyRootFilesystem:truecapabilities:drop:-ALLadd:-NET_BIND_SERVICE# 只添加必要的 capability#!/bin/bash# 文件名:setup_web_permissions.sh# 用途:配置 Nginx Web 服务器的权限WEB_ROOT="/var/www/html"WEB_USER="www-data"WEB_GROUP="www-data"UPLOAD_DIR="${WEB_ROOT}/uploads"# 创建目录结构mkdir -p "${WEB_ROOT}"/{static,uploads,logs}# 设置所有权chown -R ${WEB_USER}:${WEB_GROUP}${WEB_ROOT}# 设置目录权限find ${WEB_ROOT} -type d -exec chmod 755 {} \;# 设置文件权限find ${WEB_ROOT} -type f -exec chmod 644 {} \;# 上传目录特殊处理(需要写权限,但禁止执行)chmod 775 ${UPLOAD_DIR}# 在 Nginx 配置中禁止执行上传目录的脚本# location /uploads/ {# location ~ \.(php|pl|py|sh)$ { deny all; }# }# 配置文件权限(敏感信息)chmod 640 /etc/nginx/conf.d/*.confchown root:${WEB_GROUP} /etc/nginx/conf.d/*.confecho"Web permissions configured successfully!"这是安全的黄金法则,说起来简单做起来难。
# 错误示范(千万别这么干!)chmod 777 /var/www/html # 全开放,等着被黑吧chmod -R 777 / # 系统直接废了# 正确做法:按需分配# 1. 先确定谁需要访问# 2. 确定需要什么权限(读/写/执行)# 3. 只给必要的权限# 常用权限速查# 644 - 普通文件(所有者读写,其他人只读)# 755 - 可执行文件/目录(所有者全权限,其他人读+执行)# 600 - 敏感文件(仅所有者读写)# 700 - 私有目录(仅所有者访问)# 640 - 配置文件(所有者读写,组内只读)# 750 - 应用目录(所有者全权限,组内读+执行)#!/bin/bash# 文件名:security_hardening.sh# 用途:系统权限安全加固echo"=== Starting Security Hardening ==="# 1. 关键系统文件权限chmod 644 /etc/passwdchmod 640 /etc/shadowchmod 644 /etc/groupchmod 640 /etc/gshadowchmod 600 /etc/ssh/sshd_configchmod 700 /root# 2. 限制 cron 访问chmod 600 /etc/crontabchmod 700 /etc/cron.dchmod 700 /etc/cron.dailychmod 700 /etc/cron.hourly# 3. 设置 umask(新文件默认权限)# 在 /etc/profile 或 /etc/bashrc 中设置# umask 027 # 新文件 640,新目录 750# 4. 禁用不必要的 SUID# 根据实际需求决定是否移除# chmod u-s /usr/bin/chage# chmod u-s /usr/bin/gpasswdecho"=== Security Hardening Complete ==="lsattrchattr -i 移除 | ||
mount -o remount,rw | ||
# 这些命令可能导致系统崩溃,请三思!# 1. 递归修改根目录权限chmod -R 777 / # 绝对禁止!chown -R user:group / # 绝对禁止!# 2. 修改关键系统文件chmod 777 /etc/shadow # 安全漏洞chmod 777 /etc/sudoers # sudo 会拒绝工作# 3. 移除自己的权限chmod 000 ~/.bashrc # 可能无法登录# 第一步:检查基础权限ls -la /path/to/filestat /path/to/file# 第二步:检查 ACLgetfacl /path/to/file# 第三步:检查扩展属性lsattr /path/to/file# 第四步:检查 SELinux(RHEL 系)ls -Z /path/to/fileausearch -m avc -ts recent# 第五步:检查 AppArmor(Debian 系)aa-statusdmesg | grep apparmor# 使用 auditd 监控权限变更# 安装 auditdsudo apt install auditd # Debian/Ubuntusudo dnf install audit # RHEL/CentOS# 添加审计规则sudo auditctl -w /etc/passwd -p wa -k passwd_changessudo auditctl -w /etc/shadow -p wa -k shadow_changessudo auditctl -w /etc/sudoers -p wa -k sudoers_changes# 查看审计日志sudo ausearch -k passwd_changes# 备份文件权限getfacl -R /data > /backup/data_acl_backup.txt# 恢复文件权限setfacl --restore=/backup/data_acl_backup.txt作者寄语:权限管理看似简单,实则博大精深。记住最小权限原则,养成良好习惯,你的系统会更加安全。有问题欢迎交流!