From 97a4752dbe8b4b49a8310a795479a50e25131f14 Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Fri, 29 Dec 2023 23:40:34 +0100 Subject: [PATCH] feat(playbook): vault deployment is smooth-ish, unseals and initialize cluster as needed --- .gitignore | 1 + playbooks/deploy.yml | 11 ++++-- playbooks/group_vars/all.yml | 4 +- plugins/modules/vault_unseal.py | 65 ++++++++++++++++----------------- 4 files changed, 41 insertions(+), 40 deletions(-) diff --git a/.gitignore b/.gitignore index 1cc2a51..d3ac8ef 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ **/__pycache__ .vscode roles/ednxzu.* +etc* \ No newline at end of file diff --git a/playbooks/deploy.yml b/playbooks/deploy.yml index 6c64222..285dddd 100644 --- a/playbooks/deploy.yml +++ b/playbooks/deploy.yml @@ -15,8 +15,11 @@ key_shares: 3 key_threshold: 2 run_once: true + retries: 5 + delay: 5 delegate_to: "{{ groups['vault_servers'] | first }}" register: _vault_init_secret + until: not _vault_init_secret.failed - name: "Write vault configuration to file" ansible.builtin.copy: @@ -46,7 +49,7 @@ api_url: "http://127.0.0.1:8200" key_shares: "{{ _vault_cluster_config['keys'] }}" max_retries: "{{ (_vault_cluster_config['keys'] | length) - 1 }}" - - - name: "Debug" - ansible.builtin.debug: - msg: "{{ _vault_cluster_config }}" + retries: 5 + delay: 5 + register: _unseal_status + until: not _unseal_status.failed diff --git a/playbooks/group_vars/all.yml b/playbooks/group_vars/all.yml index db2ad06..fa38d8f 100644 --- a/playbooks/group_vars/all.yml +++ b/playbooks/group_vars/all.yml @@ -11,7 +11,7 @@ deployment_method: "docker" api_interface: "eth0" api_interface_address: "{{ ansible_facts[api_interface]['ipv4']['address'] }}" -configuration_directory: "/tmp/hashistack" +configuration_directory: "{{ lookup('env', 'PWD') }}/etc/hashistack" ########################## # Support options ######## @@ -93,7 +93,7 @@ default_container_extra_volumes: - "/etc/localtime:/etc/localtime" hashi_vault_start_service: true -hashi_vault_version: latest +hashi_vault_version: "1.15.2" hashi_vault_deploy_method: "{{ deployment_method }}" # deployment method, either host or docker hashi_vault_env_variables: {} hashi_vault_data_dir: "/opt/vault" diff --git a/plugins/modules/vault_unseal.py b/plugins/modules/vault_unseal.py index 0d72a4c..49812c8 100644 --- a/plugins/modules/vault_unseal.py +++ b/plugins/modules/vault_unseal.py @@ -1,9 +1,10 @@ #!/usr/bin/python -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: my_test @@ -33,9 +34,9 @@ options: author: - Your Name (@yourGitHubHandle) -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # Pass in a message - name: Test with a message my_namespace.my_collection.my_test: @@ -51,9 +52,9 @@ EXAMPLES = r''' - name: Test failure of the module my_namespace.my_collection.my_test: name: fail me -''' +""" -RETURN = r''' +RETURN = r""" # These are examples of possible return values, and in general should use other names for return values. original_message: description: The original name param that was passed in. @@ -65,8 +66,7 @@ message: type: str returned: always sample: 'goodbye' -''' - +""" from ansible.module_utils.basic import AnsibleModule import traceback @@ -79,46 +79,41 @@ else: HVAC_IMPORT_ERROR = None HAS_HVAC = True + def run_module(): module_args = dict( - api_url=dict(type='str', required=True), - key_shares=dict(type='list', required=False, default=[]), - max_retries=dict(type='int', required=False, default=3) + api_url=dict(type="str", required=True), + key_shares=dict(type="list", required=False, default=[]), + max_retries=dict(type="int", required=False, default=3), ) - result = dict( - changed=False, - original_message='', - state='' - ) + result = dict(changed=False, original_message="", state=None) - module = AnsibleModule( - argument_spec=module_args, - supports_check_mode=True - ) + module = AnsibleModule(argument_spec=module_args, supports_check_mode=True) - # Check if hvac module is available - try: - import hvac - except ImportError: + if not HAS_HVAC: module.fail_json( - msg="Missing required library: hvac", - exception=HVAC_IMPORT_ERROR + msg="Missing required library: hvac", exception=HVAC_IMPORT_ERROR ) if module.check_mode: module.exit_json(**result) # Initialize HashiCorp Vault client - client = hvac.Client( - url=module.params['api_url'] - ) + client = hvac.Client(url=module.params["api_url"]) + + # Check if Vault is sealed + if not client.sys.is_sealed(): + module.exit_json(**result) # Unseal Vault try: retries = 0 - 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 = None + 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: @@ -128,13 +123,15 @@ def run_module(): if client.sys.is_sealed(): module.fail_json(msg="Vault unsealing failed: Maximum retries reached.") - result['state'] = vault_unseal_result - result['changed'] = True + result["state"] = vault_unseal_result + result["changed"] = True module.exit_json(**result) + def main(): run_module() -if __name__ == '__main__': + +if __name__ == "__main__": main()