使用 Shell 脚本批量部署 SSH 公钥最推荐的做法是在循环中调用 ssh-copy-id 命令,适用于 Linux 和 macOS 等类 Unix 系统。操作前需确保本地已生成 SSH 密钥对,且目标服务器开放 22 端口并允许密码认证,主要风险在于文件权限配置错误会导致免密登录失效。
先说结论:批量部署 SSH 公钥应优先使用原生 ssh-copy-id 配合主机列表循环,避免手动修改配置文件带来的权限风险。
- 适合:拥有多台 Linux/Unix 服务器且需要统一密钥管理的运维场景
- 先看:本地是否已生成 SSH 密钥对,目标服务器是否安装 openssh-clients
- 建议:部署完成后务必使用 BatchMode 模式验证免密是否真正生效
命令速用版
以下脚本假设本地公钥已生成,且目标服务器列表保存在 hosts.txt 中,每行一个 IP 或主机名。
#!/bin/bash USER="root" KEY_FILE=~/.ssh/id_rsa.pub HOSTS_FILE="hosts.txt" while read -r HOST; do echo "Deploying to $HOST..." ssh-copy-id -i $KEY_FILE $USER@$HOST done < $HOSTS_FILE
如果目标服务器没有 ssh-copy-id 命令,可以使用以下替代命令手动追加公钥。
cat ~/.ssh/id_rsa.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"
为什么会这样
SSH 免密登录的本质是将本地公钥内容追加到目标服务器的 ~/.ssh/authorized_keys 文件中。
SSH 服务端在验证时会检查该文件权限必须为 600,所属目录 .ssh 权限必须为 700,且所有者必须是登录用户本人。Shell 脚本批量部署的核心难点不在于传输公钥,而在于确保所有目标服务器的目录结构和权限符合 OpenSSH 的安全规范,否则服务端会拒绝使用公钥认证。
分步处理
第一步:生成本地密钥对
在控制机执行 ssh-keygen -t rsa -b 2048,默认路径为 ~/.ssh/id_rsa 和 ~/.ssh/id_rsa.pub。如果已存在可跳过,注意不要覆盖现有密钥。
第二步:准备主机列表
创建 hosts.txt 文件,填入目标服务器 IP,确保控制机能通过密码登录这些服务器。
第三步:执行部署脚本
运行上述速用版脚本,过程中需要输入一次目标服务器的登录密码。脚本会自动创建目录并设置权限。
第四步:处理已知主机检查
如果是全新环境,建议在脚本中加入 -o StrictHostKeyChecking=no 参数,避免首次连接时因指纹确认中断脚本执行。
怎么验证是否生效
使用 -o BatchMode=yes 参数测试 SSH 连接,该参数禁止密码输入提示,如果连接成功则说明免密配置正确。
ssh -o BatchMode=yes -o StrictHostKeyChecking=no user@host "echo success"
如果输出 success 且没有提示输入密码,则部署成功。如果提示 Permission denied (publickey,password),则说明公钥未生效或权限错误。
常见坑
- 权限错误:目标服务器 ~/.ssh 目录权限不能超过 700,authorized_keys 不能超过 600,否则 SSH 服务端会忽略该文件。
- SELinux 限制:在 CentOS/RHEL 系统中,如果开启 SELinux,可能需要运行 restorecon -R -v ~/.ssh 修复安全上下文。
- 根目录权限:部分严格配置下,用户家目录 ~ 的权限不能是 777,否则认证会被拒绝。
- 公钥追加:多次运行脚本会导致公钥重复追加到 authorized_keys,建议先检查是否存在相同密钥。
常见问题
目标服务器没有 ssh-copy-id 命令怎么办?
使用 cat 管道配合 ssh 手动创建文件和设置权限,参考“命令速用版”中的替代命令。
如何实现完全无人值守输入密码?
可以安装 sshpass 工具配合脚本使用,但明文密码存在安全风险,仅建议在临时内网环境使用。
部署后仍然提示输入密码?
检查目标服务器 /var/log/secure 或 /var/log/auth.log 日志,确认是否因权限问题导致公钥被拒绝。
参考来源
- OpenSSH Manual: ssh-copy-id
- Red Hat Documentation: Configuring SSH key-based authentication