Linux命令-ssh-keyscan(从主机收集公钥)
ssh-keyscan 是一个批量收集 SSH 主机公钥的工具。与交互式的 ssh 首次连接确认不同,ssh-keyscan 非交互地从指定主机获取公钥,非常适合自动化脚本、主机清单管理和大规模部署前的 known_hosts 预填充。
快速参考
bash
ssh-keyscan [选项] [主机...]
ssh-keyscan从目标的 SSH 服务端口连接并获取公钥,输出可重定向到~/.ssh/known_hosts文件,避免首次连接时的交互式确认提示。
基本用法
bash
# 获取单台主机的公钥
ssh-keyscan server.example.com
# 获取并直接添加到 known_hosts
ssh-keyscan -H server.example.com >> ~/.ssh/known_hosts
# 指定非标准端口
ssh-keyscan -p 2222 server.example.com
# 获取多个主机的公钥
ssh-keyscan server1 server2 server3
# 从文件批量获取
ssh-keyscan -f hosts.txt
选项详解
| 选项 | 说明 |
|---|---|
-H |
哈希主机名(推荐,增加安全性) |
-p PORT |
指定 SSH 端口 |
-T TIMEOUT |
连接超时时间(秒) |
-t TYPE |
指定密钥类型(rsa/dsa/ecdsa/ed25519) |
-f FILE |
从文件读取主机列表 |
-4/-6 |
强制使用 IPv4/IPv6 |
-c |
请求证书而非纯密钥 |
-v |
详细输出 |
-D |
打印调试信息 |
常见使用场景
bash
# === 场景 1: 避免首次连接提示 ===
# 传统方式(交互式)
ssh new-server.example.com
# The authenticity of host 'new-server.example.com' can't be established.
# Are you sure you want to continue connecting (yes/no)?
# 使用 ssh-keyscan 预填充
ssh-keyscan -H new-server.example.com >> ~/.ssh/known_hosts
ssh new-server.example.com # 无提示!
# === 场景 2: Ansible/自动化部署 ===
# 部署前批量添加所有主机公钥
ansible all --list-hosts | tail -n +2 | while read host; do
ssh-keyscan -H "$host" >> ~/.ssh/known_hosts 2>/dev/null
done
# === 场景 3: 不同密钥类型收集 ===
# 仅收集 Ed25519 密钥
ssh-keyscan -t ed25519 host.example.com
# 收集多种类型
ssh-keyscan -t ecdsa,ed25519 host.example.com
# 收集全部可用类型
ssh-keyscan -t rsa,dsa,ecdsa,ed25519 host.example.com 2>/dev/null
批量操作
bash
# 从 IP 范围批量收集
for ip in 192.168.1.{1..254}; do
ssh-keyscan -T 2 "$ip" >> ~/.ssh/known_hosts 2>/dev/null &
done
wait
# 从主机清单文件批量操作
cat > hostlist.txt << 'EOF'
192.168.1.10
192.168.1.20
db.private.local
web.prod.internal
EOF
# 哈希存储(推荐)
ssh-keyscan -H -f hostlist.txt >> ~/.ssh/known_hosts
# 非哈希存储(调试时使用)
ssh-keyscan -f hostlist.txt > known_hosts_plain.txt
# 去重 known_hosts
sort -u ~/.ssh/known_hosts -o ~/.ssh/known_hosts
# 并行批量收集(超时 3 秒)
cat hostlist.txt | xargs -P 10 -I {} ssh-keyscan -T 3 -H {} >> ~/.ssh/known_hosts 2>keyscan_errors.log
# 验证收集结果
wc -l ~/.ssh/known_hosts
安全管理
bash
# 清除特定主机的旧记录
ssh-keygen -R compromised-host.example.com
# 清除旧记录后重新扫描
ssh-keyscan -H new-server.example.com >> ~/.ssh/known_hosts
# 验证 known_hosts 中的记录
ssh-keygen -H -F server.example.com
# 审计 known_hosts 文件
echo "=== known_hosts 审计 ==="
echo "总记录数: $(wc -l < ~/.ssh/known_hosts)"
echo "MD5 哈希: $(md5sum ~/.ssh/known_hosts | cut -d' ' -f1)"
# 提取所有主机名(需要先取消哈希,安全注意!)
ssh-keygen -F "*" -f ~/.ssh/known_hosts 2>/dev/null
# 对比新旧主机密钥(检测 MITM 攻击)
ssh-keyscan server.example.com | while read line; do
if grep -qF "$line" ~/.ssh/known_hosts; then
echo "✅ 密钥匹配: server.example.com"
else
echo "❌ 密钥不匹配!可能存在中间人攻击"
fi
done
高级用法
bash
# 为特定子网生成 known_hosts 文件
NETWORK="10.0.0"
mkdir -p /tmp/ssh_scan
for octet in {1..50}; do
ssh-keyscan -T 1 -H "${NETWORK}.${octet}" >> /tmp/ssh_scan/network_hosts 2>/dev/null &
done
wait
echo "收集完成,条目数: $(wc -l < /tmp/ssh_scan/network_hosts)"
# 按密钥类型分类输出
ssh-keyscan -t rsa -H host.example.com > rsa_keys.txt
ssh-keyscan -t ed25519 -H host.example.com > ed25519_keys.txt
# 导出为 SSHFP DNS 记录格式
ssh-keyscan host.example.com | ssh-keygen -r host.example.com
# 扫描域名对应的多个 IP(处理负载均衡)
HOST="cdn.example.com"
for ip in $(dig +short "$HOST"); do
echo "# IP: $ip"
ssh-keyscan -T 3 "$ip" 2>/dev/null
done
# 脚本化:检查密钥是否已存在于 known_hosts
check_known_host() {
local host="$1"
if ssh-keygen -F "$host" -f ~/.ssh/known_hosts > /dev/null 2>&1; then
echo "✅ $host 已存在于 known_hosts"
return 0
else
echo "⚠️ $host 未在 known_hosts 中,正在添加..."
ssh-keyscan -H "$host" >> ~/.ssh/known_hosts 2>/dev/null
return $?
fi
}
check_known_host "new-server.local"
安全注意事项
安全警告:
ssh-keyscan不验证主机身份------它只是收集公钥。收到的主机公钥在首次使用时都应通过带外方式(如电话、当面确认)进行验证- 始终使用
-H选项哈希主机名,防止主机名信息泄露- 在生产环境中,应使用 SSH CA 证书签名代替手动 known_hosts 管理
- 定期使用
ssh-keygen -R清理不再需要的 known_hosts 记录
bash
# SSH CA 模式(企业推荐方案,避免手动管理 known_hosts)
# 客户端配置 ~/.ssh/config
cat >> ~/.ssh/config << 'EOF'
Host *.company.com
CertificateFile ~/.ssh/user_key-cert.pub
EOF
# 这样就不需要逐台收集主机密钥了
ssh-keyscan 是自动化 SSH 部署中的先遣工具。虽然它不验证主机身份,但在可信任的私有网络环境中,它极大地简化了首次 SSH 连接的工作流程。