说明:文中所有 IP 均为 RFC1918 私有段示例占位,主机名与终端用户名已脱敏为通用名称,请替换为您自己的环境值。

在 Linux 系统中,使用 root 用户执行 su - <user> 切换身份时,如果抛出 su: warning: cannot change directory... Permission deniedsu: 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. 补充修复关键软件包权限(可选)

如果变更影响范围较大,可一并对 bashcoreutilssudo 等核心组件重新刷入标准权限:

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_DIRexport,重新登录后为空。chmod 600 $DATA_DIR/* 经 shell 展开后实际变成了 chmod 600 /*,把 /boot/etc/home/usr 等所有一级目录批量改成了 600drw-------),目录丢失 x 位。
  • 为什么 /bin/bash 也执行不了/bin 是指向 usr/bin 的软链接,chmod 默认跟随软链接,所以 /usr/bin 目录同样被改成 drw-------。执行 /bin/bash 需要对 /usr/bin 有搜索(x)权限,于是报 failed to execute
  • 为什么 /data 里的文件没变chmod 600 /* 只作用于根目录下的一级条目本身,不递归,所以 /data 目录权限变了,其内部文件仍是 644

五、经验总结

  1. 排障路径:故障现象 → 基础权限检查 → SELinux → chattr 扩展属性 → rpm -V 全局校验,层层递进即可锁定根因。
  2. RPM 系发行版的权限修复利器rpm --setperms / rpm --setugids 可以按 RPM 数据库记录的元数据一键还原权限与属主,比手工逐个 chmod 可靠得多。
  3. history 是最后的真相记录仪:权限"莫名"被改时,先翻命令历史,往往能找到那条肇事命令。
  4. 防呆建议
    - 引用可能为空的变量时使用 ${DATA_DIR:?DATA_DIR is empty},变量为空时命令直接报错中止,而不是展开成 /*
    - 脚本中开启 set -u(nounset),引用未定义变量立即退出;
    - 执行带通配符的 chmod/chown/rm 前,先 echo 一遍确认实际展开的目标列表;
    - 临时变量如需跨会话使用,务必 export 或写入配置文件。
文章作者: emporer
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Emporer-Linux
喜欢就支持一下吧