ansible ssh 提示 known_hosts 问题
ansible ssh prompt known_hosts issue
我运行正在使用 Ansible 剧本,它在一台机器上运行良好。
在新机器上第一次尝试时,出现以下错误。
17:04:34 PLAY [appservers] *************************************************************
17:04:34
17:04:34 GATHERING FACTS ***************************************************************
17:04:34 fatal: [server02.cit.product-ref.dev] => {'msg': "FAILED: (22, 'Invalid argument')", 'failed': True}
17:04:34 fatal: [server01.cit.product-ref.dev] => {'msg': "FAILED: (22, 'Invalid argument')", 'failed': True}
17:04:34
17:04:34 TASK: [common | remove old ansible-tmp-*] *************************************
17:04:34 FATAL: no hosts matched or all hosts have already failed -- aborting
17:04:34
17:04:34
17:04:34 PLAY RECAP ********************************************************************
17:04:34 to retry, use: --limit @/var/lib/jenkins/site.retry
17:04:34
17:04:34 server01.cit.product-ref.dev : ok=0 changed=0 unreachable=1 failed=0
17:04:34 server02.cit.product-ref.dev : ok=0 changed=0 unreachable=1 failed=0
17:04:34
17:04:34 Build step 'Execute shell' marked build as failure
17:04:34 Finished: FAILURE
这个错误可以解决,如果我先去源机器(从我 运行ning ansible playbook 那里)并手动 ssh 到目标机器(作为给定的用户)并输入"yes" known_hosts 个文件条目。
现在,如果我第二次 运行 相同的 ansible 剧本,它可以正常工作。
因此,如何在给定用户(~/.ssh 文件夹,文件 known_hosts)首次输入 ssh known_hosts 时抑制 SSH 给出的提示?
我发现如果我在 ~/.ssh/config 文件中使用以下配置条目,我可以做到这一点。
~/.ssh/config
# For vapp virtual machines
Host *
StrictHostKeyChecking no
UserKnownHostsFile=/dev/null
User kobaloki
LogLevel ERROR
即如果我将上面的代码放在用户的 ~/.ssh/config 远程机器的文件中并第一次尝试 Ansible playbook,我不会被提示输入 "yes" 并且 playbook 会 运行 成功(无需用户手动创建从源计算机到 target/remote 计算机的 known_hosts 文件条目)。
我的问题:
1. 走~/.ssh/config方式需要注意哪些安全问题
2. 如何在命令行将设置(配置文件中的内容)作为 parameters/options 传递给 ansible,以便它在新机器上首次 运行(不提示/取决于 known_hosts目标机器在源机器上的文件条目?
ansible 文档有 a section on this。引用:
Ansible has host key checking enabled by default.
If a host is reinstalled and has a different key in ‘known_hosts’,
this will result in an error message until corrected. If a host is not
initially in ‘known_hosts’ this will result in prompting for
confirmation of the key, which results in an interactive experience if
using Ansible, from say, cron. You might not want this.
If you understand the implications and wish to disable this behavior,
you can do so by editing /etc/ansible/ansible.cfg or ~/.ansible.cfg:
[defaults]
host_key_checking = False
Alternatively this can be set by the ANSIBLE_HOST_KEY_CHECKING
environment variable:
$ export ANSIBLE_HOST_KEY_CHECKING=False
Also note that host key checking in paramiko mode is reasonably slow, therefore switching to ‘ssh’ is also recommended when using this feature.
从安全角度来看,完全禁用主机密钥检查不是一个好主意,因为它会使您容易受到中间人攻击。
如果您可以假设当前网络没有受到威胁(也就是说,当您第一次通过 ssh 连接到机器并收到一个密钥时,该密钥实际上是机器的而不是攻击者的),那么你可以使用 ssh-keyscan
and the shell module to add the new servers' keys to your known hosts file (edit: 做这个更好的方法):
- name: accept new ssh fingerprints
shell: ssh-keyscan -H {{ item.public_ip }} >> ~/.ssh/known_hosts
with_items: ec2.instances
(此处展示的内容与您在 ec2 provisioning 之后看到的一样)
为了更新本地 known_hosts
文件,我最终结合使用了 ssh-keyscan
(使用 dig
将主机名解析为 IP 地址)和 ansible 模块 known_hosts
如下:(文件名ssh-known_hosts.yml
)
- name: Store known hosts of 'all' the hosts in the inventory file
hosts: localhost
connection: local
vars:
ssh_known_hosts_command: "ssh-keyscan -T 10"
ssh_known_hosts_file: "{{ lookup('env','HOME') + '/.ssh/known_hosts' }}"
ssh_known_hosts: "{{ groups['all'] }}"
tasks:
- name: For each host, scan for its ssh public key
shell: "ssh-keyscan {{ item }},`dig +short {{ item }}`"
with_items: "{{ ssh_known_hosts }}"
register: ssh_known_host_results
ignore_errors: yes
- name: Add/update the public key in the '{{ ssh_known_hosts_file }}'
known_hosts:
name: "{{ item.item }}"
key: "{{ item.stdout }}"
path: "{{ ssh_known_hosts_file }}"
with_items: "{{ ssh_known_host_results.results }}"
要执行这样的 yml,请执行
ANSIBLE_HOST_KEY_CHECKING=false ansible-playbook path/to/the/yml/above/ssh-known_hosts.yml
因此,对于 目录 中的每个主机,所有支持的算法都将在 下的 known_hosts
文件中 added/updated hostname,ipaddress对记录;比如
atlanta1.my.com,10.0.5.2 ecdsa-sha2-nistp256 AAAAEjZHN ... NobYTIGgtbdv3K+w=
atlanta1.my.com,10.0.5.2 ssh-rsa AAAAB3NaC1y ... JTyWisGpFeRB+VTKQ7
atlanta1.my.com,10.0.5.2 ssh-ed25519 AAAAC3NaCZD ... UteryYr
denver8.my.com,10.2.13.3 ssh-rsa AAAAB3NFC2 ... 3tGDQDSfJD
...
(假设 库存 文件如下所示:
[master]
atlanta1.my.com
atlanta2.my.com
[slave]
denver1.my.com
denver8.my.com
)
与 Xiong 的回答相反,这将正确处理 known_hosts
文件的内容。
如果使用目标主机重新映像的虚拟化环境(因此 ssh 公钥被更改),此操作特别有用。
警告:host(以及 Ansible)将不再对任何 ssh 连接执行 SSH host 密钥验证以上设置。这可能存在风险,强烈不 在生产 环境
中推荐
您也可以从服务器 os 级别进行设置。您将需要配置 ssh 配置文件以避免 ssh 检查提示:
编辑文件路径:
/etc/ssh/ssh_config
现在取消注释行:
StrictHostKeyChecking no
保存更改即可
警告:host(以及 Ansible)将不再对任何 ssh 连接执行 SSH host 密钥验证以上设置。这可能存在风险,强烈不 在生产 环境
中推荐
关注@Stepan Vavra 的正确答案。较短的版本是:
- known_hosts:
name: "{{ item }}"
key: "{{ lookup('pipe', 'ssh-keyscan {{ item }},`dig +short {{ item }}`') }}"
with_items:
- google.com
- github.com
不会做这样的事情来启动 known_hosts 文件:
ANSIBLE_HOST_KEY_CHECKING=false ansible all -m ping
这应该连接到清单中的每台主机,更新每台主机的 known_hosts 文件,而不必为每个提示输入 "yes",然后在每个主机上运行 "ping" 模块主持人?
快速测试(删除我的 known_hosts 文件然后 运行 以上,在 Ubuntu 16.04 实例上完成)似乎用他们当前的 known_hosts 文件填充指纹。
@Stepan Vavra 的解决方案对我不起作用,因为我使用的是别名主机(连接到没有可用 DNS 的内部 IP,所以我想要更多描述性名称来指代中的每个主机清单并让 ansible_host 变量指向每个的实际 IP)。 运行 上面的内容简单多了,并且无需在 ansible 或 ssh 中禁用主机密钥检查就可以启动我的 known_hosts 文件。
我已经创建了这个 shell 脚本(顺便说一句,也适用于 Jenkins)
my_known_hosts="$HOME/.ssh/known_hosts"
## housekeeping ##
if [ -f $my_known_hosts".old" ]
then rm -f $my_known_hosts".old"
fi
## housekeeping ##
## backup ##
if [ -f $my_known_hosts ]
then mv $my_known_hosts "$my_known_hosts.old"
fi
## backup ##
## query aws for active hosts and add to known_hosts
aws ec2 describe-instances --query 'Reservations[*].Instances[*].NetworkInterfaces[*].Association.PublicDnsName' --output text | xargs -L1 ssh-keyscan -H >> $my_known_hosts
## query aws for active hosts and add to known_hosts
https://admin-o-mat.blogspot.com/2020/09/ansible-and-aws-adding-hosts-to.html
我运行正在使用 Ansible 剧本,它在一台机器上运行良好。
在新机器上第一次尝试时,出现以下错误。
17:04:34 PLAY [appservers] *************************************************************
17:04:34
17:04:34 GATHERING FACTS ***************************************************************
17:04:34 fatal: [server02.cit.product-ref.dev] => {'msg': "FAILED: (22, 'Invalid argument')", 'failed': True}
17:04:34 fatal: [server01.cit.product-ref.dev] => {'msg': "FAILED: (22, 'Invalid argument')", 'failed': True}
17:04:34
17:04:34 TASK: [common | remove old ansible-tmp-*] *************************************
17:04:34 FATAL: no hosts matched or all hosts have already failed -- aborting
17:04:34
17:04:34
17:04:34 PLAY RECAP ********************************************************************
17:04:34 to retry, use: --limit @/var/lib/jenkins/site.retry
17:04:34
17:04:34 server01.cit.product-ref.dev : ok=0 changed=0 unreachable=1 failed=0
17:04:34 server02.cit.product-ref.dev : ok=0 changed=0 unreachable=1 failed=0
17:04:34
17:04:34 Build step 'Execute shell' marked build as failure
17:04:34 Finished: FAILURE
这个错误可以解决,如果我先去源机器(从我 运行ning ansible playbook 那里)并手动 ssh 到目标机器(作为给定的用户)并输入"yes" known_hosts 个文件条目。
现在,如果我第二次 运行 相同的 ansible 剧本,它可以正常工作。
因此,如何在给定用户(~/.ssh 文件夹,文件 known_hosts)首次输入 ssh known_hosts 时抑制 SSH 给出的提示?
我发现如果我在 ~/.ssh/config 文件中使用以下配置条目,我可以做到这一点。
~/.ssh/config
# For vapp virtual machines
Host *
StrictHostKeyChecking no
UserKnownHostsFile=/dev/null
User kobaloki
LogLevel ERROR
即如果我将上面的代码放在用户的 ~/.ssh/config 远程机器的文件中并第一次尝试 Ansible playbook,我不会被提示输入 "yes" 并且 playbook 会 运行 成功(无需用户手动创建从源计算机到 target/remote 计算机的 known_hosts 文件条目)。
我的问题: 1. 走~/.ssh/config方式需要注意哪些安全问题 2. 如何在命令行将设置(配置文件中的内容)作为 parameters/options 传递给 ansible,以便它在新机器上首次 运行(不提示/取决于 known_hosts目标机器在源机器上的文件条目?
ansible 文档有 a section on this。引用:
Ansible has host key checking enabled by default.
If a host is reinstalled and has a different key in ‘known_hosts’, this will result in an error message until corrected. If a host is not initially in ‘known_hosts’ this will result in prompting for confirmation of the key, which results in an interactive experience if using Ansible, from say, cron. You might not want this.
If you understand the implications and wish to disable this behavior, you can do so by editing /etc/ansible/ansible.cfg or ~/.ansible.cfg:
[defaults]
host_key_checking = False
Alternatively this can be set by the
ANSIBLE_HOST_KEY_CHECKING
environment variable:
$ export ANSIBLE_HOST_KEY_CHECKING=False
Also note that host key checking in paramiko mode is reasonably slow, therefore switching to ‘ssh’ is also recommended when using this feature.
从安全角度来看,完全禁用主机密钥检查不是一个好主意,因为它会使您容易受到中间人攻击。
如果您可以假设当前网络没有受到威胁(也就是说,当您第一次通过 ssh 连接到机器并收到一个密钥时,该密钥实际上是机器的而不是攻击者的),那么你可以使用 ssh-keyscan
and the shell module to add the new servers' keys to your known hosts file (edit:
- name: accept new ssh fingerprints
shell: ssh-keyscan -H {{ item.public_ip }} >> ~/.ssh/known_hosts
with_items: ec2.instances
(此处展示的内容与您在 ec2 provisioning 之后看到的一样)
为了更新本地 known_hosts
文件,我最终结合使用了 ssh-keyscan
(使用 dig
将主机名解析为 IP 地址)和 ansible 模块 known_hosts
如下:(文件名ssh-known_hosts.yml
)
- name: Store known hosts of 'all' the hosts in the inventory file
hosts: localhost
connection: local
vars:
ssh_known_hosts_command: "ssh-keyscan -T 10"
ssh_known_hosts_file: "{{ lookup('env','HOME') + '/.ssh/known_hosts' }}"
ssh_known_hosts: "{{ groups['all'] }}"
tasks:
- name: For each host, scan for its ssh public key
shell: "ssh-keyscan {{ item }},`dig +short {{ item }}`"
with_items: "{{ ssh_known_hosts }}"
register: ssh_known_host_results
ignore_errors: yes
- name: Add/update the public key in the '{{ ssh_known_hosts_file }}'
known_hosts:
name: "{{ item.item }}"
key: "{{ item.stdout }}"
path: "{{ ssh_known_hosts_file }}"
with_items: "{{ ssh_known_host_results.results }}"
要执行这样的 yml,请执行
ANSIBLE_HOST_KEY_CHECKING=false ansible-playbook path/to/the/yml/above/ssh-known_hosts.yml
因此,对于 目录 中的每个主机,所有支持的算法都将在 下的 known_hosts
文件中 added/updated hostname,ipaddress对记录;比如
atlanta1.my.com,10.0.5.2 ecdsa-sha2-nistp256 AAAAEjZHN ... NobYTIGgtbdv3K+w=
atlanta1.my.com,10.0.5.2 ssh-rsa AAAAB3NaC1y ... JTyWisGpFeRB+VTKQ7
atlanta1.my.com,10.0.5.2 ssh-ed25519 AAAAC3NaCZD ... UteryYr
denver8.my.com,10.2.13.3 ssh-rsa AAAAB3NFC2 ... 3tGDQDSfJD
...
(假设 库存 文件如下所示:
[master]
atlanta1.my.com
atlanta2.my.com
[slave]
denver1.my.com
denver8.my.com
)
与 Xiong 的回答相反,这将正确处理 known_hosts
文件的内容。
如果使用目标主机重新映像的虚拟化环境(因此 ssh 公钥被更改),此操作特别有用。
警告:host(以及 Ansible)将不再对任何 ssh 连接执行 SSH host 密钥验证以上设置。这可能存在风险,强烈不 在生产 环境
中推荐您也可以从服务器 os 级别进行设置。您将需要配置 ssh 配置文件以避免 ssh 检查提示:
编辑文件路径:
/etc/ssh/ssh_config
现在取消注释行:
StrictHostKeyChecking no
保存更改即可
警告:host(以及 Ansible)将不再对任何 ssh 连接执行 SSH host 密钥验证以上设置。这可能存在风险,强烈不 在生产 环境
中推荐关注@Stepan Vavra 的正确答案。较短的版本是:
- known_hosts:
name: "{{ item }}"
key: "{{ lookup('pipe', 'ssh-keyscan {{ item }},`dig +short {{ item }}`') }}"
with_items:
- google.com
- github.com
不会做这样的事情来启动 known_hosts 文件:
ANSIBLE_HOST_KEY_CHECKING=false ansible all -m ping
这应该连接到清单中的每台主机,更新每台主机的 known_hosts 文件,而不必为每个提示输入 "yes",然后在每个主机上运行 "ping" 模块主持人?
快速测试(删除我的 known_hosts 文件然后 运行 以上,在 Ubuntu 16.04 实例上完成)似乎用他们当前的 known_hosts 文件填充指纹。
@Stepan Vavra 的解决方案对我不起作用,因为我使用的是别名主机(连接到没有可用 DNS 的内部 IP,所以我想要更多描述性名称来指代中的每个主机清单并让 ansible_host 变量指向每个的实际 IP)。 运行 上面的内容简单多了,并且无需在 ansible 或 ssh 中禁用主机密钥检查就可以启动我的 known_hosts 文件。
我已经创建了这个 shell 脚本(顺便说一句,也适用于 Jenkins)
my_known_hosts="$HOME/.ssh/known_hosts"
## housekeeping ##
if [ -f $my_known_hosts".old" ]
then rm -f $my_known_hosts".old"
fi
## housekeeping ##
## backup ##
if [ -f $my_known_hosts ]
then mv $my_known_hosts "$my_known_hosts.old"
fi
## backup ##
## query aws for active hosts and add to known_hosts
aws ec2 describe-instances --query 'Reservations[*].Instances[*].NetworkInterfaces[*].Association.PublicDnsName' --output text | xargs -L1 ssh-keyscan -H >> $my_known_hosts
## query aws for active hosts and add to known_hosts
https://admin-o-mat.blogspot.com/2020/09/ansible-and-aws-adding-hosts-to.html