openssh_cert - Adding regenerate option (#256)

* Initial commit

* Fixing unit tests

* More unit fixes

* Adding changelog fragment

* Minor refactor in Certificate.generate()

* Addressing option case-sensitivity and directive overrides

* Renaming idempotency to regenerate

* updating changelog

* Minor refactoring of default options

* Cleaning up with inline functions

* Fixing false failures when regenerate=fail and improving clarity

* Applying second round of review suggestions

* adding helper for safe atomic moves
This commit is contained in:
Ajpantuso
2021-07-31 05:36:03 -04:00
committed by GitHub
parent d6403ace6e
commit aaba87ac57
10 changed files with 704 additions and 76 deletions

View File

@@ -22,6 +22,12 @@
- name: Import key_idempotency tests
import_tasks: ../tests/key_idempotency.yml
- name: Import options tests
import_tasks: ../tests/options_idempotency.yml
- name: Import regenerate tests
import_tasks: ../tests/regenerate.yml
- name: Import remove tests
import_tasks: ../tests/remove.yml
when: not (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6")

View File

@@ -177,12 +177,17 @@
valid_from: "2001-01-21"
valid_to: "2019-01-21"
changed: false
# Options are currently not checked for idempotency purposes
- test_name: Generate an OpenSSH user Certificate with no options (idempotent)
type: user
valid_from: "2001-01-21"
valid_to: "2019-01-21"
changed: false
- test_name: Generate an OpenSSH user Certificate with no options - full idempotency (idempotent)
type: user
valid_from: "2001-01-21"
valid_to: "2019-01-21"
regenerate: full_idempotence
changed: true
- test_name: Generate cert without serial
type: user
valid_from: always
@@ -228,13 +233,19 @@
valid_from: always
valid_to: forever
changed: false
# Identifiers are not included in idempotency checks so a new cert will not be generated
- test_name: Generate cert with identifier
type: user
identifier: foo
valid_from: always
valid_to: forever
changed: false
- test_name: Generate cert with identifier - full idempotency
type: user
identifier: foo
valid_from: always
valid_to: forever
regenerate: full_idempotence
changed: true
- name: Execute idempotency tests
openssh_cert:
@@ -251,6 +262,7 @@
valid_at: "{{ test_case.valid_at | default(omit) }}"
valid_from: "{{ test_case.valid_from | default(omit) }}"
valid_to: "{{ test_case.valid_to | default(omit) }}"
regenerate: "{{ test_case.regenerate | default(omit) }}"
check_mode: "{{ test_case.check_mode | default(omit) }}"
register: idempotency_test_output
loop: "{{ test_cases }}"

View File

@@ -40,12 +40,35 @@
valid_to: forever
register: new_public_key_output
# Signing key and public key are not considered during idempotency checks
- name: Assert changes to public key or signing key results in no change
- name: Generate cert with new signing key - full idempotency
openssh_cert:
type: user
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ new_signing_key }}"
valid_from: always
valid_to: forever
regenerate: full_idempotence
register: new_signing_key_full_idempotency_output
- name: Generate cert with new pubic key - full idempotency
openssh_cert:
type: user
path: "{{ certificate_path }}"
public_key: "{{ new_public_key }}"
signing_key: "{{ new_signing_key }}"
valid_from: always
valid_to: forever
regenerate: full_idempotence
register: new_public_key_full_idempotency_output
- name: Assert changes to public key or signing key results in no change unless idempotency=full
assert:
that:
- new_signing_key_output is not changed
- new_public_key_output is not changed
- new_signing_key_full_idempotency_output is changed
- new_public_key_full_idempotency_output is changed
- name: Remove certificate
openssh_cert:

View File

@@ -0,0 +1,102 @@
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
- name: Generate cert with no options
openssh_cert:
type: user
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ signing_key }}"
valid_from: always
valid_to: forever
options:
- clear
regenerate: full_idempotence
register: no_options
- name: Generate cert with no options with explicit directives
openssh_cert:
type: user
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ signing_key }}"
valid_from: always
valid_to: forever
options:
- no-user-rc
- no-x11-forwarding
- no-agent-forwarding
- no-port-forwarding
- no-pty
regenerate: full_idempotence
register: no_options_explicit_directives
- name: Generate cert with explicit extension
openssh_cert:
type: user
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ signing_key }}"
valid_from: always
valid_to: forever
options:
- clear
- permit-pty
regenerate: full_idempotence
register: explicit_extension_before
- name: Generate cert with explicit extension (idempotency)
openssh_cert:
type: user
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ signing_key }}"
valid_from: always
valid_to: forever
options:
- clear
- permit-pty
regenerate: full_idempotence
register: explicit_extension_after
- name: Generate cert with explicit extension and corresponding directive
openssh_cert:
type: user
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ signing_key }}"
valid_from: always
valid_to: forever
options:
- no-pty
- permit-pty
regenerate: full_idempotence
register: explicit_extension_and_directive
- name: Generate cert with default options
openssh_cert:
type: user
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ signing_key }}"
valid_from: always
valid_to: forever
regenerate: full_idempotence
register: default_options
- name: Assert options results
assert:
that:
- no_options is changed
- no_options_explicit_directives is not changed
- explicit_extension_before is changed
- explicit_extension_after is not changed
- explicit_extension_and_directive is changed
- default_options is not changed
- name: Remove certificate
openssh_cert:
path: "{{ certificate_path }}"
state: absent

