分类
devops

ssh-keygen+ssh-keyscan+ssh-copy-id三条命令实现ssh non interactive登录

ssh-keygen+ssh-keyscan+ssh-copy-id三条命令实现ssh免密 non interactive登录

如下代码实现从A机器(192.168.1.100)到B机器(192.168.1.111)的ssh免密non interactive登录。

## 在A机上操作如下三句
ssh-keygen -t rsa -b 4096 -N '' -m PEM <<<$'\ny\n'
ssh-keyscan -H 192.168.1.111 >> ~/.ssh/known_hosts
ssh-copy-id -i ~/.ssh/id_rsa.pub jerry@192.168.1.111

解释

ssh-keygen -t rsa -b 4096 -N '' -m PEM <<<$'\ny\n'

这句是生成密钥对,不产生交互,如果以前存在则删除重建。

参考https://stackoverflow.com/questions/43235179/how-to-execute-ssh-keygen-without-prompt

ssh-keyscan -H 192.168.1.111 >> ~/.ssh/known_hosts

这句是把ssh fingerprint加入到A机器~/.ssh/known_hosts

参考https://www.techrepublic.com/article/how-to-easily-add-an-ssh-fingerprint-to-your-knownhosts-file-in-linux/

ssh-copy-id -i ~/.ssh/id_rsa.pub jerry@192.168.1.111

这句是附加公钥(即A机器的~/.ssh/id_rsa.pub)到B机器~/.ssh/authorized_keys,第一次提示输入密码。

20220826更新

想了以下,认为这个也不叫免密登陆,叫ssh 通过私钥实现无交互登陆

20220827更新

更简洁的使用两条命令就可以了,无需增加 ssh-keyscan 这一条,但是需要通过-o增加ssh参数。参考

## 测试用例
ssh-keygen -f "$HOME/.ssh/known_hosts" -R "192.168.1.111" > /dev/null;
ssh-copy-id -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_rsa.pub jim@192.168.1.111

第一次输入密码可以通过sshpass or expect方式实现自动完成。参考

20220929更新

脚本:批量根据密码拷贝公钥到目标机器的bash脚本

#!/bin/bash
# cat > hosts <<'EOF'
# #hostname         user    password    port    identity_file
# 192.168.1.111 root    password    22  
# EOF


auto_ssh_copy_id() {
    #ssh-keygen -R "$1" -f "$HOME/.ssh/known_hosts" > /dev/null;
    expect -c "set timeout -1;
        spawn ssh-copy-id -p $3 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null  $1;
        expect {
            *(yes/no)* {send -- yes\r;exp_continue;}
            *assword:* {send -- $2\r;exp_continue;}
            eof        {exit 0;}
        }";
}

ssh_copy_id_to_all() {
    while IFS= read -r line || [ -n "$line" ]; do
        if [[ "$line" =~ ^\#.* ]]; then
            continue
        fi

        server=$(echo "${line}" | awk -F " " '{print $1}')
        user=$(echo "${line}" | awk -F " " '{print $2}')
        password=$(echo "${line}" | awk -F " " '{print $3}')
        port=$(echo "${line}" | awk -F " " '{print $4}')
        echo "${line}" && auto_ssh_copy_id "${user}@${server}" "${password}" "${port}"
    done < hosts
}

ssh_copy_id_to_all

authorized_keys and known_hosts

~/.ssh/authorized_keys vs ~/.ssh/known_hosts 两个文件一个在服务端,一个在客户端,不要混淆。

authorized_keys(在 SSH 服务端)

作用:允许哪些公钥可以免密登录到”我”这个账号

  • 路径:~/.ssh/authorized_keys(在被登录的机器上)
  • 内容:一行一条 SSH 公钥(ssh-rsa AAAA… user@host)
  • 工作原理:你把客户端的公钥放进服务端的 authorized_keys 里,客户端用对应私钥登录时,SSH 服务端就能验证”这把私钥的持有者就是被允许的用户”,于是免去密码

    典型场景:

  • 你从 A 机器免密 ssh user@b → 把 A 的公钥加到 b 用户的 ~/.ssh/authorized_keys
  • GitHub 的 “Settings → SSH and GPG keys” 就是把你的公钥加到 GitHub 那个用户的 authorized_keys

    权限要求(很多人踩坑):
    chmod 700 ~/.ssh
    chmod 600 ~/.ssh/authorized_keys # 必须严格600,否则 sshd 拒绝读取

known_hosts(在 SSH 客户端)

作用:记住”我连过的每台 SSH 服务器的指纹”,下次连的时候验证”这台机器还是当初那台吗”

  • 路径:~/.ssh/known_hosts(在发起连接的机器上)
  • 内容:每个 SSH 服务器的主机公钥(host key),格式类似:
    github.com ssh-ed25519 AAAA…5d
    192.168.1.10 ecdsa-sha2-nistp256 AAAA…xQ
  • 工作原理:
  1. 第一次连 ssh user@192.168.1.10,客户端会问”是否信任这台机器的 host key?”(Are you sure you want to continue connecting?),你输入 yes
  2. 这台机器的 host key 被记进 known_hosts
  3. 下次再连,客户端拿 known_hosts 里的指纹和服务器返回的指纹比对:

– ✅ 一致 → 放心连(说明还是当初那台机器)
– ❌ 不一致 → 警告 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!(可能中间人攻击,或机器重装了系统)

典型场景:
– 服务器重装系统 → known_hosts 里的旧指纹失效,需要手动删掉那一行再重连
– 自动化脚本里会用 ssh-keyscan host >> known_hosts 提前”信任”目标机,避免交互## 对比

┌──────────────┬─────────────────────┬──────────────────────────────┐
│ 维度 │ authorized_keys │ known_hosts │
├──────────────┼─────────────────────┼──────────────────────────────┤
│ 在哪 │ 被登录方(server) │ 发起连接方(client) │
├──────────────┼─────────────────────┼──────────────────────────────┤
│ 角色 │ 服务端决定”谁能来” │ 客户端决定”这是不是真服务端” │
├──────────────┼─────────────────────┼──────────────────────────────┤
│ 内容 │ 客户端的公钥列表 │ 服务端的 host key 指纹 │
├──────────────┼─────────────────────┼──────────────────────────────┤
│ 写时机 │ 管理员手工添加 │ 首次连上新主机时自动添加 │
├──────────────┼─────────────────────┼──────────────────────────────┤
│ 不一致时报错 │ 公钥不符 → 拒绝登录 │ host key 不符 → 警告中间人 │
└──────────────┴─────────────────────┴──────────────────────────────┘

一句话口诀

▎ authorized_keys 是门卫的名单(谁能进),known_hosts 是熟客名单(这人我认识)。

两者配合起来就是 SSH 公钥认证 + 主机验证的完整安全基础。

参考资料