ssh-copy-id卡在“Now try logging into the machine”表明公钥已写入但后续密码验证失败,常见原因是目标主机禁用密码登录(PasswordAuthentication no),此时应立即检查远程~/.ssh/authorized_keys内容及权限(目录700、文件600),并确认sshd_config中PubkeyAuthentication yes、AuthorizedKeysFile路径正确及SELinux上下文无误。

ssh-copy-id 为什么卡在 “Now try logging into the machine”
这不是成功提示,而是 ssh-copy-id 已完成公钥写入但无法自动验证连接——常见于目标主机禁用密码登录(PermitRootLogin no 或 PasswordAuthentication no),导致它尝试用密码重连失败并挂起。
此时不能等、不能 Ctrl+C 后重试,因为公钥可能已写入但权限错误,或目录未创建。应立即手动检查远程 ~/.ssh/authorized_keys 是否存在且内容正确,并确认权限:
-
~/.ssh目录权限必须是700(chmod 700 ~/.ssh) -
authorized_keys文件权限必须是600(chmod 600 ~/.ssh/authorized_keys) - 若远程用户家目录非标准路径(如容器中为
/app),~不展开,需显式写成/app/.ssh/authorized_keys
当 ssh-copy-id 失败时,用管道命令手动追加公钥
这是最可靠 fallback 方式,尤其适用于批量部署、CI 环境或目标主机完全关闭密码认证的场景。核心是把本地公钥内容通过 stdin 流式传入远程 shell 执行。
正确命令格式(注意引号和空格):
cat ~/.ssh/id_rsa.pub | ssh user@host "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
关键点:
- 所有
~由远程 shell 展开,不是本地路径 -
chmod 600必须放在最后一步,OpenSSH 会拒绝读取权限过宽的authorized_keys - 若目标端 sshd 配置了
StrictModes yes(默认),忽略这步会导致免密失败 - 使用
id_ed25519.pub替代id_rsa.pub时,只需改路径,其余逻辑不变
写一个真正可用的批量分发脚本
纯 Bash 脚本比 Python 更轻量,也避免在目标环境缺失解释器。但“能跑”不等于“可用”,重点在容错和收敛。
脚本必须包含:
-
set -e:任一命令失败即退出,防止部分机器失败后继续执行 -
timeout 10s或-o ConnectTimeout=5:避免单台主机卡死阻塞整批 -
BatchMode=yes:禁止交互式提示(如 yes/no 确认),否则脚本挂住 - 对
hosts.txt每行做空行与注释过滤:[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] - 提取 host/user/port 时用
awk安全分割,避免空格导致字段错位
示例片段(不依赖外部工具,可直接保存为 ssh-batch-deploy.sh):
#!/bin/bash
set -e
HOSTS_FILE=${1:-./hosts.txt}
KEY_FILE=${2:-"$HOME/.ssh/id_rsa.pub"}
<p>[[ ! -f "$KEY_FILE" ]] && { echo "Error: public key not found at $KEY_FILE"; exit 1; }</p><p>while IFS= read -r line; do
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
host=$(echo "$line" | awk '{print $1}')
user=$(echo "$line" | awk '{print $2}')
port=${$(echo "$line" | awk '{print $3}'):-22}</p><p>echo "Deploying to $user@$host:$port"
cat "$KEY_FILE" | timeout 10s ssh -o ConnectTimeout=5 -o BatchMode=yes -p "$port" "$user@$host" \
"mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
done < "$HOSTS_FILE"免密登录仍失败?优先查这三处
不是密钥没传过去,而是 SSH daemon 拒绝加载它。最常被忽略的是服务端配置和文件上下文。
-
/etc/ssh/sshd_config中PubkeyAuthentication yes必须启用(默认通常开启,但某些加固镜像会关掉) -
AuthorizedKeysFile路径是否被修改过?默认是~/.ssh/authorized_keys,若改成.ssh/keys,你的公钥就进错地方了 - SELinux 或 AppArmor 启用时,
~/.ssh/authorized_keys可能因上下文标签错误被拒绝读取,用ls -Z ~/.ssh/authorized_keys查看,必要时运行restorecon -v ~/.ssh/authorized_keys
这些细节不报错、不提示,只默默拒绝认证——调试时得主动查,而不是反复重传公钥。


















