记一次 su 切换用户报 Permission denied:从排查修复到根因复现
说明:文中所有 IP 均为 RFC1918 私有段示例占位,主机名与终端用户名已脱敏为通用名称,请替换为您自己的环境值。
在 Linux 系统中,使用 root 用户执行 su - <user> 切换身份时,如果抛出 su: warning: cannot change directory... Permission denied 及 su: failed to execute /bin/bash: Permission denied 报错,通常说明系统基础文件、核心目录的权限结构或扩展属性(chattr)被篡改。
一、故障现象
执行用户切换命令时出现以下提示:
[root@server-01 ~]# su - opsuser
Last login: Thu Aug 27 21:17:59 CST 2026 on tty1
su: warning: cannot change directory to /home/opsuser: Permission denied
su: failed to execute /bin/bash: Permission denied
两条报错分别指向两个能力被破坏:
- 无法进入家目录 → 家目录或其上级路径丢失
x(寻址/穿越)权限; - 无法执行
/bin/bash→ shell 二进制本身或其所在目录丢失x权限。
二、问题排查与定位步骤
1. 检查基础可执行权限与家目录权限
检查 /bin/bash 及相关用户家目录是否缺失 x(可执行/寻址)权限:
ls -l /bin/bash
ls -ld /home/opsuser /home
正常标准:
/bin/bash权限应为-rwxr-xr-x(755)。/home权限应为drwxr-xr-x(755)。
2. 排查 SELinux 状态
检查是否由于 SELinux 上下文错乱或拦截导致的权限拒绝:
# 临时切换为宽容模式测试
setenforce 0
注意:若关闭 SELinux 后依旧报错,说明问题发生在系统标准权限(POSIX Mode)或文件系统属性(chattr)上。
3. 排查文件系统扩展属性(chattr)
检查核心二进制文件(如 /bin/bash)是否被错误设置了扩展属性:
lsattr /bin/bash
若输出中带有 c(Compressed)或 i(Immutable)标志(如 -------------c-- /bin/bash),会导致内核直接拒绝执行该二进制文件。
4. 使用 RPM 校验系统基础包属性(定位根因)
使用 RPM 包管理器对系统基础文件系统(filesystem)进行校验,查看全局权限是否异常:
rpm -V filesystem
示例输出:
.M....... /boot
.M....... /etc
.M....... /home
.M....... /usr
.M....... /usr/bin
.M....... /usr/lib64
标志含义:.M....... 中的 M 代表 Mode(权限位)发生了篡改,说明系统大量核心目录的默认权限已被批量破坏。
三、解决方案
明确根因为系统核心目录与包文件权限丢失后,无需逐个修改目录,直接利用 RPM 的权限恢复功能进行一键重置。
1. 一键恢复系统基础目录权限(关键步骤)
执行以下命令,将 filesystem 软件包管理的所有系统核心目录(如 /、/etc、/home、/usr/bin 等)恢复为官方默认权限:
rpm --setperms filesystem
2. 恢复核心目录所有权(属主/属组)
同步恢复目录的默认所有者:
rpm --setugids filesystem
3. 补充修复关键软件包权限(可选)
如果变更影响范围较大,可一并对 bash、coreutils 和 sudo 等核心组件重新刷入标准权限:
rpm --setperms bash coreutils sudo shadow-utils policycoreutils
rpm --setugids bash coreutils sudo shadow-utils policycoreutils
4. 重新校验与验证
重新执行校验命令,确保没有任何异常输出:
# 1. 确认权限已恢复(应无输出)
rpm -V filesystem
# 2. 测试切换用户
su - opsuser
切换成功后,系统权限即可彻底恢复正常。
四、根因复盘:权限是怎么被批量破坏的(故障复现)
修复完成后,回头查 history 命令记录,最终定位到根因:运维人员在终端里定义了一个临时变量,退出终端重新登录后变量失效为空,随后一条带着空变量和通配符的 chmod 命令,把整个根目录下的一级目录权限批量改掉了。
完整复现过程如下(已脱敏):
# 1. 准备测试目录
[root@server-01 ~]# ll /data/
total 0
-rw-r--r--. 1 root root 0 Aug 16 20:30 1
-rw-r--r--. 1 root root 0 Aug 16 20:30 2
-rw-r--r--. 1 root root 0 Aug 16 20:30 3
# 2. 定义临时变量(未 export,仅当前会话有效)
[root@server-01 ~]# DATA_DIR=/data
[root@server-01 ~]# exit
logout
Connection to 192.168.1.10 closed.
# 3. 重新登录,变量已失效
user@laptop ~ % ssh root@192.168.1.10
Activate the web console with: systemctl enable --now cockpit.socket
Last login: Mon Aug 17 03:26:07 2026 from 192.168.1.1
# 4. 灾难性命令:$DATA_DIR 为空,实际执行的是 chmod 600 /*
[root@server-01 ~]# chmod 600 $DATA_DIR/*
[root@server-01 ~]# echo $DATA_DIR
# 环境变量为空;若是 export 定义的变量则全局可用,不会出现此问题
# 5. /data 下的文件未受影响(chmod 600 /* 只命中了根下的一级目录)
[root@server-01 ~]# ll /data/
total 0
-rw-r--r--. 1 root root 0 Aug 16 20:30 1
-rw-r--r--. 1 root root 0 Aug 16 20:30 2
-rw-r--r--. 1 root root 0 Aug 16 20:30 3
# 6. 但整个 / 下的一级目录全部变成了 drw-------
[root@server-01 ~]# ls -l /
total 28
drw-------. 2 root root 6 Nov 3 2024 afs
lrwxrwxrwx. 1 root root 7 Nov 3 2024 bin -> usr/bin
drw-------. 6 root root 4096 Jul 3 15:51 boot
drw-------. 2 root root 33 Aug 16 20:30 data
drw-------. 20 root root 3340 Aug 16 20:24 dev
drw-------. 91 root root 8192 Aug 17 03:19 etc
drw-------. 2 root root 6 Nov 3 2024 home
lrwxrwxrwx. 1 root root 7 Nov 3 2024 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 Nov 3 2024 lib64 -> usr/lib64
drw-------. 2 root root 6 Nov 3 2024 media
drw-------. 2 root root 6 Nov 3 2024 mnt
drwxr-xr-x. 5 root root 56 Aug 14 14:00 opt
dr-xr-xr-x. 280 root root 0 Jul 21 21:45 proc
drw-------. 5 root root 4096 Aug 16 23:08 root
drw-------. 34 root root 980 Aug 17 03:20 run
lrwxrwxrwx. 1 root root 8 Nov 3 2024 sbin -> usr/sbin
drw-------. 2 root root 6 Nov 3 2024 srv
drwxr-xr-x. 12 root root 0 Jul 21 21:45 sys
drw-------. 9 root root 4096 Aug 17 03:27 tmp
drw-------. 12 root root 144 Jul 3 15:47 usr
drw-------. 21 root root 4096 Jul 7 15:45 var
随后故障现象立即出现:
[root@server-01 ~]# su - opsuser
su: warning: cannot change directory to /home/opsuser: Permission denied
su: failed to execute /bin/bash: Permission denied
复盘要点
- 根因:
DATA_DIR未export,重新登录后为空。chmod 600 $DATA_DIR/*经 shell 展开后实际变成了chmod 600 /*,把/boot、/etc、/home、/usr等所有一级目录批量改成了600(drw-------),目录丢失x位。 - 为什么
/bin/bash也执行不了:/bin是指向usr/bin的软链接,chmod默认跟随软链接,所以/usr/bin目录同样被改成drw-------。执行/bin/bash需要对/usr/bin有搜索(x)权限,于是报failed to execute。 - 为什么
/data里的文件没变:chmod 600 /*只作用于根目录下的一级条目本身,不递归,所以/data目录权限变了,其内部文件仍是644。
五、经验总结
- 排障路径:故障现象 → 基础权限检查 → SELinux → chattr 扩展属性 →
rpm -V全局校验,层层递进即可锁定根因。 - RPM 系发行版的权限修复利器:
rpm --setperms/rpm --setugids可以按 RPM 数据库记录的元数据一键还原权限与属主,比手工逐个chmod可靠得多。 history是最后的真相记录仪:权限"莫名"被改时,先翻命令历史,往往能找到那条肇事命令。- 防呆建议:
- 引用可能为空的变量时使用${DATA_DIR:?DATA_DIR is empty},变量为空时命令直接报错中止,而不是展开成/*;
- 脚本中开启set -u(nounset),引用未定义变量立即退出;
- 执行带通配符的chmod/chown/rm前,先echo一遍确认实际展开的目标列表;
- 临时变量如需跨会话使用,务必export或写入配置文件。