View File

@@ -0,0 +1,135 @@
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
- set_fact:
test_cases:
- test_name: Generate certificate
type: user
signing_key: "{{ signing_key }}"
public_key: "{{ public_key }}"
path: "{{ certificate_path }}"
valid_from: always
valid_to: forever
regenerate: never
changed: true
- test_name: Regenerate never - same options
type: user
signing_key: "{{ signing_key }}"
public_key: "{{ public_key }}"
path: "{{ certificate_path }}"
valid_from: always
valid_to: forever
regenerate: never
changed: false
- test_name: Regenerate never - different options
type: user
signing_key: "{{ signing_key }}"
public_key: "{{ public_key }}"
path: "{{ certificate_path }}"
valid_from: always
valid_to: forever
options:
- clear
regenerate: never
changed: false
- test_name: Regenerate never with force
force: true
type: user
signing_key: "{{ signing_key }}"
public_key: "{{ public_key }}"
path: "{{ certificate_path }}"
valid_from: always
valid_to: forever
regenerate: never
changed: true
- test_name: Remove certificate
path: "{{ certificate_path }}"
state: absent
changed: true
- test_name: Regenerate fail - new certificate
type: user
signing_key: "{{ signing_key }}"
public_key: "{{ public_key }}"
path: "{{ certificate_path }}"
valid_from: always
valid_to: forever
regenerate: fail
changed: true
- test_name: Regenerate fail - same options
type: user
signing_key: "{{ signing_key }}"
public_key: "{{ public_key }}"
path: "{{ certificate_path }}"
valid_from: always
valid_to: forever
regenerate: fail
changed: false
- test_name: Regenerate fail - different options
type: user
signing_key: "{{ signing_key }}"
public_key: "{{ public_key }}"
path: "{{ certificate_path }}"
valid_from: always
valid_to: forever
options:
- clear
regenerate: fail
changed: false
ignore_errors: true
- test_name: Regenerate fail with force
force: true
type: user
signing_key: "{{ signing_key }}"
public_key: "{{ public_key }}"
path: "{{ certificate_path }}"
valid_from: always
valid_to: forever
regenerate: fail
changed: true
- test_name: Regenerate always
type: user
signing_key: "{{ signing_key }}"
public_key: "{{ public_key }}"
path: "{{ certificate_path }}"
valid_from: always
valid_to: forever
regenerate: always
changed: true
- name: Execute regenerate tests
openssh_cert:
force: "{{ test_case.force | default(omit) }}"
options: "{{ test_case.options | default(omit) }}"
path: "{{ test_case.path | default(omit) }}"
public_key: "{{ test_case.public_key | default(omit) }}"
principals: "{{ test_case.principals | default(omit) }}"
regenerate: "{{ test_case.regenerate | default(omit) }}"
serial_number: "{{ test_case.serial_number | default(omit) }}"
signing_key: "{{ test_case.signing_key | default(omit) }}"
state: "{{ test_case.state | default(omit) }}"
type: "{{ test_case.type | default(omit) }}"
valid_at: "{{ test_case.valid_at | default(omit) }}"
valid_from: "{{ test_case.valid_from | default(omit) }}"
valid_to: "{{ test_case.valid_to | default(omit) }}"
check_mode: "{{ test_case.check_mode | default(omit) }}"
ignore_errors: "{{ test_case.ignore_errors | default(omit) }}"
register: regenerate_tests_output
loop: "{{ test_cases }}"
loop_control:
loop_var: test_case
- name: Assert task statuses
assert:
that:
- result.changed == test_cases[index].changed
loop: "{{ regenerate_tests_output.results }}"
loop_control:
index_var: index
loop_var: result
- name: Remove certificate
openssh_cert:
path: "{{ certificate_path }}"
state: absent