Jenkins流水线中SSH禁用StrictHostKeyChecking的安全替代方案
我正在学习CI/CD,为教学目的搭建了一个演示Jenkins流水线。在流水线中,我在SSH命令中使用-o StrictHostKeyChecking=no参数来禁用严格主机密钥检查,允许自动将服务器公钥添加至known_hosts文件。但我深知这种方式存在安全风险,会绕过服务器身份验证,易遭中间人攻击。
我希望找到无需使用该参数即可建立安全SSH连接的替代方法,确保服务器身份得到正确验证的同时,实现顺畅的自动化连接流程。
环境配置
- 两台EC2实例:一台运行Jenkins,另一台作为部署服务器;
- Jenkins流水线构建Docker镜像并部署至部署服务器;
- 因用于演示,EC2实例无静态IP或弹性IP;
- 使用Jenkins的
sshagent插件管理连接部署服务器的SSH密钥。
相关Jenkinsfile代码片段
stage("Deploy") { steps { script { echo 'Deploying the Docker image to the deployment server...' def shellCmd = "bash ./server-commands.sh ${IMAGE_NAME}" def deploymentServer = "ec2-user@<deployment-server-public-ip>" sshagent(['my-ssh-key']) { sh "scp -o StrictHostKeyChecking=no server-commands.sh ${deploymentServer}:/home/ec2-user" sh "scp -o StrictHostKeyChecking=no docker-compose.yaml ${deploymentServer}:/home/ec2-user" sh "ssh -o StrictHostKeyChecking=no ${deploymentServer} ${shellCmd}" } } } }
考虑到EC2实例的动态特性,能否指导我在Jenkins流水线中安全管理SSH主机密钥检查的最佳实践?有没有推荐的技术或工具可在不降低安全性的前提下验证服务器身份?
我明白使用-o StrictHostKeyChecking=no并不理想,尤其是在生产环境中。由于这是学习环境,我希望探索可应用于实际场景的安全替代方案。
针对EC2动态IP的场景,以下是几种安全管理SSH主机密钥检查的方案,既验证服务器身份,又适配自动化流程:
1. 预获取EC2实例主机密钥并注入Jenkins节点
EC2实例的主机密钥在创建时生成,即使IP变更,密钥也保持不变(除非实例重建)。你可以提前获取部署服务器的主机密钥,添加到Jenkins运行用户的known_hosts文件中,或者在流水线中动态注入。
操作步骤:
- 手动连接一次部署服务器,从本地
~/.ssh/known_hosts中提取对应主机的密钥行,格式类似:<deployment-server-ip> ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= - 在Jenkins流水线中,通过
sh命令将密钥追加到Jenkins节点的known_hosts:stage("Prepare SSH Host Key") { steps { script { // 替换为你的部署服务器主机密钥 def hostKey = "<deployment-server-ip> ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=" sh "echo '${hostKey}' >> ~/.ssh/known_hosts" } } } - 后续SSH/SCP命令可直接移除
StrictHostKeyChecking=no参数:sshagent(['my-ssh-key']) { sh "scp server-commands.sh ${deploymentServer}:/home/ec2-user" sh "scp docker-compose.yaml ${deploymentServer}:/home/ec2-user" sh "ssh ${deploymentServer} ${shellCmd}" }
如果部署服务器频繁重建,可通过AWS CLI动态获取主机密钥:
stage("Fetch and Add Host Key") { steps { script { def instanceId = "<your-deployment-instance-id>" // 获取实例公网IP def publicIp = sh(script: "aws ec2 describe-instances --instance-id ${instanceId} --query 'Reservations[0].Instances[0].PublicIpAddress' --output text", returnStdout: true).trim() // 获取实例主机密钥 def hostKey = sh(script: "ssh-keyscan ${publicIp}", returnStdout: true).trim() deploymentServer = "ec2-user@${publicIp}" // 写入known_hosts sh "echo '${hostKey}' >> ~/.ssh/known_hosts" } } }
注意:需确保Jenkins服务器已配置AWS CLI及对应权限。
2. 使用StrictHostKeyChecking=accept-new(折中方案)
如果你能接受仅首次连接时自动添加未知主机密钥,后续连接仍验证密钥一致性,可使用accept-new参数替代no。该参数仅在密钥未记录时自动添加,若后续密钥变化(如中间人攻击),连接会直接失败,安全性远高于完全禁用检查。
修改后的命令示例:
sshagent(['my-ssh-key']) { sh "scp -o StrictHostKeyChecking=accept-new server-commands.sh ${deploymentServer}:/home/ec2-user" sh "scp -o StrictHostKeyChecking=accept-new docker-compose.yaml ${deploymentServer}:/home/ec2-user" sh "ssh -o StrictHostKeyChecking=accept-new ${deploymentServer} ${shellCmd}" }
适合演示环境,生产环境仍建议采用预配置密钥的方案。
3. 使用Jenkins SSH插件集中管理主机密钥
通过Jenkins的SSH Server插件,可在全局配置中统一管理部署服务器的主机密钥,流水线直接使用插件提供的步骤完成文件传输与命令执行,无需手动处理known_hosts。
操作步骤:
- 安装Jenkins的
SSH Server插件; - 进入Jenkins全局配置 → 找到「SSH Servers」,添加部署服务器信息:
- 服务器地址:部署服务器公网IP;
- 端口:22;
- 凭据:选择已配置的SSH密钥(
my-ssh-key); - 点击「Advanced」,在「Host Key Verification Strategy」中选择「Manually provided key」,输入部署服务器的主机密钥;
- 在流水线中使用插件提供的
sshPut和sshCommand步骤:stage("Deploy") { steps { script { echo 'Deploying the Docker image to the deployment server...' def shellCmd = "bash ./server-commands.sh ${IMAGE_NAME}" // 上传文件 sshPut( remote: [host: '<deployment-server-public-ip>', credentialsId: 'my-ssh-key', port: 22], from: 'server-commands.sh', into: '/home/ec2-user/' ) sshPut( remote: [host: '<deployment-server-public-ip>', credentialsId: 'my-ssh-key', port: 22], from: 'docker-compose.yaml', into: '/home/ec2-user/' ) // 执行部署命令 sshCommand( remote: [host: '<deployment-server-public-ip>', credentialsId: 'my-ssh-key', port: 22], command: shellCmd ) } } }
这种方式将主机密钥管理集中化,更规范,适合团队协作场景。
内容的提问来源于stack exchange,提问作者Karan Thakkar

