From 00883b2dec5e2a1ca36479a269161f531ec03f48 Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Thu, 25 Jan 2024 22:40:44 +0100 Subject: [PATCH] feat(consul): add initial simple consul cluster --- molecule/no_tls_multi_node/.vault_password | 2 + molecule/no_tls_multi_node/ansible.cfg | 8 ++ .../etc/hashistack/globals.yml | 6 ++ playbooks/bootstrap.yml | 1 + playbooks/deploy.yml | 28 +++--- playbooks/group_vars/all.yml | 99 +++++++++++++++++-- playbooks/tasks/consul_vars.yml | 31 ++++++ playbooks/tasks/load_vars.yml | 7 ++ plugins/modules/vault_unseal.py | 14 ++- 9 files changed, 170 insertions(+), 26 deletions(-) create mode 100755 molecule/no_tls_multi_node/.vault_password create mode 100644 molecule/no_tls_multi_node/ansible.cfg create mode 100644 playbooks/tasks/consul_vars.yml diff --git a/molecule/no_tls_multi_node/.vault_password b/molecule/no_tls_multi_node/.vault_password new file mode 100755 index 0000000..82176d1 --- /dev/null +++ b/molecule/no_tls_multi_node/.vault_password @@ -0,0 +1,2 @@ +#! /bin/sh +vault kv get --field=password kv_hs/ansible/ansible_vault diff --git a/molecule/no_tls_multi_node/ansible.cfg b/molecule/no_tls_multi_node/ansible.cfg new file mode 100644 index 0000000..0b7508f --- /dev/null +++ b/molecule/no_tls_multi_node/ansible.cfg @@ -0,0 +1,8 @@ +[defaults] +nocows = true +ansible_python_interpreter=/usr/bin/python3 +pipelining = true +host_key_checking = false +deprecation_warnings = true +callbacks_enabled = profile_tasks, profile_roles +vault_password_file = .vault_password diff --git a/molecule/no_tls_multi_node/etc/hashistack/globals.yml b/molecule/no_tls_multi_node/etc/hashistack/globals.yml index c1e93ae..c334019 100644 --- a/molecule/no_tls_multi_node/etc/hashistack/globals.yml +++ b/molecule/no_tls_multi_node/etc/hashistack/globals.yml @@ -117,3 +117,9 @@ hashi_vault_version: "latest" # #tls_cert_file: "{{ hashi_vault_data_dir }}/tls/cert.pem" # #tls_key_file: "{{ hashi_vault_data_dir }}/tls/key.pem" # storage: "{{ vault_storage_configuration }}" + +vault_extra_configuration: + user_lockout: + all: + lockout_duration: "10m" + lockout_counter_reset: "10m" diff --git a/playbooks/bootstrap.yml b/playbooks/bootstrap.yml index c5a92b2..1c53989 100644 --- a/playbooks/bootstrap.yml +++ b/playbooks/bootstrap.yml @@ -14,6 +14,7 @@ - name: hvac version_constraint: latest state: present + when: "'vault_servers' in group_names" - name: "Include ednxzu.install_docker" ansible.builtin.include_role: diff --git a/playbooks/deploy.yml b/playbooks/deploy.yml index b239b41..a5e13d1 100644 --- a/playbooks/deploy.yml +++ b/playbooks/deploy.yml @@ -18,8 +18,23 @@ ansible.builtin.debug: msg: "{{ hashi_vault_configuration }}" + - name: "Debug" + ansible.builtin.debug: + msg: "{{ hashi_consul_configuration }}" + # - ansible.builtin.fail: + - name: "Consul" + when: + - enable_consul | bool + - "'consul_servers' in group_names" + tags: + - consul + block: + - name: "Include ednxzu.hashistack.hashicorp_consul" + ansible.builtin.include_role: + name: ednxzu.hashistack.hashicorp_consul + - name: "Vault" when: - enable_vault | bool @@ -27,7 +42,7 @@ tags: - vault block: - - name: "Include ednxzu.hashicorp_vault" + - name: "Include ednxzu.hashistack.hashicorp_consul" ansible.builtin.include_role: name: ednxzu.hashistack.hashicorp_vault @@ -61,26 +76,15 @@ ednxzu.hashistack.vault_unseal: api_url: "{{ hashi_vault_configuration['api_addr'] }}" 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 register: _vault_unseal_secret - - name: "Print unseal status" - ansible.builtin.debug: - msg: "{{ _vault_unseal_secret }}" - - name: "Unseal all vault nodes" ednxzu.hashistack.vault_unseal: api_url: "{{ hashi_vault_configuration['api_addr'] }}" key_shares: "{{ _vault_cluster_config['keys'] }}" - # max_retries: "{{ (_vault_cluster_config['keys'] | length) - 1 }}" retries: 5 delay: 5 register: _unseal_status - until: _unseal_status.changed - - - name: "Print unseal status" - ansible.builtin.debug: - msg: "{{ _unseal_status }}" diff --git a/playbooks/group_vars/all.yml b/playbooks/group_vars/all.yml index 82e431e..74cc4a1 100644 --- a/playbooks/group_vars/all.yml +++ b/playbooks/group_vars/all.yml @@ -3,8 +3,8 @@ # General options ######## ########################## -enable_vault: "yes" -enable_consul: "no" +enable_vault: "no" +enable_consul: "yes" enable_nomad: "no" deployment_method: "host" @@ -65,17 +65,103 @@ hashi_nomad_configuration: {} # Consul options ######### ########################## +consul_domain: consul +consul_datacenter: dc1 +consul_primary_datacenter: dc1 +consul_leave_on_terminate: true +consul_rejoin_after_leave: true +consul_enable_script_checks: true + +############################## +# consul address configuration +############################## + +consul_address_configuration: + # The address to which Consul will bind client interfaces, + # including the HTTP and DNS servers. + client_addr: "0.0.0.0" + # The address that should be bound to for internal cluster communications. + bind_addr: "{{ api_interface_address }}" + # The advertise address is used to change the address that we advertise to other nodes in the cluster. + advertise_addr: "{{ api_interface_address }}" + +########################## +# consul ACL configuration +########################## + +consul_acl_configuration: + enabled: false + default_policy: "allow" # can be allow or deny + enable_token_persistence: true + +##################### +# extra configuration +##################### + +consul_extra_configuration: {} + +########################## +# consul DNS configuration +########################## + +consul_dns_configuration: + allow_stale: true + enable_truncate: true + only_passing: true + hashi_consul_start_service: true hashi_consul_version: latest -hashi_consul_deploy_method: host # deployment method, either host or docker. +hashi_consul_deploy_method: "{{ deployment_method }}" hashi_consul_env_variables: {} +hashi_cosul_config_dir: "/etc/consul.d" hashi_consul_data_dir: "/opt/consul" hashi_consul_extra_files: false -hashi_consul_extra_files_src: /tmp/extra_files -hashi_consul_extra_files_dst: /etc/consul.d/extra_files +hashi_consul_extra_files_src: "{{ sub_configuration_directories.consul_servers }}/config" +hashi_consul_extra_files_dst: "{{ hashi_consul_config_dir }}/config" hashi_consul_envoy_install: false hashi_consul_envoy_version: latest -hashi_consul_configuration: {} +hashi_consul_configuration: + domain: "{{ consul_domain }}" + datacenter: "{{ consul_datacenter }}" + primary_datacenter: "{{ consul_primary_datacenter }}" + data_dir: "{{ hashi_consul_data_dir }}" + encrypt: "{{ 'mysupersecretgossipencryptionkey'|b64encode }}" + server: "{{ 'consul_servers' in group_names }}" + retry_join: "{{ + groups['consul_servers'] | + map('extract', hostvars, ['consul_address_configuration', 'bind_addr']) | + list | + to_json | + from_json + }}" + ui_config: + enabled: true + connect: + enabled: false + leave_on_terminate: true + rejoin_after_leave: true + enable_script_checks: true + enable_syslog: true + log_level: INFO + acl: "{{ consul_acl_configuration }}" + dns_config: "{{ consul_dns_configuration }}" + ports: + dns: 8600 + http: 8500 + https: -1 + grpc: 8502 + grpc_tls: 8503 + server: 8300 + serf_lan: 8301 + serf_wan: 8302 + sidecar_min_port: 21000 + sidecar_max_port: 21255 + expose_min_port: 21500 + expose_max_port: 21755 + +# this is used to circumvent jinja limitation to convert string to integer +hashi_consul_configuration_string: | + bootstrap_expect: {{ (groups['consul_servers'] | length) }} ########################## # Vault options ########## @@ -123,6 +209,7 @@ vault_extra_listener_configuration: {} ###################### # service registration ###################### + vault_enable_service_registration: false vault_service_registration_configuration: consul: diff --git a/playbooks/tasks/consul_vars.yml b/playbooks/tasks/consul_vars.yml new file mode 100644 index 0000000..6df595b --- /dev/null +++ b/playbooks/tasks/consul_vars.yml @@ -0,0 +1,31 @@ +--- +# hashistack configuration merging for consul +- name: "Consul | Merge stringified configuration" + vars: + _config_to_merge: "{{ hashi_consul_configuration_string }}" + ansible.builtin.set_fact: + hashi_consul_configuration: "{{ + hashi_consul_configuration | + combine(_config_to_merge|from_yaml) + }}" + when: hashi_consul_configuration_string is defined + +- name: "Consul | Merge addresses configuration" + vars: + _config_to_merge: "{{ consul_address_configuration }}" + ansible.builtin.set_fact: + hashi_consul_configuration: "{{ + hashi_consul_configuration | + combine(_config_to_merge) + }}" + when: consul_address_configuration is defined + +- name: "Consul | Merge extra configuration settings" + vars: + _config_to_merge: "{{ consul_extra_configuration }}" + ansible.builtin.set_fact: + hashi_consul_configuration: "{{ + hashi_consul_configuration | + combine(_config_to_merge) + }}" + when: consul_extra_configuration is defined diff --git a/playbooks/tasks/load_vars.yml b/playbooks/tasks/load_vars.yml index 04b41d0..1a91088 100644 --- a/playbooks/tasks/load_vars.yml +++ b/playbooks/tasks/load_vars.yml @@ -64,6 +64,13 @@ loop_var: item delegate_to: localhost +- name: "Merge consul configurations" + ansible.builtin.import_tasks: + file: "consul_vars.yml" + when: + - enable_consul | bool + - "'consul_servers' in group_names" + - name: "Merge vault configurations" ansible.builtin.import_tasks: file: "vault_vars.yml" diff --git a/plugins/modules/vault_unseal.py b/plugins/modules/vault_unseal.py index e5d8ed5..9a24c7e 100644 --- a/plugins/modules/vault_unseal.py +++ b/plugins/modules/vault_unseal.py @@ -85,7 +85,6 @@ def run_module(): api_url=dict(type="str", required=True), key_shares=dict(type="list", required=False, default=[]), ) - result = dict(changed=False, state="") module = AnsibleModule(argument_spec=module_args, supports_check_mode=True) @@ -98,25 +97,24 @@ def run_module(): if module.check_mode: module.exit_json(**result) - # Initialize HashiCorp Vault client 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: key_shares = module.params["key_shares"] vault_unseal_result = client.sys.submit_unseal_keys(key_shares) result["state"] = vault_unseal_result + + if client.sys.is_sealed(): + module.fail_json(msg="Vault unsealing failed.") + else: + result["changed"] = True + except hvac.exceptions.VaultError as ve: module.fail_json(msg=f"Vault unsealing failed: {ve}") - if client.sys.is_sealed(): - module.fail_json(msg="Vault unsealing failed.") - - result["changed"] = True module.exit_json(**result)