首页
Jenkinsfile 中配置使用 ssh agent 连接远程主机

在使用 Jenkins 做持续集成的时候,我们经常会通过ssh连接到远程主机来进行一些操作。因为所有的操作都是脚本自动运行的,所以我们通常会使用免密登录。那么在Jenkinsfile中我们如何来实现呢?

Jenkinsfile 配置免密登录

看到之前项目中的Jenkinsfile是这样写的,在环境变量里定义了ssh private key,如下:

environment {
    DEPLOY_SSH =
'''
-----BEGIN RSA PRIVATE KEY-----
......
-----END RSA PRIVATE KEY-----
'''
}

然后在脚本中这样写:

set -e
mkdir -p ~/.ssh
echo "$DEPLOY_SSH" >> ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
echo "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config

ssh root@192.168.0.110 "mkdir -p /data/website"
rsync -rvR ./ root@192.168.0.110:/data/website

也就是说把ssh private key写到 Jenkins 脚本运行环境里的id_rsa文件中,以达到ssh远程免密连接的效果。但是这样带来一个很严重的问题,我们连接到远程服务器的ssh private key 暴露在源代码中了,任何人都可以获取到该ssh private key连接到服务器去做一些操作。

所以这种方式是不可行的。因为之前 Jenkins 在项目中主要起的作用是发布代码到测试环境,所以大家都不并不在意这个问题。但是生产环境,这种操作是绝对禁止的。

那么,我们在Jenkinsfile中怎么去连接到远程服务器呢?其实 Jenkins 提供了各种 ssh 插件可以帮助我们完成的,配合它的 Credentials 管理,我们可以把这种敏感信息隐藏起来,开发人员是接触不到的。比如: SSH Agent Plugin

SSH Agent Plugin 的使用

通过 SSH Agent Plugin 我们可以提供一个SSH credentials去进行构建,步骤如下:

1. 安装 SSH Agent Plugin

image_1emqf84u019ugkovnrsj4kues20.png-70.5kB

2. 添加一个凭证

在 Jenkins 主页面有个凭据的菜单,点进去可以新增一个凭据

image_1emontlm41jv5jno1tu115ok1r9e9.png-44.2kB

image_1emonud51123bnup1am517rh1nfmp.png-29.1kB

然后我们选择SSH Username with private key,依次填入表单信息。

  • ID:该凭证的唯一标识,最好不填让系统自动生成
  • Description:描述很重要,比如该凭证是用于登陆到哪个服务器
  • Username:凭证的使用者,默认是 jenkins
  • Private Key:连接到目标服务器的 private ssh key

image_1emoo5i5d1tjn1m3hair15ojrhv16.png-83.1kB

创建完毕后,在凭据界面我们会看到新生成的凭据

image_1emookrf812453cnhjhbibucq1j.png-16.4kB

3. 在 Jenkinsfile 中使用

接下来,我们就要在Jenkinsfile文件中利用SSH Agent Plugin来使用该凭证了。这个插件比较奇怪的地方是在插件的自述文件以及 github 的 README 中都没写到怎么去使用。最后倒是在 pipeline 的教程中找到了有关于它的使用。

node {
  sshagent (credentials: ['deploy-dev']) {
    sh 'ssh -o StrictHostKeyChecking=no -l cloudbees 192.168.1.106 uname -a'
  }
}

这个是脚本式流水线的写法,在声明式流水线中也是一样的,只不过是写在steps里面。deploy-dev就是前面我们生成的Credential Id

steps {
  sshagent (credentials: ['deploy-dev']) {
    sh 'ssh -o StrictHostKeyChecking=no -l cloudbees 192.168.1.106 uname -a'
  }
}

StrictHostKeyChecking

在上面的sh语句中,我们发现一个很重要的参数StrictHostKeyChecking。之前没有使用SSH Agent Plugin的一个很重要原因就是不知道需要加上这个参数,导致直接连接的时候会被拒绝。那么这个参数的作用是什么呢,下面我们来讲解一下。

SSH 公钥检查

在使用 SSH 连接到远程主机的时候,会检查主机的公钥。如果是第一次连接该主机,会显示该主机的公钥摘要,提示用户是否信任该主机。我们需要输入yes才可以连接到远程主机,并且会把该主机的公钥追加到~/.ssh/known_hosts文件中。当再次连接到该主机时,就不会再提示该问题了。

The authenticity of host '192.168.210.128 (192.168.210.128)' can't be established.
ECDSA key fingerprint is SHA256:NWbwTrGB+hWDGVd8kDaZXyvZybruyGDP848ejoHTKUU.
Are you sure you want to continue connecting (yes/no)? 

这样会导致一个问题,如果我们用 Jenkins 跑自动化任务,是没有输入yes这个过程的,任务会因此中断。所以我们需要做到连接新主机时,不进行公钥确认。

连接新主机,不进行公钥确认

我们上面提到的StrictHostKeyChecking配置就可以实现当第一次连接到主机时,自动接受新的公钥。它有两种使用方式:

  • 一种就是修改ssh的配置文件:/etc/ssh/ssh_config~/.ssh/config,在里面加入下面的语句:
Host *
 StrictHostKeyChecking no

我们之前脚本的这句话就是为了达到这个目的

echo "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
  • 另一种就是在ssh的命令中使用-o参数
ssh -o StrictHostKeyChecking=no root@192.168.210.128 "ls -l"

rsync 中如何设置 StrictHostKeyChecking

在上面的脚本中,我们还用到了 rsync 来做一些文件的同步,它也需在命令中登陆到远程服务器,同样会有公钥确认的问题。

虽然它的参数列表里面并没有指定StrictHostKeyChecking=no的参数,但是它提供了一个-e参数,可以指定所要使用的远程 shell 程序,默认为 ssh。

所以我们的命令可以这样来写:

rsync -rvRe "ssh -o StrictHostKeyChecking=no" ./ root@192.168.210.128:/data/web/

参考:

1.Jenkins设置SSH免登录访问远程服务器
2.ssh StrictHostKeyChecking
3.rsync(一):基本命令和用法