|
|
|
# test code for the known_hosts module
|
|
|
|
# (c) 2017, Marius Gedminas <marius@gedmin.as>
|
|
|
|
|
|
|
|
# This file is part of Ansible
|
|
|
|
#
|
|
|
|
# Ansible is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# Ansible is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
- name: copy an existing file in place
|
|
|
|
copy:
|
|
|
|
src: existing_known_hosts
|
|
|
|
dest: "{{ remote_tmp_dir }}/known_hosts"
|
|
|
|
|
|
|
|
# test addition
|
|
|
|
|
|
|
|
- name: add a new host in check mode
|
|
|
|
check_mode: yes
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: diff
|
|
|
|
|
|
|
|
- name: assert that the diff looks as expected (the key was added at the end)
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'diff is changed'
|
|
|
|
- 'diff.diff.before_header == diff.diff.after_header == remote_tmp_dir|expanduser + "/known_hosts"'
|
|
|
|
- 'diff.diff.after.splitlines()[:-1] == diff.diff.before.splitlines()'
|
|
|
|
- 'diff.diff.after.splitlines()[-1] == example_org_rsa_key.strip()'
|
|
|
|
|
|
|
|
- name: add a new host
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts
|
|
|
|
|
|
|
|
- name: assert that the key was added and ordering preserved
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'result is changed'
|
|
|
|
- 'known_hosts.stdout_lines[0].startswith("example.com")'
|
|
|
|
- 'known_hosts.stdout_lines[4].startswith("@cert-authority")'
|
|
|
|
- 'known_hosts.stdout_lines[5].startswith("# example.net")'
|
|
|
|
- 'known_hosts.stdout_lines[-1].strip() == example_org_rsa_key.strip()'
|
|
|
|
|
|
|
|
# test idempotence of addition
|
|
|
|
|
|
|
|
- name: add the same host in check mode
|
|
|
|
check_mode: yes
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: check
|
|
|
|
|
|
|
|
- name: assert that no changes were expected
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'check is not changed'
|
|
|
|
- 'check.diff.before == check.diff.after'
|
|
|
|
|
|
|
|
- name: add the same host
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v2
|
|
|
|
|
|
|
|
- name: assert that no changes happened
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'result is not changed'
|
|
|
|
- 'result.diff.before == result.diff.after'
|
|
|
|
- 'known_hosts.stdout == known_hosts_v2.stdout'
|
|
|
|
|
|
|
|
# https://github.com/ansible/ansible/issues/78598
|
|
|
|
# test removing nonexistent host key when the other keys exist for the host
|
|
|
|
- name: remove different key
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_ed25519_key }}"
|
|
|
|
state: absent
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: remove nonexistent key with check mode
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_ed25519_key }}"
|
|
|
|
state: absent
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
check_mode: yes
|
|
|
|
register: check_mode_result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_different_key_removal
|
|
|
|
|
|
|
|
- name: assert that no changes happened
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'result is not changed'
|
|
|
|
- 'check_mode_result is not changed'
|
|
|
|
- 'result.diff.before == result.diff.after'
|
|
|
|
- 'known_hosts_v2.stdout == known_hosts_different_key_removal.stdout'
|
|
|
|
|
|
|
|
# test removal
|
|
|
|
|
|
|
|
- name: remove the host in check mode
|
|
|
|
check_mode: yes
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: absent
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: diff
|
|
|
|
|
|
|
|
- name: assert that the diff looks as expected (the key was removed)
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'diff.diff.before_header == diff.diff.after_header == remote_tmp_dir|expanduser + "/known_hosts"'
|
|
|
|
- 'diff.diff.before.splitlines()[-1] == example_org_rsa_key.strip()'
|
|
|
|
- 'diff.diff.after.splitlines() == diff.diff.before.splitlines()[:-1]'
|
|
|
|
|
|
|
|
- name: remove the host
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: absent
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v3
|
|
|
|
|
|
|
|
- name: assert that the key was removed and ordering preserved
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'diff is changed'
|
|
|
|
- 'result is changed'
|
|
|
|
- '"example.org" not in known_hosts_v3.stdout'
|
|
|
|
- 'known_hosts_v3.stdout_lines[0].startswith("example.com")'
|
|
|
|
- 'known_hosts_v3.stdout_lines[-1].startswith("# example.net")'
|
|
|
|
|
|
|
|
# test idempotence of removal
|
|
|
|
|
|
|
|
- name: remove the same host in check mode
|
|
|
|
check_mode: yes
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: absent
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: check
|
|
|
|
|
|
|
|
- name: assert that no changes were expected
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'check is not changed'
|
|
|
|
- 'check.diff.before == check.diff.after'
|
|
|
|
|
|
|
|
- name: remove the same host
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: absent
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v4
|
|
|
|
|
|
|
|
- name: assert that no changes happened
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'result is not changed'
|
|
|
|
- 'result.diff.before == result.diff.after'
|
|
|
|
- 'known_hosts_v3.stdout == known_hosts_v4.stdout'
|
|
|
|
|
|
|
|
# test addition as hashed_host
|
|
|
|
|
|
|
|
- name: add a new hashed host
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
hash_host: yes
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v5
|
|
|
|
|
|
|
|
- name: assert that the key was added and ordering preserved
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'result is changed'
|
|
|
|
- 'known_hosts_v5.stdout_lines[0].startswith("example.com")'
|
|
|
|
- 'known_hosts_v5.stdout_lines[5].startswith("# example.net")'
|
|
|
|
- 'known_hosts_v5.stdout_lines[-1].strip().startswith("|1|")'
|
|
|
|
- 'known_hosts_v5.stdout_lines[-1].strip().endswith(example_org_rsa_key.strip().split()[-1])'
|
|
|
|
|
|
|
|
# test idempotence of hashed addition
|
|
|
|
|
|
|
|
- name: add the same host hashed
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
hash_host: yes
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v6
|
|
|
|
|
|
|
|
- name: assert that no changes happened
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'result is not changed'
|
|
|
|
- 'result.diff.before == result.diff.after'
|
|
|
|
- 'known_hosts_v5.stdout == known_hosts_v6.stdout'
|
|
|
|
|
|
|
|
# test hashed removal
|
|
|
|
|
|
|
|
- name: remove the hashed host
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: absent
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v7
|
|
|
|
|
|
|
|
- name: assert that the key was removed and ordering preserved
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'result is changed'
|
|
|
|
- 'example_org_rsa_key.strip().split()[-1] not in known_hosts_v7.stdout'
|
|
|
|
- 'known_hosts_v7.stdout_lines[0].startswith("example.com")'
|
|
|
|
- 'known_hosts_v7.stdout_lines[-1].startswith("# example.net")'
|
|
|
|
|
|
|
|
# test idempotence of removal
|
|
|
|
|
|
|
|
- name: remove the same hashed host
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: absent
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v8
|
|
|
|
|
|
|
|
- name: assert that no changes happened
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'result is not changed'
|
|
|
|
- 'result.diff.before == result.diff.after'
|
|
|
|
- 'known_hosts_v7.stdout == known_hosts_v8.stdout'
|
|
|
|
|
|
|
|
# test roundtrip plaintext => hashed => plaintext
|
|
|
|
# The assertions are rather relaxed, because most of this hash been tested previously
|
|
|
|
|
|
|
|
- name: add a new host
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v8
|
|
|
|
|
|
|
|
- name: assert the plaintext host is there
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'known_hosts_v8.stdout_lines[-1].strip() == example_org_rsa_key.strip()'
|
|
|
|
|
|
|
|
- name: update the host to hashed mode
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
hash_host: true
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v9
|
|
|
|
|
|
|
|
- name: assert the hashed host is there
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'known_hosts_v9.stdout_lines[-1].strip().startswith("|1|")'
|
|
|
|
- 'known_hosts_v9.stdout_lines[-1].strip().endswith(example_org_rsa_key.strip().split()[-1])'
|
|
|
|
|
|
|
|
- name: downgrade the host to plaintext mode
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v10
|
|
|
|
|
|
|
|
- name: assert the plaintext host is there
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'known_hosts_v10.stdout_lines[6].strip() == example_org_rsa_key.strip()'
|
|
|
|
|
|
|
|
# ... and remove the host again for the next test
|
|
|
|
|
|
|
|
- name: copy an existing file in place
|
|
|
|
copy:
|
|
|
|
src: existing_known_hosts
|
|
|
|
dest: "{{ remote_tmp_dir }}/known_hosts"
|
|
|
|
|
|
|
|
# Test key changes
|
|
|
|
|
|
|
|
- name: add a hashed host
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
hash_host: true
|
|
|
|
|
|
|
|
- name: change the key of a hashed host
|
|
|
|
known_hosts:
|
|
|
|
name: example.org
|
|
|
|
key: "{{ example_org_rsa_key.strip()[:-7] + 'RANDOM=' }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
hash_host: true
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v11
|
|
|
|
|
|
|
|
- name: assert the change took place and the key got modified
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'known_hosts_v11.stdout_lines[-1].strip().endswith("RANDOM=")'
|
|
|
|
|
|
|
|
- name: add the ed25519 host key
|
|
|
|
known_hosts:
|
|
|
|
name: host.example.com
|
|
|
|
key: "{{ host_example_com_ed25519_key }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v12
|
|
|
|
|
|
|
|
- name: assert that the key was added and ordering preserved
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'result is changed'
|
|
|
|
- 'known_hosts_v12.stdout_lines[0].startswith("example.com")'
|
|
|
|
- 'known_hosts_v12.stdout_lines[4].startswith("@cert-authority")'
|
|
|
|
- 'known_hosts_v12.stdout_lines[5].startswith("# example.net")'
|
|
|
|
- 'known_hosts_v12.stdout_lines[-1].strip() == host_example_com_ed25519_key.strip()'
|
|
|
|
|
|
|
|
- name: add the ed25519 ca key
|
|
|
|
known_hosts:
|
|
|
|
name: '*.example.com'
|
|
|
|
key: "{{ example_com_ed25519_ca }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v13
|
|
|
|
|
|
|
|
- name: assert that the key was added and ordering preserved
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'result is changed'
|
|
|
|
- 'known_hosts_v13.stdout_lines[0].startswith("example.com")'
|
|
|
|
- 'known_hosts_v13.stdout_lines[4].startswith("@cert-authority")'
|
|
|
|
- 'known_hosts_v13.stdout_lines[5].startswith("# example.net")'
|
|
|
|
- 'known_hosts_v13.stdout_lines[-1].strip() == example_com_ed25519_ca.strip()'
|
|
|
|
|
|
|
|
- name: Remove the ed25519 ca key
|
|
|
|
known_hosts:
|
|
|
|
name: '*.example.com'
|
|
|
|
key: "{{ example_com_ed25519_ca }}"
|
|
|
|
state: absent
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v14
|
|
|
|
|
|
|
|
- name: assert that the key was removed and ordering preserved
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'result is changed'
|
|
|
|
- 'known_hosts_v12.stdout == known_hosts_v14.stdout'
|
|
|
|
|
|
|
|
- name: add the revoked ed25519 host key
|
|
|
|
known_hosts:
|
|
|
|
name: 'host.example.com'
|
|
|
|
key: "@revoked {{ host_example_com_ed25519_signedhost }}"
|
|
|
|
state: present
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v15
|
|
|
|
|
|
|
|
- name: assert that the key was added and ordering preserved
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'result is changed'
|
|
|
|
- 'known_hosts_v15.stdout_lines[0].startswith("example.com")'
|
|
|
|
- 'known_hosts_v15.stdout_lines[4].startswith("@cert-authority")'
|
|
|
|
- 'known_hosts_v15.stdout_lines[5].startswith("# example.net")'
|
|
|
|
- 'known_hosts_v15.stdout_lines[-1].strip() == "@revoked " ~ host_example_com_ed25519_signedhost.strip()'
|
|
|
|
|
|
|
|
- name: remove the revoked ed25519 host key
|
|
|
|
known_hosts:
|
|
|
|
name: 'host.example.com'
|
|
|
|
key: "@revoked {{ host_example_com_ed25519_signedhost }}"
|
|
|
|
state: absent
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: get the file content
|
|
|
|
command: "cat {{remote_tmp_dir}}/known_hosts"
|
|
|
|
register: known_hosts_v16
|
|
|
|
|
|
|
|
- name: assert that the key was removed and ordering preserved
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- 'result is changed'
|
|
|
|
- 'known_hosts_v12.stdout == known_hosts_v16.stdout'
|
|
|
|
|
|
|
|
# test errors
|
|
|
|
|
|
|
|
- name: Try using a comma separated list of hosts
|
|
|
|
known_hosts:
|
|
|
|
name: example.org,acme.com
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
ignore_errors: yes
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: Assert that error message was displayed
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- result is failed
|
|
|
|
- result.msg == 'Comma separated list of names is not supported. Please pass a single name to lookup in the known_hosts file.'
|
|
|
|
|
|
|
|
- name: Try using a name that does not match the key
|
|
|
|
known_hosts:
|
|
|
|
name: example.com
|
|
|
|
key: "{{ example_org_rsa_key }}"
|
|
|
|
path: "{{remote_tmp_dir}}/known_hosts"
|
|
|
|
ignore_errors: yes
|
|
|
|
register: result
|
|
|
|
|
|
|
|
- name: Assert that name checking failed with error message
|
|
|
|
assert:
|
|
|
|
that:
|
|
|
|
- result is failed
|
|
|
|
- result.msg == 'Host parameter does not match hashed host field in supplied key'
|