大事記 - Jenkins 腳本部署報錯 “Host key verification failed”

// 這本是一個不應該出現的問題...

 

問題描述:

有一臺用來部署項目的 Jenkins 服務器,然後新開了一臺應用服務器,需要通過 Jenkins 將打包後的代碼部署到應用服務器

但由於權限控制,我不知道兩邊服務器的密碼,應用服務器(記爲 dev.server)也只能通過跳板機訪問

而 Jenkins 服務器(記爲 jenkins.server)我沒有任何辦法訪問,只能訪問搭建好的 Jenkins 頁面

在這樣的背景下, 如果在 Jenkins 腳本中通過 ssh 直連應用服務器

就會出現 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/known_hosts 中

 

我突然想到,Jenkins 服務器也是第一次訪問應用服務器,會不會也有這個確認過程?而在腳本中無法確認,所以就會報錯

此時我已經連上了 Jenkins 服務器,並且在應用服務器的 authorized_keys 中添加了 Jenkins 服務器的公鑰

接着我在 Jenkins 服務器上,再次嘗試通過 ssh 訪問應用服務器,果然出現了上面的確認提示!

我雙手顫抖着輸入 yes,隨後回到 Jenkins 部署頁面,回放腳本

這次一路暢通,沒有出現任何報錯。看到腳本執行成功的綠色標記,我長舒了一口氣

 


 

再來回溯整個問題,其實就是 known_hosts 導致的。如果解決了 known_hosts 的問題,使用 withCredentials 也是可以的

最後補充一篇《SSH之known_hosts文件》作爲參考~

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章