Ansible 如何包含组中所有主机的所有主机密钥

Ansible 如何包含组中所有主机的所有主机密钥,ansible,Ansible,在尝试学习Ansible的过程中,我创建了一个难以实现的场景。假设我想使用known_hosts模块将所有SSH主机密钥添加到组中每个成员的/etc/SSH/SSH_known_hosts文件中。我使用的是2.7,并试图使用折旧循环函数远离。我可以获得debug中列出的所有键,但我无法找到一种方法在与模块的循环中实现它。为了理解如何一般地做到这一点,我假设并非所有主机都具有所有类型的键,因此它需要是动态的。以下是我到目前为止的情况: - name: SSH Host Keys Debug

在尝试学习Ansible的过程中,我创建了一个难以实现的场景。假设我想使用known_hosts模块将所有SSH主机密钥添加到组中每个成员的/etc/SSH/SSH_known_hosts文件中。我使用的是2.7,并试图使用折旧循环函数远离
。我可以获得debug中列出的所有键,但我无法找到一种方法在与模块的循环中实现它。为了理解如何一般地做到这一点,我假设并非所有主机都具有所有类型的键,因此它需要是动态的。以下是我到目前为止的情况:

- name: SSH Host Keys Debug                                                     
  debug:                                                                        
    msg: "Hostname: {{ hostvars[item].ansible_hostname }},                      
          IP Address: {{ hostvars[item].ansible_eth0.ipv4.address }},           
          Keys: {{ hostvars[item] | select('match', '^ansible_ssh_host_key_.+_public') | map('extract', hostvars[item]) | list }}"
  loop: "{{ groups['haproxy'] }}" 
然后创建一个循环函数,根据模块拥有的键数,在每个主机上多次调用该模块

- name: SSH Host Keys                                                           
  become: true                                                                  
  known_hosts:                                                                  
    dest: /etc/ssh/ssh_known_hosts                                              
    name: "{{ item.ansible_hostname }}"                                         
    key: "{{ item.ansible_hostname }},{{ item.ansible_eth0.ipv4.address }} {{ item.<public_key> }}"
  loop: <nested loop that provides hostname, ipv4_address and public_keys>
调试的输出为:

TASK [base_conf : SSH Host Keys Debug] ************************************************************************************************
task path: /home/rleblanc/code/ansible_learn/base_conf/tasks/main.yml:30
ok: [ansible-a] => (item=ansible-a) => {
    "msg": "Hostname: ansible-a, IP Address: 192.168.99.48, Keys: ['AAAAB3NzaC1yc2EAAAADAQABAAABAQDH3OunSMQfaksqVxlLhKUyDUegaw6QuOiemgBWwwJypiDRshF0N2xQ6/RqHYA/gY+oDieIHzbs6OtxNt7JbSOwkjnKrYBkqzVQqtCtjpJ+pbzAcxdYwjfEYNlV/Fq41XrsFaWQgbQB57yS3dJVneheXskSc/mIwX3a2143X1CFLSz9krhwcNIWaAhZFMlV0ZqRSvHDoiDZ4rQ4qQ4riaTm/NXjJzJjQqSiwUZUQdBtv88Ik1trQJUwKsYq2WZKiuv6yp/XVLL1/LLYQQJeH2GRqy8EI1TYRunrfHEo/D3T5QPsaJ1up/YNPtRP+H3dA68Ybwowb8m5A9IoAtHbHdEr', 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDhGYjJS18MSCojIDLA9MTxITHpy+IOBFCHR+ZSZMyr5ek0r4RCK+zo6D1WZNs0dWcUB7IUJMThKcPpxdbrC0rk=', 'AAAAC3NzaC1lZDI1NTE5AAAAIBZjI7AJ5SHU31V6Vs9WTxLss/5gU/3pJJlTTzpJxcyr']"
}
编辑1 一些伪代码实现了我对每个主机的期望:

for host in groups['haproxy']:
   for key in host.ansible_ssh_host_key_*_public:
     <pass key to known_host module>

Ansible并不擅长嵌套循环。我们可以解决这个问题,但有点难看。
ecdsa
公钥使我们的生活变得复杂,因为对于大多数密钥,公钥文件中的类型指定为
ssh-
,而对于
ecdsa
密钥,显然是
ecdsa-sha2-nistp256

以下是我的想法:

---
- hosts: all
  tasks:

    # In this first task, we construct for each host in the `all` group
    # a list of dictionaries that contain information about the ssh
    # hostkeys, extracted from the `ansible_ssh_host_key_*` variables.
    #
    # We are looping over all variables (and values) for this host for which
    # the variable name starts with `ansible_ssh_host_key`.
    - set_fact:
        hostkeys: >-
          {{ (hostkeys|default([])) + [{
            'hostname': inventory_hostname,
            'type': item.key|regex_replace('ansible_ssh_host_key_([^_]+)_public', '\1'),
            'key': item.value
          }] }}
      loop: >-
        {{ hostvars[inventory_hostname]|
           dict2items|
           selectattr('key', 'match', '^ansible_ssh_host_key_')|list }}

