Talk is cheap. Show me your code

大事记 - Jenkins 脚本部署报错 “Host key verification failed”

// 这本是一个不应该出现的问题...

 

问题描述:

有一台用来部署项目的 Jenkins 服务器,然后新开了一台应用服务器,需要通过 Jenkins 将打包后的代码部署到应用服务器

但由于权限控制,我不知道两边服务器的密码,应用服务器(记为 dev.server)也只能通过跳板机访问

而 Jenkins 服务器(记为 jenkins.server)我没有任何办法访问,只能访问搭建好的 Jenkins 页面

在这样的背景下, 如果在 Jenkins 脚本中通过 ssh 直连应用服务器

ssh deploy@dev.server

就会出现 Host key verification failed 错误

 

 

分析原因:

其实原因很简单,通过 ssh 访问服务器是需要携带认证信息的,比如用户和密码,或者通过公钥免密登录

但这两个方案都不适用,因为我既不知道应用服务器的密码,也无法拿到 Jenkins 服务器的公钥

 

 

解决方案:

于是一次迂回大作战打响了

 

第一回合:Jenkinsfile 携带私钥

由于问题的本质是认证失败,我便打算通过 Jenkins credentials 来处理

首先在 Jenkins 中创建一个类型为 SSH Username with private key 的全局凭据

并将应用服务器的私钥粘贴进去

cat /root/.ssh/id_rsa

// 完整流程可以参考《Jenkins----凭据管理之配置ssh私钥》

然后需要调整 Jenkinsfile 脚本,用 withCredentials 方法将需要凭据的部分给包起来

比如我原本的脚本是:

stages {
  stage('Init ssh server'){
    steps {
      sh "ssh ${env._HOST_NAME} 'mkdir -p ${env._DEPLOY_PATH}'"
    }
  }
}

加入凭据之后就是:

stages {
  stage('Init ssh server'){
    steps {
      withCredentials(bindings: [
        sshUserPrivateKey(credentialsId: 'jenkins-ssh-key-xxxx', keyFileVariable: 'SSH_KEY_XXX')
      ]) {
        sh "ssh ${env._HOST_NAME} 'mkdir -p ${env._DEPLOY_PATH}'"
      }
    }
  }
}

// 建议使用 Jenkins 的片段生成器

配置好之后,我再次回放脚本

在我期待的目光下,Console Output 又抛出那个熟悉错误 Host key verification failed

 

第二回合:尝试配置免密登录

withCredentials 的方案失败之后,我再次梳理了一下整个流程,还是决定配置免密登录

我先用自己的电脑来测试,获取到本地的公钥 

cat ~/.ssh/id_rsa.pub

然后通过跳板机进入应用服务器,打开 authorized_keys 文件(若无则创建)

vi ~/.ssh/authorized_keys

然后将本地公钥粘贴到应用服务器 authorized_keys 的末尾


如果知道应用服务器的密码,上述过程还可以简化为一行命令:

ssh-copy-id user@hostname

运行之后需要输入对应用户的密码,然后就会把本地的公钥追加到 authorized_keys


此外还需要调整 ssh 的配置文件以及访问权限,详细配置可以参考《服务器配置免密码密钥登录》

不过运维已经完成了应用服务器的基本配置,在修改了 authorized_keys 之后,我就能在本地链接应用服务器了

 

见本地连接成功,我急忙腆着脸去找 Jenkins 服务器的管理员,费了好多口舌,他终于把 Jenkins 服务器的公钥给我了

随后我将 Jenkins 服务器的公钥也配到应用服务器的 authorized_keys 中,再次回放脚本

然而,结果依然是 Host key verification failed

同样的配置,为什么本地能正常访问,Jenkins 服务器却无法访问呢?

 

决战:本地直连 Jenkins 服务器

我想了许久,也没有想通本地与 Jenkins 服务器的差异

最后把心一横,决定想办法连上 Jenkins 服务器看看

可运维始终不肯开放跳板机权限,在我束手无措的时候,突然想到 Jenkinsfile 本身就是在 Jenkins 服务器上跑脚本

那么... 我岂不是可以通过 Jenkins 脚本,将我本地的公钥添加到 Jenkins 服务器的 authorized_keys 中,实现本地到 Jenkins 服务器的免密登录?

 

于是我在 Jenkins 脚本中添加了这一行命令:

sh "echo 'ssh-rsa xxxxxx' >> ~/.ssh/authorized_keys"

其中的 'ssh-rsa xxxxxx' 就是我本地公钥 id_rsa.pub

脚本跑完之后,我在本地使用 ssh 访问 Jenkins 服务器:

ssh root@jenkins.server

没有让我输入密码,这说明免密登录配置成功了。但有以下提示:

这个提示并不陌生, 首次连接服务器时都会有这个确认信息

确认之后,这个远程服务器的信息就会记录到本地的  ~/.ssh/known_hosts 中

 

我突然想到,Jenkins 服务器也是第一次访问应用服务器,会不会也有这个确认过程?而在脚本中无法确认,所以就会报错

此时我已经连上了 Jenkins 服务器,并且在应用服务器的 authorized_keys 中添加了 Jenkins 服务器的公钥

接着我在 Jenkins 服务器上,再次尝试通过 ssh 访问应用服务器,果然出现了上面的确认提示!

我双手颤抖着输入 yes,随后回到 Jenkins 部署页面,回放脚本

这次一路畅通,没有出现任何报错。看到脚本执行成功的绿色标记,我长舒了一口气

 


 

再来回溯整个问题,其实就是 known_hosts 导致的。如果解决了 known_hosts 的问题,使用 withCredentials 也是可以的

最后补充一篇《SSH之known_hosts文件》作为参考~

 

posted @ 2022-08-02 09:24  Wise.Wrong  阅读(1838)  评论(0编辑  收藏  举报