feat(modules): added unseal module

This commit is contained in:
Bertrand Lanson 2023-12-29 12:51:00 +01:00
parent 90b20f2b83
commit 29d906bcd2
13 changed files with 55 additions and 44 deletions

View File

@ -7,7 +7,7 @@
tasks: tasks:
- name: "Include ednxzu.hashicorp_vault" - name: "Include ednxzu.hashicorp_vault"
ansible.builtin.include_role: ansible.builtin.include_role:
name: ednxzu.hashicorp_vault name: ednxzu.hashistack.hashicorp_vault
- name: "Initialize vault cluster" - name: "Initialize vault cluster"
ednxzu.hashistack.vault_init: ednxzu.hashistack.vault_init:
@ -20,14 +20,33 @@
- name: "Write vault configuration to file" - name: "Write vault configuration to file"
ansible.builtin.copy: ansible.builtin.copy:
content: "{{ _vault_init_secret.state }}" content: "{{ _vault_init_secret.state | to_nice_yaml}}"
dest: "{{ configuration_directory }}/vault/vault_config" dest: "{{ configuration_directory }}/vault/vault_config"
mode: '0600' mode: '0644'
when: _vault_init_secret.state when: _vault_init_secret.changed
run_once: true run_once: true
delegate_to: localhost delegate_to: localhost
- name: "Load vault cluster variables necessary for unseal operation"
ansible.builtin.include_vars:
file: "{{ configuration_directory }}/vault/vault_config"
name: _vault_cluster_config
- name: "Unseal the bootstrap node"
ednxzu.hashistack.vault_unseal:
api_url: "http://127.0.0.1:8200"
key_shares: "{{ _vault_cluster_config['keys'] }}"
max_retries: "{{ (_vault_cluster_config['keys'] | length) - 1 }}"
run_once: true
delegate_to: "{{ groups['vault_servers'] | first }}"
when: _vault_init_secret.changed
- name: "Unseal all vault nodes"
ednxzu.hashistack.vault_unseal:
api_url: "http://127.0.0.1:8200"
key_shares: "{{ _vault_cluster_config['keys'] }}"
max_retries: "{{ (_vault_cluster_config['keys'] | length) - 1 }}"
- name: "Debug" - name: "Debug"
ansible.builtin.debug: ansible.builtin.debug:
msg: "{{ _vault_init_secret }}" msg: "{{ _vault_cluster_config }}"
when: _vault_init_secret is defined

View File

@ -99,7 +99,7 @@ def run_module():
if not HAS_HVAC: if not HAS_HVAC:
module.fail_json( module.fail_json(
msg="missing_required_lib(hvac)", msg="Missing required library: hvac",
exception=HVAC_IMPORT_ERROR exception=HVAC_IMPORT_ERROR
) )

View File

@ -68,6 +68,7 @@ message:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
import traceback
try: try:
import hvac import hvac
@ -79,70 +80,61 @@ else:
HAS_HVAC = True HAS_HVAC = True
def run_module(): def run_module():
# define available arguments/parameters a user can pass to the module
module_args = dict( module_args = dict(
api_url=dict(type='str', required=True), api_url=dict(type='str', required=True),
key_shares=dict(type='list', required=False, default=[]),
name=dict(type='str', required=True), max_retries=dict(type='int', required=False, default=3)
new=dict(type='bool', required=False, default=False)
) )
# seed the result dict in the object
# we primarily care about changed and state
# changed is if this module effectively modified the target
# state will include any data that you want your module to pass back
# for consumption, for example, in a subsequent task
result = dict( result = dict(
changed=False, changed=False,
original_message='', original_message='',
message='' state=''
) )
# the AnsibleModule object will be our abstraction working with Ansible
# this includes instantiation, a couple of common attr would be the
# args/params passed to the execution, as well as if the module
# supports check mode
module = AnsibleModule( module = AnsibleModule(
argument_spec=module_args, argument_spec=module_args,
supports_check_mode=True supports_check_mode=True
) )
if not HAS_HVAC: # Check if hvac module is available
try:
import hvac
except ImportError:
module.fail_json( module.fail_json(
msg=missing_required_lib("hvac"), msg="Missing required library: hvac",
exception=HVAC_IMPORT_ERROR, exception=HVAC_IMPORT_ERROR
) )
# if the user is working with this module in only check mode we do not
# want to make any changes to the environment, just return the current
# state with no modifications
if module.check_mode: if module.check_mode:
module.exit_json(**result) module.exit_json(**result)
# manipulate or modify the state as needed (this is going to be the # Initialize HashiCorp Vault client
# part where your module will do what it needs to do) client = hvac.Client(
result['original_message'] = module.params['name'] url=module.params['api_url']
result['message'] = 'goodbye' )
# use whatever logic you need to determine whether or not this module # Unseal Vault
# made any modifications to your target try:
if module.params['new']: retries = 0
result['changed'] = True while client.sys.is_sealed() and retries < module.params['max_retries']:
key_share = module.params['key_shares'][min(retries, len(module.params['key_shares']) - 1)]
vault_unseal_result = client.sys.submit_unseal_key(key_share)
retries += 1
except hvac.exceptions.VaultError as ve:
module.fail_json(msg=f"Vault unsealing failed: {ve}")
# during the execution of the module, if there is an exception or a # Check if the Vault is successfully unsealed
# conditional state that effectively causes a failure, run if client.sys.is_sealed():
# AnsibleModule.fail_json() to pass in the message and the result module.fail_json(msg="Vault unsealing failed: Maximum retries reached.")
if module.params['name'] == 'fail me':
module.fail_json(msg='You requested this to fail', **result) result['state'] = vault_unseal_result
result['changed'] = True
# in the event of a successful module execution, you will want to
# simple AnsibleModule.exit_json(), passing the key/value results
module.exit_json(**result) module.exit_json(**result)
def main(): def main():
run_module() run_module()
if __name__ == '__main__': if __name__ == '__main__':
main() main()