- hosts: localhost
  gather_facts: false
  tasks:

    # Now we take those per-host lists and construct one combined list
    # with all the information.
    - set_fact:
        hostkeys: "{{ (hostkeys|default([])) + hostvars[item].hostkeys }}"
      loop: "{{ groups.all }}"

- hosts: all
  gather_facts: false
  tasks:

    # Finally, on each host, we write a known_hosts file containing all the
    # host keys. I'm using an alternate path here because I didn't want
    # to actually write to /etc/ssh/known_hosts on my system.
    - known_hosts:
        path: "/tmp/hosts-{{ inventory_hostname }}"
        name: "{{ item.hostname }}"
        key: "{{ item.hostname }} {{ keytype }} {{ item.key }}"
      vars:
        keytype: >-
          {{ (item.type == 'ecdsa')|
             ternary('ecdsa-sha2-nistp256', 'ssh-' ~ item.type) }}
      loop: "{{ hostvars.localhost.hostkeys }}"
      loop_control:
        label: "{{ item.hostname }} {{ keytype }} {{ item.key[:20] }}..."

我没有在这里讨论ip地址,但是添加ip地址应该不会太困难(在第一步生成的
hostkeys
字典中包含该信息,然后在known_hosts模块的参数中使用该信息)。

谢谢。我想知道是否需要执行中间步骤并将其保存在变量中。因为我是在角色中使用它的,所以我需要对它进行一些调整。我也有一些错误,hostvars不是dict2items的dict,我不能让角色在localhost上只运行一次,但是我可以让它为每个主机存储,所以它看起来已经足够好了。
- name: Generate Host key list per host                                         
  set_fact:                                                                     
    myhostkeys: >-                                                              
      {{ (myhostkeys | default([]) + [{                                         
        'hostname': inventory_hostname,                                         
        'ip_addr': hostvars[inventory_hostname]['ansible_default_ipv4']['address'],
        'type': item | regex_replace('ansible_ssh_host_key_([^_]+)_public', '\1'),
        'key': hostvars[inventory_hostname][item]                               
      }]) }}                                                                    
  loop: >-                                                                      
    {{ hostvars[inventory_hostname] | select('match', '^ansible_ssh_host_key_.*_public') | list }}
                                                                                
- name: Combine all host keys                                                   
  set_fact:                                                                     
    hostkeys: >-                                                                
      {{ (hostkeys | default([])) + hostvars[item]['myhostkeys'] }}             
  loop: >-                                                                      
      {{ ansible_play_hosts_all }}                                              
                                                                                
- name: Add all host keys                                                       
  become: true                                                                  
  known_hosts:                                                                  
    path: /etc/ssh/ssh_known_hosts                                              
    name: "{{ item.hostname }}"                                                 
    key: "{{ item.hostname }},{{ item.ip_addr }} {{ keytype }} {{ item.key }}"  
  vars:                                                                         
    keytype: >-                                                                 
      {{ (item.type == 'ecdsa') |                                               
        ternary('ecdsa-sha2-nistp256', 'ssh-' ~ item.type) }}                   
  loop: >-                                                                      
    {{ hostvars[inventory_hostname]['hostkeys'] }}                              
  loop_control:                                                                 
    label: "Key: \"{{ item.hostname }},{{ item.ip_addr }} {{ keytype }} {{ item.key[:20] }}...\""
---
- hosts: all
  tasks:

    # In this first task, we construct for each host in the `all` group
    # a list of dictionaries that contain information about the ssh
    # hostkeys, extracted from the `ansible_ssh_host_key_*` variables.
    #
    # We are looping over all variables (and values) for this host for which
    # the variable name starts with `ansible_ssh_host_key`.
    - set_fact:
        hostkeys: >-
          {{ (hostkeys|default([])) + [{
            'hostname': inventory_hostname,
            'type': item.key|regex_replace('ansible_ssh_host_key_([^_]+)_public', '\1'),
            'key': item.value
          }] }}
      loop: >-
        {{ hostvars[inventory_hostname]|
           dict2items|
           selectattr('key', 'match', '^ansible_ssh_host_key_')|list }}

- hosts: localhost
  gather_facts: false
  tasks:

    # Now we take those per-host lists and construct one combined list
    # with all the information.
    - set_fact:
        hostkeys: "{{ (hostkeys|default([])) + hostvars[item].hostkeys }}"
      loop: "{{ groups.all }}"

- hosts: all
  gather_facts: false
  tasks:

    # Finally, on each host, we write a known_hosts file containing all the
    # host keys. I'm using an alternate path here because I didn't want
    # to actually write to /etc/ssh/known_hosts on my system.
    - known_hosts:
        path: "/tmp/hosts-{{ inventory_hostname }}"
        name: "{{ item.hostname }}"
        key: "{{ item.hostname }} {{ keytype }} {{ item.key }}"
      vars:
        keytype: >-
          {{ (item.type == 'ecdsa')|
             ternary('ecdsa-sha2-nistp256', 'ssh-' ~ item.type) }}
      loop: "{{ hostvars.localhost.hostkeys }}"
      loop_control:
        label: "{{ item.hostname }} {{ keytype }} {{ item.key[:20] }}..."