背景
Ansible
是一个开源的自动化平台,它允许系统管理员自动化云服务、配置管理、应用部署、任务执行等任务。Ansible使用YAML语言编写的剧本(Playbooks),这些剧本描述了如何配置系统或应用程序。
SSH
即安全外壳协议(Secure Shell Protocol),是一种加密的网络协议,用于在不安全的网络上安全地访问远程计算机。SSH提供了一个安全的通道,允许用户执行远程命令、传输文件以及进行端口转发等操作。
案例
问题描述
在日常持续集成与持续部署的开发中,会使用Ansible工具来实现自动化的主机部署。
1、项目结构
我们会有这样的Ansible配置结构:
|--hosts
|--site.yml
|--roles
|--install
|--tasks
|--main.yml
2、任务配置
具体的,会配置这样的一个Ansible任务,实现登陆到目标主机执行命令:
- name: Check package_storage_path
file:
path: "{{ package_storage_path }}"
state: directory
mode: 0777
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
tags: create_package_storage_path
另外,主要的hosts文件配置为:
目标ip ansible_host=目标ip ansible_ssh_port=目标端口 ansible_user=目标主机用户名 ansible_ssh_pass=目标主机密码 ansible_ssh_common_args='-o StrictHostKeyChecking=no'
3、任务运行
执行该启动命令:ansible-playbook -i hosts site.yml
4、常见问题
但是,经常会遇到问题,导致执行命令失败:
5、问题排查
我们到目标主机上通过 journalctl -u sshd 命令查看日志,会发现网络是通的,有收到请求;
但是ssh连接,发现客户端的公钥不正确之后,就没有下文了:
Jun 21 10:34:03 cc-rqh-gztest63-x86-controller-1 sshd[1134801]: Connection from 请求ip port 请求端口 on 目标ip port 目标端口
Jun 21 10:34:03 cc-rqh-gztest63-x86-controller-1 sshd[1134801]: Failed publickey for root from请求ip port 请求端口 ssh2: RSA SHA256:6GGbGKD/9nYZSb0yS7pSRKxRBUiUQnwOBpI55PIYuir
Jun 21 10:34:03 cc-rqh-gztest63-x86-controller-1 sshd[1134801]: Connection closed by authenticating user 用户账号 请求ip port 请求端口 [preauth]
解决方案
1、调整执行命令
改为:ansible-playbook -i hosts site.yml --timeout 60 --ssh-extra-args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'
2、任务成功
3、日志查看
-
在目标主机上通过 journalctl -u sshd 命令查看日志:
Jun 21 10:45:53 cc-rqh-gztest63-x86-controller-1 sshd[1227554]: Connection from 请求ip port 请求端口 on 目标ip port 目标端口
Jun 21 10:45:53 cc-rqh-gztest63-x86-controller-1 sshd[1227554]: Failed publickey for root from 请求ip port 请求端口 ssh2: RSA SHA256:6GGbGKD/9nYZSb0yS7pSRKxRBUiUQnwOBpI55PIYuir
Jun 21 10:45:53 cc-rqh-gztest63-x86-controller-1 sshd[1227554]: Accepted password for 目标主机用户名 from 请求ip port 请求端口 ssh2
Jun 21 10:45:53 cc-rqh-gztest63-x86-controller-1 sshd[1227554]: pam_unix(sshd:session): session opened for user 目标主机用户名(uid=0) by (uid=0)
Jun 21 10:45:53 cc-rqh-gztest63-x86-controller-1 sshd[1227554]: Starting session: command on pts/9 for 目标主机用户名 from 请求ip port 请求端口 id 0
Jun 21 10:45:54 cc-rqh-gztest63-x86-controller-1 sshd[1227554]: Close session: user root from 请求ip port 请求端口 id 0
Jun 21 10:45:54 cc-rqh-gztest63-x86-controller-1 sshd[1227554]: Connection closed by 请求ip port 请求端口
Jun 21 10:45:54 cc-rqh-gztest63-x86-controller-1 sshd[1227554]: pam_unix(sshd:session): session closed for user root
Jun 21 10:45:54 cc-rqh-gztest63-x86-controller-1 sshd[1227554]: Transferred: sent 2608, received 2548 bytes
Jun 21 10:45:54 cc-rqh-gztest63-x86-controller-1 sshd[1227554]: Closing connection to 请求ip port 请求端口
- 发现在客户端的公钥校验失败后,客户端主动使用账号、通过密码连接上了,可以成功在主机执行命令。
原理解析
问题原因
-
网络抖动
-
目标主机的密钥变化
解决方式
给 ansible-playbook -i hosts site.yml 命令添加参数解决问题:
--timeout 60 --ssh-extra-args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'
参数的意义
-
--timeout 60:该选项设置了连接到远程主机的超时时间,单位是秒;加大超时时间,可避免网络抖动。
-
--ssh-extra-args:该选项可传递额外的SSH参数给Ansible。
-
-o StrictHostKeyChecking=no:该参数告诉SSH不要进行严格的主机密钥检查。通常,SSH客户端会检查服务器的公钥是否在known_hosts文件中,以防止中间人攻击。使用这个选项会禁用这个检查,使得SSH连接不会因未知的主机密钥而失败。
-
-o UserKnownHostsFile=/dev/null:该参数指定SSH不使用用户的known_hosts文件,而是写入到/dev/null,即黑洞设备。这意味着SSH不会记录任何主机密钥信息,也不会检查现有的记录。在自动化脚本中,可避免因known_hosts文件中的数据变化而导致的问题。
中间人攻击
是一种网络攻击技术,攻击者在通信双方之间插入自己,截获并可能篡改双方的通信数据。
为防止中间人攻击,SSH客户端在首次连接到服务器时,会要求用户确认服务器的公钥指纹,以防止攻击者使用伪造的公钥,如:
The authenticity of host '目标ip (目标ip)' can't be established.
ECDSA key fingerprint is SHA256:AOEUgBJnuTpZVLTMLNcBht0XDYyamkgDXZpeeyhfuil.
Are you sure you want to continue connecting (yes/no)?
总结
这种解决方式通过避免主机密钥检查,解决目标主机因为主机IP变化、重装系统等原因导致无法控制的问题,通常用于自动化环境或持续集成/持续部署流程中,但在生产环境中,考虑到中间人攻击,应该谨慎使用该方式。