test(ssh): cover key rotation with Molecule
Replace a private key while retaining its public key file to detect stale derived keys. Verify the replacement public key, ownership and file permissions. Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2 files changed
+69
-3
No files matched your search
@@ -37,10 +37,22 @@
|
||||
- "{{ _authorized_key }}"
|
||||
|
||||
# The key pair is generated instead of committed, private key material does not belong into a repository.
|
||||
- name: Generate the ssh key pair the role is fed with
|
||||
- name: Generate the SSH key pairs for initial configuration and rotation
|
||||
ansible.builtin.command:
|
||||
cmd: "ssh-keygen -t ed25519 -N '' -C molecule -f {{ _private_key }}"
|
||||
creates: "{{ _private_key }}"
|
||||
argv:
|
||||
- ssh-keygen
|
||||
- -t
|
||||
- ed25519
|
||||
- -N
|
||||
- ''
|
||||
- -C
|
||||
- molecule
|
||||
- -f
|
||||
- "{{ item }}"
|
||||
creates: "{{ item }}"
|
||||
loop:
|
||||
- "{{ _private_key }}"
|
||||
- "{{ _private_key }}.replacement"
|
||||
|
||||
- name: Offer the public key as authorized key fixture
|
||||
ansible.builtin.copy:
|
||||
|
||||
@@ -142,3 +142,57 @@
|
||||
- _bob_files.results[0].stat.exists
|
||||
- not _bob_files.results[1:] | map(attribute='stat.exists') | select | list
|
||||
fail_msg: "molecule-bob has files that were never declared for him"
|
||||
|
||||
- name: Verify SSH key rotation
|
||||
hosts: all
|
||||
vars:
|
||||
_replacement_key: "{{ molecule_scenario_directory }}/files/ssh/private_keys/molecule.ed25519.key.replacement"
|
||||
unix_groups: {}
|
||||
unix_users:
|
||||
molecule-alice:
|
||||
group: molecule-alice
|
||||
password: alice
|
||||
ssh:
|
||||
private_keys:
|
||||
- filename: alice.ed25519.key
|
||||
content: "{{ lookup('ansible.builtin.file', _replacement_key, rstrip=false) }}"
|
||||
state: present
|
||||
tasks:
|
||||
- name: Replace the private key while keeping the existing public key file
|
||||
ansible.builtin.include_role:
|
||||
name: unix-users
|
||||
|
||||
- name: Read the public key after rotation
|
||||
ansible.builtin.slurp:
|
||||
src: /home/molecule-alice/.ssh/alice.ed25519.key.pub
|
||||
register: _rotated_public_key
|
||||
|
||||
- name: Assert the existing public key matches the replacement key and differs from the original
|
||||
vars:
|
||||
_expected_public_key: "{{ lookup('ansible.builtin.file', _replacement_key + '.pub') }}"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (_rotated_public_key.content | b64decode).split()[1] == _expected_public_key.split()[1]
|
||||
- _rotated_public_key.content != _alice_keys.results[1].content
|
||||
|
||||
- name: Stat the rotated private and public keys
|
||||
ansible.builtin.stat:
|
||||
path: "{{ item.path }}"
|
||||
loop:
|
||||
- path: /home/molecule-alice/.ssh/alice.ed25519.key
|
||||
mode: '0600'
|
||||
- path: /home/molecule-alice/.ssh/alice.ed25519.key.pub
|
||||
mode: '0644'
|
||||
loop_control:
|
||||
label: "{{ item.path }}"
|
||||
register: _rotated_key_files
|
||||
|
||||
- name: Assert rotated keys retain their owner and permissions
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- item.stat.pw_name == 'molecule-alice'
|
||||
- item.stat.gr_name == 'molecule-alice'
|
||||
- item.stat.mode == item.item.mode
|
||||
loop: "{{ _rotated_key_files.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item.path }}"
|
||||
Reference in new issue
Block a user