[2026 Remastered · 07] 为什么 .erlang.cookie 会莫名变成 root:root?RabbitMQ 启动 eacces 报错溯源、mnesia 漏修隐患与 0400 铁律
🛸 站长重制导读(Anti Gravi's Remaster Note)
本文是 「来时路 · 2026 重制企划(7/12)」 的第 7 篇,对应本站 2020 年 8 月 3 日发布的 《CentOS 6 rabbitmq-server无法启动》(
CID: 34)。2020 年那篇排障笔记记录了一个非常经典的 Erlang / RabbitMQ 生产故障:
service rabbitmq-server status抛出长串 Erlang 异常栈"Error when reading /var/lib/rabbitmq/.erlang.cookie: eacces",通过ls -al /var/lib/rabbitmq/发现.erlang.cookie的属主是root:root,执行chown rabbitmq:rabbitmq /var/lib/rabbitmq/.erlang.cookie后恢复启动。当年顺着错误码
eacces(EACCES: Permission denied)定位到文件属主的排障思路非常清晰!但当我们今天拿着放大镜重新审视你当年在原稿第 132~138 行贴出的那段ls -al真实终端输出时,却在里面发现了一个当年差点擦肩而过的二次故障隐患,以及这起事故背后真正的“案发第一现场”!
一、 破案微距镜头:重看 2020 原稿 (CID: 34) 第 132 行的真实终端输出
我们先一字不差地调出你在 2020 年原稿里贴出的 ls -al 现场证据:
# 2020 原稿 (CID: 34) 第 132~138 行真实终端输出:
[root@gzb ~]# ls -al /var/lib/rabbitmq/
total 16
drwxr-xr-x. 3 root root 4096 Jun 24 2019 .
drwxr-xr-x. 21 root root 4096 Aug 3 03:35 ..
-r--------. 1 root root 20 Jun 24 2019 .erlang.cookie
drwxr-x---. 4 root root 4096 Jun 24 10:21 mnesia
然后原稿第 145 行给出的修复命令是:
# 2020 原稿第 145 行修复命令:
chown rabbitmq:rabbitmq /var/lib/rabbitmq/.erlang.cookie
请仔细盯住上面 ls -al /var/lib/rabbitmq/ 的每一行!这里藏着三个极其深刻的生产运维细节:
| 观察点 | 2020 原稿终端现场证据 | 深度筛查与潜在隐患剖析 | 2026 Remastered 标准根治动作 |
|---|---|---|---|
1. 为什么 .erlang.cookie 和 . 目录会是 root:root? |
.(/var/lib/rabbitmq)、.erlang.cookie 和 mnesia 的属主全都是 root:root,且创建时间为 Jun 24 2019 |
谁把它们变成 root 的?正常通过 RPM/APT 安装并用 service / systemctl 启动时,脚本会通过 su - rabbitmq 或 User=rabbitmq 降权运行,生成的文件天然属于 rabbitmq:rabbitmq。之所以全变成 root:root,是因为在 Jun 24 2019 那天,有人以 root 身份在命令行直接裸敲了 rabbitmq-server(或 rabbitmq-plugins)!用 root 裸跑时,Erlang VM 就会用 root 身份创建或覆写 .erlang.cookie 与 mnesia 目录,等下次改用正规 service rabbitmq-server start(以 rabbitmq 用户启动)时,立刻因为读不到 root 的文件而暴毙! |
严禁以 root 身份直接在前台裸跑任何后台服务二进制(如 rabbitmq-server、redis-server、mongod),一律通过 systemctl start <service> 或 sudo -u rabbitmq ... 执行 |
2. 原稿漏修了第 137 行的 mnesia 数据库目录! |
看第 137 行:drwxr-x---. 4 root root 4096 Jun 24 10:21 mnesia以及第 134 行: drwxr-xr-x. 3 root root 4096 Jun 24 2019 . |
注意看 mnesia 目录的权限位:drwxr-x---(0750),属主 root:root!这意味着除了 root 用户和 root 组之外,其他用户(包括 rabbitmq 运行账户)对 mnesia 目录的权限是 ---(连进入目录的 x 和读取的 r 都没有)!而且父目录 /var/lib/rabbitmq 本身也是 drwxr-xr-x root:root(rabbitmq 用户无法在里面新建文件)。如果只单独 chown 了 .erlang.cookie,当 RabbitMQ 读写队列持久化数据或新建节点元数据时就会再次因为 eacces 崩溃! |
必须对整个数据目录与日志目录执行递归属主修复:chown -R rabbitmq:rabbitmq /var/lib/rabbitmq /var/log/rabbitmq |
3. Erlang 对 .erlang.cookie 的 0400 独享权限铁律 |
第 136 行权限为 -r-------- (0400) |
很多新手遇到权限报错时喜欢随手敲 chmod 777 或 chmod 644。但在 Erlang 标准库 lib/kernel/src/auth.erl(即原稿报错栈第 37 行的 auth.erl)中,内核会显式调用 check_cookie() 检查文件 mode:一旦发现 Group 或 Others 有任何读/写/执行权限(即 (Mode & 0077) != 0),Erlang 同样会拒绝启动,并抛出 "Cookie file must be accessible by owner only"! |
严格保持 .erlang.cookie 为 0400 (-r--------)(或 0600),目录保持 0750 |
二、 底层源码级解密:原稿报错栈里的 auth.erl 究竟在检查什么?
回看你 2020 年原稿第 23~43 行捕获的那段 Erlang 崩溃堆栈:
Error: Failed to initialize erlang distribution: {
{shutdown, {failed_to_start_child, auth, {
"Error when reading /var/lib/rabbitmq/.erlang.cookie: eacces",
[{auth, init_cookie, 0, [{file, "auth.erl"}, {line, 286}]}, ...]
}}}
}
什么是 .erlang.cookie?
- RabbitMQ 底层运行在 Erlang BEAM 虚拟机 之上;不仅多节点集群间通信依靠 Erlang 分布式协议(Erlang Distribution),连你在本机敲
rabbitmqctl status,本质上也是启动了一个临时的 Erlang 节点(如原稿第 99 行的'rabbitmq-cli-88'),通过 TCP 连接本机 RabbitMQ 节点并比对双方的.erlang.cookie(一个 20 字节的共享密钥字符串) 来完成身份认证! - 在 OTP
auth.erl的read_cookie/2函数中,Erlang VM 强制执行两道铁门:- 第一道门(系统调用
file:read_file_info):以当前进程 UID(rabbitmq)打开/var/lib/rabbitmq/.erlang.cookie。因为文件属主是root:root且权限为0400,内核直接返回 POSIX 错误码EACCES(在 Erlang 中映射为原子eacces)——这就是你当年遇到的报错! - 第二道门(权限掩码位与校验):哪怕文件能读出来,如果
(Mode band 8#077) =/= 0(即属组或其他人有任何权限位),auth.erl会立即抛出Cookie file ... must be accessible by owner only并终止节点启动。
- 第一道门(系统调用
三、 2026 生产级 RabbitMQ 权限自检与一键修复标准流程
当你在生产环境接手一台被前人用 root 误操作污染过权限的 RabbitMQ 节点时,请按以下完整清单一次性根治所有隐患:
#!/usr/bin/env bash
set -Eeuo pipefail
RABBIT_DATA="/var/lib/rabbitmq"
RABBIT_LOG="/var/log/rabbitmq"
COOKIE_FILE="${RABBIT_DATA}/.erlang.cookie"
# 1. 确保没有残留的 root 裸跑 Erlang (beam.smp / epmd) 僵尸进程霸占 4369/25672/5672 端口
if pgrep -u root -f "beam.smp.*rabbitmq" >/dev/null; then
echo "[!] Detected rogue root-owned RabbitMQ beam.smp process! Stopping..."
pkill -TERM -u root -f "beam.smp.*rabbitmq" || true
sleep 2
fi
# 2. 递归修复数据目录(含 mnesia 子目录!)与日志目录的属主为 rabbitmq:rabbitmq
sudo chown -R rabbitmq:rabbitmq "$RABBIT_DATA" "$RABBIT_LOG"
# 3. 严格锁定目录权限为 0750,锁定 .erlang.cookie 为 Erlang 强制要求的 0400 (-r--------)
sudo chmod 750 "$RABBIT_DATA"
if [[ -f "$COOKIE_FILE" ]]; then
sudo chmod 400 "$COOKIE_FILE"
fi
# 4. 若管理员在 root 下执行 rabbitmqctl 报 Cookie 不匹配,将服务的 Cookie 同步至 /root/.erlang.cookie
if [[ -f "$COOKIE_FILE" ]]; then
sudo install -m 400 -o root -g root "$COOKIE_FILE" /root/.erlang.cookie
fi
# 5. 通过 systemd 标准拉起服务并核验健康状态
sudo systemctl start rabbitmq-server
sudo rabbitmq-diagnostics check_running
[!TIP]
为什么第 4 步顺手同步了/root/.erlang.cookie?
因为当运维登录root账号执行rabbitmqctl list_queues时,CLI 默认读取的是/root/.erlang.cookie,而后台rabbitmq-server服务读取的是/var/lib/rabbitmq/.erlang.cookie。如果之前用root裸跑过导致两边文件内容不一致,rabbitmqctl就会报TCP connection succeeded but Erlang distribution failed (Authentication failed)。保持两处文件一致且均为0400,即可彻底杜绝此类故障!