花拾录
← 返回知识库

sudo 管道喂密码叠加 heredoc,把 root 密码原样写进了配置文件

云计算 / 运维导入2026/09/220 阅读0 评论

你写了一段"自动写配置"的脚本:用一条带密码的 sudo 去提权,再用 heredoc 把一段配置内容写进目标文件。跑完看着很成功,可检查目标文件时,里面平白多出一行莫名其妙的字符串;更糟的是,这份配置被解析成了"对全网段开放",等于把你的密码暴露给了整个局域网。

现象

  • 执行完脚本后,目标配置文件里多出一行奇怪的字符串——看起来正是你的 root 密码;
  • 该配置随后被解析成一条"对全网段开放"的导出项,生效后对局域网敞开了;
  • 脚本本身没有报错,退出码正常,所以你以为"成功了"。

这类事故的可怕之处在于:它不报错,而且立刻生效。 密码被当成业务数据写进了对外的配置文件。

根因

根因是 stdin(标准输入)被两个东西抢着读,而它们互不知情。

你用的 sudo 封装通常是这种形式——用管道把密码喂给 sudo -S:

echo "$PASS" | sudo -S some_command

-S 的含义是"从标准输入读密码"。所以这条命令的 stdin 已经被密码占用了。

当这个封装再叠加一个 heredoc(用来提供要写入的文件内容)时,问题就来了:

echo "$PASS" | sudo -S bash -c "cat <<EOF > /etc/exports
/srv/data *(rw,sync)
EOF"

heredoc 的内容本意是"要写进文件的数据",但它同样是以 stdin 的形式喂给 bash -c 里的 cat 的。于是同一条管道里,stdin 上同时排着两样东西:一个是 echo 出来的密码,一个是 heredoc 的内容。

结果就是:cat 读到的第一个"行"其实是密码(因为它在管道最前面),它把密码当成了 heredoc 的第一行内容,写进了 /etc/exports。而真正的配置内容可能错位、缺失,或者把密码单独顶成了一条导出项——那条导出项往往形如"某网段可读写",一旦生效,就是对全网开放。

一句话机制:stdin 是独占资源。 管道和 heredoc 都想从它取数据,先到先得,另一方拿到的是错的东西。而 -S 又恰好把密码放在了这股竞争里。

解决

有四条纪律,按优先级排:

① 绝不给"管道喂密码的 sudo"再叠加 heredoc 或 stdin 重定向。 这是最根本的一条——只要不让 stdin 被两方争抢,事故就不会发生。

② 要写 root 文件,先在临时目录用普通权限拼好内容,再 sudo 拷贝过去。 推荐流程:

# 1. 在临时目录(普通权限即可)把内容写好
cat > /tmp/exports.new <<'EOF'
/srv/data <内网网段>(rw,sync)
EOF

# 2. 用 sudo 把它安装到目标位置(cp 只搬运文件,不占用 stdin 传内容)
echo "$PASS" | sudo -S -p '' cp /tmp/exports.new /etc/exports

# 3. 收尾
rm -f /tmp/exports.new

这里 cp 的输入是"文件路径参数",内容来自文件本身,不经过 stdin,所以与 -S 读密码不会冲突。

③ 改完必须验证真实生效的配置,而不是你以为写进去的内容。 对 NFS 导出这类配置,用它的专用查看命令:

sudo exportfs -v          # 查看实际生效的导出项
showmount -e <IP>         # 从客户端侧确认导出列表

这两条命令会告诉你内核/服务实际认了什么,而不是文件里"看起来"写了什么。写文件和生效是两件事,必须验证后者。

④ 出事后,先把被污染的版本备份留证,再重写并重新加载。 别急着覆盖——备份一份现场,方便排查是哪一步写错的:

sudo cp /etc/exports /etc/exports.broken.bak

然后重写正确内容、重新加载服务(如 sudo exportfs -ra),最后再次用 exportfs -v 确认。

延伸与预防

这条坑的普适教训是:stdin 是独占资源,两个东西同时想读它,一定有一方拿到错的东西。 凡是"管道 + heredoc + 需要 stdin 的命令"凑在一起的写法,都要警惕这场输入竞争。

几条可复用的原则:

  • 提权写文件,优先"先拼内容、再搬运",而不是"边提权边用 heredoc 生成"。
  • 改完配置永远要看"生效视图",别只看文件内容——exportfs -v、nginx -T、ss -tlnp 这类命令才是真相。
  • 远端无 tty 时,echo '<密码>' | sudo -S -p '' 命令 是常用手法,但正因它占用 stdin,绝不能和 heredoc 混用。

一句话:别让两样数据去抢同一个输入通道。

评论(0)

  • 还没有评论,来抢沙发~

相关文章