diff --git a/molecule/no_tls_multi_node/etc/hashistack/globals.yml b/molecule/no_tls_multi_node/etc/hashistack/globals.yml index ca8da97..cd7183a 100644 --- a/molecule/no_tls_multi_node/etc/hashistack/globals.yml +++ b/molecule/no_tls_multi_node/etc/hashistack/globals.yml @@ -6,7 +6,7 @@ # enable_haproxy: "yes" # enable_vault: "yes" # enable_consul: "yes" -enable_nomad: "no" +# enable_nomad: "yes" # haproxy_version: "2.8" # nomad_version: "1.7.7" diff --git a/molecule/no_tls_multi_node/molecule.yml b/molecule/no_tls_multi_node/molecule.yml index 5aa16bb..d645f94 100644 --- a/molecule/no_tls_multi_node/molecule.yml +++ b/molecule/no_tls_multi_node/molecule.yml @@ -11,7 +11,7 @@ platforms: - name: proxy01 box: generic/${MOLECULE_TEST_OS} cpus: 2 - memory: 4096 + memory: 2048 interfaces: - network_name: private_network ip: 192.168.100.91 @@ -24,7 +24,7 @@ platforms: - name: proxy02 box: generic/${MOLECULE_TEST_OS} cpus: 2 - memory: 4096 + memory: 2048 interfaces: - network_name: private_network ip: 192.168.100.92 @@ -37,7 +37,7 @@ platforms: - name: hashistack01 box: generic/${MOLECULE_TEST_OS} cpus: 4 - memory: 4096 + memory: 2048 interfaces: - network_name: private_network ip: 192.168.100.101 @@ -51,7 +51,7 @@ platforms: - name: hashistack02 box: generic/${MOLECULE_TEST_OS} cpus: 4 - memory: 4096 + memory: 2048 interfaces: - network_name: private_network ip: 192.168.100.102 @@ -65,7 +65,7 @@ platforms: - name: hashistack03 box: generic/${MOLECULE_TEST_OS} cpus: 4 - memory: 4096 + memory: 2048 interfaces: - network_name: private_network ip: 192.168.100.103 @@ -76,6 +76,19 @@ platforms: - vault_servers - consul_servers - nomad_servers + - name: hashistack04 + box: generic/${MOLECULE_TEST_OS} + cpus: 4 + memory: 2048 + interfaces: + - network_name: private_network + ip: 192.168.100.104 + auto_config: true + type: static + groups: + - common + - nomad_clients + - consul_agents provisioner: name: ansible config_options: diff --git a/playbooks/deploy.yml b/playbooks/deploy.yml index 5a1613d..6e45910 100644 --- a/playbooks/deploy.yml +++ b/playbooks/deploy.yml @@ -12,23 +12,23 @@ tags: - always - - name: "Deploy Consul Control Plane" - ansible.builtin.import_tasks: - file: tasks/consul/consul_deploy.yml - when: - - enable_consul | bool - - "'consul_servers' in group_names" + - name: "Deploy Consul" tags: - consul + when: + - enable_consul | bool + block: + - name: "Deploy Consul Control Plane" + ansible.builtin.import_tasks: + file: tasks/consul/consul_deploy.yml + when: + - "'consul_servers' in group_names" - - name: "Deploy Consul Agents" - ansible.builtin.include_role: - name: ednz_cloud.hashicorp_consul - when: - - enable_consul | bool - - "'consul_agents' in group_names" - tags: - - consul + - name: "Deploy Consul Agents" + ansible.builtin.include_role: + name: ednz_cloud.hashicorp_consul + when: + - "'consul_agents' in group_names" - name: "Deploy Haproxy & Keepalived" ansible.builtin.import_tasks: @@ -47,3 +47,22 @@ - "'vault_servers' in group_names" tags: - vault + + - name: "Deploy Nomad" + tags: + - nomad + when: + - enable_nomad | bool + block: + - name: "Deploy Nomad Control Plane" + ansible.builtin.import_tasks: + file: tasks/nomad/nomad_deploy.yml + when: + - "('nomad_servers' in group_names)" + + - name: "Deploy Nomad Clients" + ansible.builtin.include_role: + name: ednz_cloud.hashicorp_nomad + when: + - "('nomad_clients' in group_names)" + - "('nomad_servers' not in group_names)" diff --git a/playbooks/generate_certs.yml b/playbooks/generate_certs.yml index ede97b2..a1d861e 100644 --- a/playbooks/generate_certs.yml +++ b/playbooks/generate_certs.yml @@ -6,6 +6,12 @@ gather_facts: true become: true tasks: + - name: "Import variables" + ansible.builtin.import_tasks: + file: tasks/load_vars.yml + tags: + - always + - name: "Create temporary cert directory in {{ sub_configuration_directories['certificates'] }}" # noqa: run-once[task] ansible.builtin.file: path: "{{ sub_configuration_directories['certificates'] }}/external" @@ -271,5 +277,3 @@ owner: "{{ lookup('env', 'USER') }}" group: "{{ lookup('env', 'USER') }}" mode: "0644" - - # - fail: diff --git a/playbooks/generate_credentials.yml b/playbooks/generate_credentials.yml index df36826..98ea991 100644 --- a/playbooks/generate_credentials.yml +++ b/playbooks/generate_credentials.yml @@ -18,14 +18,24 @@ - name: "Generate consul agents credentials" ansible.builtin.set_fact: - _cosul_agents_accessor: "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_letters','digits']) | to_uuid }}" + _consul_agents_accessor: "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_letters','digits']) | to_uuid }}" _consul_agents_token: "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_letters','digits']) | to_uuid }}" - name: "Generate consul vault credentials" ansible.builtin.set_fact: - _cosul_vault_accessor: "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_letters','digits']) | to_uuid }}" + _consul_vault_accessor: "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_letters','digits']) | to_uuid }}" _consul_vault_token: "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_letters','digits']) | to_uuid }}" + - name: "Generate consul nomad server credentials" + ansible.builtin.set_fact: + _consul_nomad_server_accessor: "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_letters','digits']) | to_uuid }}" + _consul_nomad_server_token: "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_letters','digits']) | to_uuid }}" + + - name: "Generate consul nomad client credentials" + ansible.builtin.set_fact: + _consul_nomad_client_accessor: "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_letters','digits']) | to_uuid }}" + _consul_nomad_client_token: "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_letters','digits']) | to_uuid }}" + - name: "Generate nomad credentials" block: - name: "Generate nomad gossip encryption key" @@ -43,5 +53,3 @@ owner: "{{ lookup('env', 'USER') }}" group: "{{ lookup('env', 'USER') }}" mode: '0644' - - # - fail: diff --git a/playbooks/group_vars/all/consul.yml b/playbooks/group_vars/all/consul.yml index 4768cac..b633496 100644 --- a/playbooks/group_vars/all/consul.yml +++ b/playbooks/group_vars/all/consul.yml @@ -75,8 +75,7 @@ hashicorp_consul_data_dir: "/opt/consul" hashicorp_consul_extra_files: true hashicorp_consul_extra_files_list: "{{ ([] + (consul_certificates_extra_files_dir if consul_enable_tls else []) + - (vault_plugin_extra_files_dir if vault_enable_plugins else []) + - vault_extra_files_list) + consul_extra_files_list) | unique | sort }}" @@ -102,13 +101,10 @@ hashicorp_consul_configuration: rejoin_after_leave: "{{ consul_rejoin_after_leave }}" enable_script_checks: "{{ consul_enable_script_checks }}" enable_syslog: true - log_level: INFO acl: "{{ consul_acl_configuration }}" dns_config: "{{ consul_dns_configuration }}" ports: dns: 8600 - grpc: 8502 - grpc_tls: 8503 server: 8300 serf_lan: 8301 serf_wan: 8302 @@ -119,7 +115,11 @@ hashicorp_consul_configuration: # this is used to circumvent jinja limitation to convert string to integer hashicorp_consul_configuration_string: | - bootstrap_expect: {{ (groups['consul_servers'] | length) }} ports: http: {{ (consul_api_port.http|int) if not consul_enable_tls else ('-1' | int) }} https: {{ (consul_api_port.https|int) if consul_enable_tls else ('-1' | int) }} + grpc: {{ ('8502'|int) if not consul_enable_tls else ('-1' | int) }} + grpc_tls: {{ ('8503'|int) if consul_enable_tls else ('-1' | int) }} + +hashicorp_consul_servers_configuration_string: | + bootstrap_expect: {{ (groups['consul_servers'] | length) }} diff --git a/playbooks/group_vars/all/globals.yml b/playbooks/group_vars/all/globals.yml index 7331c23..ca86a27 100644 --- a/playbooks/group_vars/all/globals.yml +++ b/playbooks/group_vars/all/globals.yml @@ -9,7 +9,7 @@ enable_consul: "yes" enable_nomad: "yes" haproxy_version: "2.8" -nomad_version: "1.7.7" +nomad_version: "1.8.1" consul_version: "1.18.1" vault_version: "1.16.2" diff --git a/playbooks/group_vars/all/haproxy.yml b/playbooks/group_vars/all/haproxy.yml index e41f632..a5ffef2 100644 --- a/playbooks/group_vars/all/haproxy.yml +++ b/playbooks/group_vars/all/haproxy.yml @@ -49,11 +49,11 @@ deploy_haproxy_frontends: {%- set haproxy_options = haproxy_options + tls_options -%} {%- endif -%} - {%- set haproxy_options = haproxy_options + consul_haproxy_frontend_options + vault_haproxy_frontend_options -%} + {%- set haproxy_options = haproxy_options + consul_haproxy_frontend_options + vault_haproxy_frontend_options + nomad_haproxy_frontend_options -%} {{ haproxy_options }} -deploy_haproxy_backends: "{{ consul_haproxy_backends + vault_haproxy_backends }}" +deploy_haproxy_backends: "{{ consul_haproxy_backends + vault_haproxy_backends + nomad_haproxy_backends }}" deploy_haproxy_listen: - name: monitoring diff --git a/playbooks/group_vars/all/nomad.yml b/playbooks/group_vars/all/nomad.yml index 561de18..4c76b03 100644 --- a/playbooks/group_vars/all/nomad.yml +++ b/playbooks/group_vars/all/nomad.yml @@ -7,6 +7,61 @@ nomad_datacenter: dc1 +#################### +# nomad api config # +#################### + +nomad_api_addr: "{{ nomad_api_scheme }}://{{ api_interface_address }}:{{ nomad_api_port[nomad_api_scheme] }}" +nomad_api_scheme: "{{ 'https' if nomad_enable_tls else 'http' }}" +nomad_api_port: + http: "{{ nomad_address_configuration.ports.http }}" + https: "{{ nomad_address_configuration.ports.http }}" + +######################### +# nomad haproxy backend # +######################### + +nomad_haproxy_frontend_options: + - acl is_nomad hdr(host) -i {{ nomad_fqdn }} + - use_backend nomad_external if is_nomad + +nomad_haproxy_backends: + - name: nomad_external + options: "{{ nomad_external_backend_options + nomad_external_backend_servers }}" + +nomad_external_backend_options: + - description nomad external http backend + - option forwardfor + - option httpchk + - http-check send meth GET uri / + - default-server inter 2s fastinter 1s downinter 1s + +nomad_external_backend_servers: | + [ + {% for host in groups['nomad_servers'] %} + 'server nomad-{{ hostvars[host].api_interface_address }} {{ hostvars[host].api_interface_address }}:{{ hostvars[host].nomad_api_port[nomad_api_scheme] }} check {{ 'ssl verify none ' if nomad_enable_tls }}inter 5s'{% if not loop.last %},{% endif %} + {% endfor %} + ] + +############################### +# nomad address configuration # +############################### + +nomad_address_configuration: + bind_addr: "{{ api_interface_address }}" + addresses: + http: "{{ api_interface_address }}" + rpc: "{{ api_interface_address }}" + serf: "{{ api_interface_address }}" + advertise: + http: "{{ api_interface_address }}" + rpc: "{{ api_interface_address }}" + serf: "{{ api_interface_address }}" + ports: + http: 4646 + rpc: 4647 + serf: 4648 + ########################### # nomad ACL configuration # ########################### @@ -28,7 +83,49 @@ nomad_autopilot_configuration: {} ############################ nomad_enable_consul_integration: "{{ enable_consul | bool }}" -nomad_consul_integration_configuration: {} +nomad_consul_integration_configuration: + address: "127.0.0.1:{{ hashicorp_consul_configuration.ports.https if consul_enable_tls else hashicorp_consul_configuration.ports.http }}" + auto_advertise: true + ssl: "{{ consul_enable_tls | bool }}" + token: "{{ _credentials.consul.tokens.nomad.server.secret_id if nomad_enable_server else _credentials.consul.tokens.nomad.client.secret_id}}" + tags: [] + +nomad_consul_integration_tls_configuration: + ca_file: "/etc/ssl/certs/ca-certificates.crt" + +nomad_consul_integration_server_configuration: + server_auto_join: true + +nomad_consul_integration_client_configuration: + client_auto_join: true + grpc_address: "127.0.0.1:{{ hashicorp_consul_configuration.ports.grpc_tls if consul_enable_tls else hashicorp_consul_configuration.ports.grpc }}" + +nomad_consul_integration_client_tls_configuration: + grpc_ca_file: "/etc/ssl/certs/ca-certificates.crt" + +nomad_consul_integration_server_policy: | + agent_prefix "" { + policy = "read" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } + acl = "write" + mesh = "write" + +nomad_consul_integration_client_policy: | + agent_prefix "" { + policy = "read" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } ############################ # nomad vault integration # @@ -43,15 +140,23 @@ nomad_vault_integration_configuration: {} # node will leave the cluster if the process is stopped # and if it is only a client -nomad_leave_on_interrupt: "{{ (('nomad_clients' in group_names) and (not 'nomad_servers' in group_names)) | bool }}" -nomad_leave_on_terminate: "{{ (('nomad_clients' in group_names) and (not 'nomad_servers' in group_names)) | bool }}" +nomad_leave_on_interrupt: "{{ (('nomad_clients' in group_names) and not ('nomad_servers' in group_names)) | bool }}" +nomad_leave_on_terminate: "{{ (('nomad_clients' in group_names) and not ('nomad_servers' in group_names)) | bool }}" + +########################## +# nomad ui configuration # +########################## + +nomad_ui_configuration: + enabled: "{{ ('nomad_servers' in group_names) | bool }}" ############################## # nomad server configuration # ############################## +nomad_enable_server: "{{ ('nomad_servers' in group_names) | bool }}" nomad_server_configuration: - enabled: "{{ 'nomad_servers' in group_names }}" + enabled: "{{ nomad_enable_server }}" data_dir: "{{ hashicorp_nomad_data_dir }}/server" encrypt: "{{ _credentials.nomad.gossip_encryption_key }}" @@ -59,29 +164,74 @@ nomad_server_configuration: # nomad client configuration # ############################## +nomad_enable_client: "{{ ('nomad_clients' in group_names) | bool }}" nomad_client_configuration: - enabled: "{{ 'nomad_clients' in group_names | bool }}" + enabled: "{{ nomad_enable_client }}" state_dir: "{{ hashicorp_nomad_data_dir }}/client" + bridge_network_name: nomad + bridge_network_subnet: "172.26.64.0/20" + +############################### +# nomad drivers configuration # +############################### + +###################### +# nomad internal tls # +###################### + +nomad_enable_tls: false +nomad_tls_configuration: + http: true + rpc: true + ca_file: "/etc/ssl/certs/ca-certificates.crt" + cert_file: "{{ nomad_certificates_directory }}/cert.pem" + key_file: "{{ nomad_certificates_directory }}/key.pem" + verify_server_hostname: true + +nomad_certificates_directory: "{{ hashicorp_nomad_config_dir }}/tls" +nomad_certificates_extra_files_dir: + - src: "{{ sub_configuration_directories['certificates'] }}/nomad/{{ inventory_hostname }}" + dest: "{{ nomad_certificates_directory }}" + +####################### +# extra configuration # +####################### + +nomad_extra_configuration: {} +nomad_extra_files_list: [] + +######################## +# nomad role variables # +######################## hashicorp_nomad_start_service: true hashicorp_nomad_service_name: "nomad" hashicorp_nomad_cni_plugins_install: true hashicorp_nomad_cni_plugins_version: latest hashicorp_nomad_cni_plugins_install_path: /opt/cni/bin -hashicorp_nomad_version: latest -hashicorp_nomad_deploy_method: host # deployment method, either host or docker +hashicorp_nomad_version: "{{ nomad_version }}" hashicorp_nomad_env_variables: {} hashicorp_nomad_config_dir: "/etc/nomad.d" hashicorp_nomad_data_dir: /opt/nomad hashicorp_nomad_extra_files: false -hashicorp_nomad_extra_files_src: /tmp/extra_files -hashicorp_nomad_extra_files_dst: /etc/nomad.d/extra_files +hashicorp_nomad_extra_files_list: "{{ ([] + + (nomad_certificates_extra_files_dir if nomad_enable_tls else []) + + nomad_extra_files_list) + | unique + | sort + }}" hashicorp_nomad_configuration: datacenter: "{{ nomad_datacenter }}" bind_addr: "0.0.0.0" data_dir: "{{ hashicorp_nomad_data_dir }}" - leave_on_interrupt: "{{ (('nomad_clients' in group_names) and (not 'nomad_servers' in group_names)) | bool }}" - leave_on_terminate: "{{ (('nomad_clients' in group_names) and (not 'nomad_servers' in group_names)) | bool }}" + leave_on_interrupt: "{{ nomad_leave_on_interrupt }}" + leave_on_terminate: "{{ nomad_leave_on_terminate }}" acl: "{{ nomad_acl_configuration }}" server: "{{ nomad_server_configuration }}" client: "{{ nomad_client_configuration }}" + ui: "{{ nomad_ui_configuration }}" + +# this is used to circumvent jinja limitation to convert string to integer +hashicorp_nomad_configuration_string: | + server: + bootstrap_expect: {{ (groups['nomad_servers'] | length) }} diff --git a/playbooks/inventory/multinode.ini b/playbooks/inventory/multinode.ini index 08734c2..373198d 100644 --- a/playbooks/inventory/multinode.ini +++ b/playbooks/inventory/multinode.ini @@ -12,15 +12,22 @@ consul01 consul02 consul03 -[consul_agents:children] -haproxy_servers -vault_servers - [nomad_servers] nomad01 nomad02 nomad03 +[nomad_clients] +nomad-client01 +nomad-client02 +nomad-client03 + +[consul_agents:children] +haproxy_servers +vault_servers +nomad_servers +nomad_clients + [deployment] localhost ansible_connection=local diff --git a/playbooks/preflight.yml b/playbooks/preflight.yml index e4216c2..b49cd73 100644 --- a/playbooks/preflight.yml +++ b/playbooks/preflight.yml @@ -211,10 +211,6 @@ msg: "Please check the api_interface settings - interface {{ api_interface }} is not active" when: not hostvars[inventory_hostname].ansible_facts[api_interface]['active'] - - name: "Debug" - ansible.builtin.debug: - msg: "{{ api_interface_address }}" - - name: "Checking the api_interface ip address configuration" ansible.builtin.fail: msg: "Please check the api_interface settings - interface {{ api_interface }} ip address problem" diff --git a/playbooks/tasks/consul/consul_deploy.yml b/playbooks/tasks/consul/consul_deploy.yml index 7c54178..561d707 100644 --- a/playbooks/tasks/consul/consul_deploy.yml +++ b/playbooks/tasks/consul/consul_deploy.yml @@ -31,7 +31,7 @@ - name: "Create consul agents token" when: - - _consul_init_secret.changed + # - _consul_init_secret.changed - consul_acl_configuration.enabled block: - name: "Create consul agents token" # noqa: run-once[task] no-handler diff --git a/playbooks/tasks/consul/consul_vars.yml b/playbooks/tasks/consul/consul_vars.yml index 5e2fdf0..fb593df 100644 --- a/playbooks/tasks/consul/consul_vars.yml +++ b/playbooks/tasks/consul/consul_vars.yml @@ -10,6 +10,17 @@ }}" when: - hashicorp_consul_configuration_string is defined + +- name: "Consul | Merge servers specific stringified configuration" + vars: + _config_to_merge: "{{ hashicorp_consul_servers_configuration_string }}" + ansible.builtin.set_fact: + hashicorp_consul_configuration: "{{ + hashicorp_consul_configuration | + combine(_config_to_merge|from_yaml, recursive=true) + }}" + when: + - hashicorp_consul_configuration_string is defined - "'consul_servers' in group_names" - name: "Consul | Merge addresses configuration" diff --git a/playbooks/tasks/haproxy/haproxy_deploy.yml b/playbooks/tasks/haproxy/haproxy_deploy.yml index 656aa5c..862012f 100644 --- a/playbooks/tasks/haproxy/haproxy_deploy.yml +++ b/playbooks/tasks/haproxy/haproxy_deploy.yml @@ -15,6 +15,10 @@ - name: "Register haproxy services in consul" community.general.consul: token: "{{ _credentials.consul.root_token.secret_id }}" + host: "127.0.0.1" + scheme: "{{ consul_api_scheme }}" + port: "{{ consul_api_port[consul_api_scheme] }}" + validate_certs: false service_name: haproxy service_port: 80 interval: 20s diff --git a/playbooks/tasks/load_vars.yml b/playbooks/tasks/load_vars.yml index 77a2527..ec8785f 100644 --- a/playbooks/tasks/load_vars.yml +++ b/playbooks/tasks/load_vars.yml @@ -45,3 +45,10 @@ when: - enable_vault | bool - "'vault_servers' in group_names" + +- name: "Merge nomad configurations" + ansible.builtin.import_tasks: + file: "nomad/nomad_vars.yml" + when: + - enable_nomad | bool + - "('nomad_servers' in group_names) or ('nomad_clients' in group_names)" diff --git a/playbooks/tasks/misc/load_all_vars.yml b/playbooks/tasks/misc/load_all_vars.yml index 9142d0d..2e5eea5 100644 --- a/playbooks/tasks/misc/load_all_vars.yml +++ b/playbooks/tasks/misc/load_all_vars.yml @@ -142,9 +142,6 @@ delegate_to: localhost when: _hashistack_ca_directory.stat.exists and _hashistack_ca_directory.stat.isdir - - ansible.builtin.debug: - msg: "{{ _hashistack_cacert_files }}" - - name: "Ensure remote ca directory exists" ansible.builtin.file: path: "{{ hashistack_remote_config_dir }}/ca" @@ -207,10 +204,3 @@ when: - enable_vault | bool - "'vault_servers' in group_names" - -- debug: - msg: "{{ deploy_haproxy_frontends }}" - -- debug: - msg: "{{ deploy_haproxy_backends }}" -# - fail: diff --git a/playbooks/tasks/misc/load_ca_certificates.yml b/playbooks/tasks/misc/load_ca_certificates.yml index 5999284..732c070 100644 --- a/playbooks/tasks/misc/load_ca_certificates.yml +++ b/playbooks/tasks/misc/load_ca_certificates.yml @@ -13,9 +13,6 @@ delegate_to: localhost when: _hashistack_ca_directory.stat.exists and _hashistack_ca_directory.stat.isdir -- ansible.builtin.debug: - msg: "{{ _hashistack_cacert_files }}" - - name: "Ensure remote ca directory exists" ansible.builtin.file: path: "{{ hashistack_remote_config_dir }}/ca" diff --git a/playbooks/tasks/misc/load_credentials_vars.yml b/playbooks/tasks/misc/load_credentials_vars.yml index d448f78..7c8980d 100644 --- a/playbooks/tasks/misc/load_credentials_vars.yml +++ b/playbooks/tasks/misc/load_credentials_vars.yml @@ -44,7 +44,3 @@ _credentials: "{{ _credentials | combine(_config_to_merge, recursive=true) }}" when: _vault_credentials_file.stat.exists delegate_to: localhost - -- name: "Debug _credentials" - ansible.builtin.debug: - msg: "{{ _credentials }}" diff --git a/playbooks/tasks/nomad/nomad_deploy.yml b/playbooks/tasks/nomad/nomad_deploy.yml new file mode 100644 index 0000000..1a64349 --- /dev/null +++ b/playbooks/tasks/nomad/nomad_deploy.yml @@ -0,0 +1,82 @@ +--- +- name: "Nomad" + block: + - name: "Create consul tokens for service registration" + when: + - enable_consul + - nomad_enable_consul_integration + delegate_to: "{{ groups['consul_servers'] | first }}" + vars: + _consul_host: "{{ hostvars[groups['consul_servers'][0]].api_interface_address }}" + _consul_port: "{{ hostvars[groups['consul_servers'][0]].consul_api_port[hostvars[groups['consul_servers'][0]].consul_api_scheme] }}" + _consul_scheme: "{{ hostvars[groups['consul_servers'][0]].consul_api_scheme }}" + run_once: true + block: + - name: "Create server credentials" + block: + - name: "Create consul server policy" + community.general.consul_policy: + token: "{{ _credentials.consul.root_token.secret_id }}" + host: "{{ _consul_host }}" + port: "{{ _consul_port }}" + scheme: "{{ _consul_scheme }}" + validate_certs: false + state: present + name: nomad-server-policy + rules: "{{ nomad_consul_integration_server_policy }}" + register: _consul_nomad_server_policy + + - name: "Create consul server token" + community.general.consul_token: + token: "{{ _credentials.consul.root_token.secret_id }}" + host: "{{ _consul_host }}" + port: "{{ _consul_port }}" + scheme: "{{ _consul_scheme }}" + validate_certs: false + accessor_id: "{{ _credentials.consul.tokens.nomad.server.accessor_id }}" + secret_id: "{{ _credentials.consul.tokens.nomad.server.secret_id }}" + policies: + - id: "{{ _consul_nomad_server_policy.policy.ID }}" + state: present + when: _consul_nomad_server_policy.changed + + - name: "Create client credentials" + block: + - name: "Create consul client policy" + community.general.consul_policy: + token: "{{ _credentials.consul.root_token.secret_id }}" + host: "{{ _consul_host }}" + port: "{{ _consul_port }}" + scheme: "{{ _consul_scheme }}" + validate_certs: false + state: present + name: nomad-client-policy + rules: "{{ nomad_consul_integration_client_policy }}" + register: _consul_nomad_client_policy + + - name: "Create consul client token" + community.general.consul_token: + token: "{{ _credentials.consul.root_token.secret_id }}" + host: "{{ _consul_host }}" + port: "{{ _consul_port }}" + scheme: "{{ _consul_scheme }}" + validate_certs: false + accessor_id: "{{ _credentials.consul.tokens.nomad.client.accessor_id }}" + secret_id: "{{ _credentials.consul.tokens.nomad.client.secret_id }}" + policies: + - id: "{{ _consul_nomad_client_policy.policy.ID }}" + state: present + when: _consul_nomad_client_policy.changed + + - name: "Include ednz_cloud.hashicorp_nomad" + ansible.builtin.include_role: + name: ednz_cloud.hashicorp_nomad + + - name: "Initialize nomad cluster" # noqa: run-once[task] + ednz_cloud.hashistack.nomad_acl_bootstrap: + bootstrap_secret: "{{ _credentials.nomad.root_token.secret_id }}" + api_url: "{{ nomad_api_addr }}" + run_once: true + delegate_to: "{{ groups['nomad_servers'] | first }}" + register: _nomad_init_secret + when: hashicorp_nomad_configuration.acl.enabled diff --git a/playbooks/tasks/nomad/nomad_vars.yml b/playbooks/tasks/nomad/nomad_vars.yml new file mode 100644 index 0000000..10a1ce5 --- /dev/null +++ b/playbooks/tasks/nomad/nomad_vars.yml @@ -0,0 +1,115 @@ +--- +# hashistack configuration merging for nomad +- name: "Nomad | Merge stringified configuration" + vars: + _config_to_merge: "{{ hashicorp_nomad_configuration_string }}" + ansible.builtin.set_fact: + hashicorp_nomad_configuration: "{{ + hashicorp_nomad_configuration | + combine(_config_to_merge|from_yaml, recursive=true) + }}" + when: + - hashicorp_nomad_configuration_string is defined + - "'nomad_servers' in group_names" + +- name: "Nomad | Merge addresses configuration" + vars: + _config_to_merge: "{{ nomad_address_configuration }}" + ansible.builtin.set_fact: + hashicorp_nomad_configuration: "{{ + hashicorp_nomad_configuration | + combine(_config_to_merge, recursive=true) + }}" + when: nomad_address_configuration is defined + +- name: "Nomad | Merge consul integration configuration" + when: + - enable_consul | bool + - nomad_enable_consul_integration | bool + block: + - name: "Nomad | Merge consul tls configuration" + when: + - nomad_consul_integration_configuration.ssl is defined + - nomad_consul_integration_configuration.ssl | bool + block: + - name: "Nomad | Merge consul default client configuration" + vars: + _config_to_merge: "{{ nomad_consul_integration_tls_configuration }}" + ansible.builtin.set_fact: + nomad_consul_integration_configuration: "{{ + nomad_consul_integration_configuration | + combine(_config_to_merge, recursive=true) + }}" + + - name: "Nomad | Merge consul configuration for nomad servers" + when: + - nomad_enable_server + block: + - name: "Nomad | Merge consul default server configuration" + vars: + _config_to_merge: "{{ nomad_consul_integration_server_configuration }}" + ansible.builtin.set_fact: + nomad_consul_integration_configuration: "{{ + nomad_consul_integration_configuration | + combine(_config_to_merge, recursive=true) + }}" + + - name: "Nomad | Merge consul configuration for nomad clients" + when: + - nomad_enable_client + block: + - name: "Nomad | Merge consul default client configuration" + vars: + _config_to_merge: "{{ nomad_consul_integration_client_configuration }}" + ansible.builtin.set_fact: + nomad_consul_integration_configuration: "{{ + nomad_consul_integration_configuration | + combine(_config_to_merge, recursive=true) + }}" + + - name: "Nomad | Merge consul tls client configuration" + vars: + _config_to_merge: "{{ nomad_consul_integration_client_tls_configuration }}" + ansible.builtin.set_fact: + nomad_consul_integration_configuration: "{{ + nomad_consul_integration_configuration | + combine(_config_to_merge, recursive=true) + }}" + when: + - nomad_consul_integration_configuration.ssl is defined + - nomad_consul_integration_configuration.ssl | bool + + - name: "Nomad | Merge consul block into main configuration" + vars: + _config_to_merge: + consul: "{{ nomad_consul_integration_configuration }}" + ansible.builtin.set_fact: + hashicorp_nomad_configuration: "{{ + hashicorp_nomad_configuration | + combine(_config_to_merge, recursive=true) + }}" + +- name: "Nomad | Merge TLS configuration" + vars: + _config_to_merge: + tls: "{{ nomad_tls_configuration }}" + ansible.builtin.set_fact: + hashicorp_nomad_configuration: "{{ + hashicorp_nomad_configuration | + combine(_config_to_merge, recursive=true) + }}" + when: nomad_enable_tls + +- name: "Nomad | Merge extra configuration settings" + vars: + _config_to_merge: "{{ nomad_extra_configuration }}" + ansible.builtin.set_fact: + hashicorp_nomad_configuration: "{{ + hashicorp_nomad_configuration | + combine(_config_to_merge, recursive=true) + }}" + when: nomad_extra_configuration is defined + +- name: "Print nomad configuration" + ansible.builtin.debug: + msg: "{{ hashicorp_nomad_configuration }}" diff --git a/playbooks/templates/credentials.yml.j2 b/playbooks/templates/credentials.yml.j2 index 7c759d5..47dfb36 100644 --- a/playbooks/templates/credentials.yml.j2 +++ b/playbooks/templates/credentials.yml.j2 @@ -10,6 +10,13 @@ consul: vault: accessor_id: "{{ _consul_vault_accessor }}" secret_id: "{{ _consul_vault_token }}" + nomad: + server: + accessor_id: "{{ _consul_nomad_server_accessor }}" + secret_id: "{{ _consul_nomad_server_token }}" + client: + accessor_id: "{{ _consul_nomad_client_accessor }}" + secret_id: "{{ _consul_nomad_client_token }}" nomad: gossip_encryption_key: "{{ _nomad_gossip_encryption_key }}" root_token: diff --git a/plugins/modules/nomad_acl_bootstrap.py b/plugins/modules/nomad_acl_bootstrap.py new file mode 100644 index 0000000..c235719 --- /dev/null +++ b/plugins/modules/nomad_acl_bootstrap.py @@ -0,0 +1,146 @@ +#!/usr/bin/python + +from __future__ import absolute_import, division, print_function +from typing import Tuple + +__metaclass__ = type + +DOCUMENTATION = r""" +--- +module: ednz_cloud.hashistack.nomad_acl_bootstrap + +short_description: Manages the ACL bootstrap of HashiCorp Nomad. + +description: + - This module bootstraps HashiCorp Nomad ACL, ensuring that it is securely set up for use. + +requirements: + - C(requests) (L(Python library,https://requests.readthedocs.io/en/latest/)) + +options: + api_url: + description: The URL of the HashiCorp Nomad API. + required: true + type: str + bootstrap_secret: + description: + - The secret to use for the bootstrap operation. + required: false + type: str + tls_verify: + description: + - Whether to verify the TLS certificate of the Nomad API URL. + - Default is true. + required: false + type: bool + default: true + +author: + - Bertrand Lanson (@ednz_cloud) +""" + +EXAMPLES = r""" +# Example: Bootstrap HashiCorp Nomad ACL with default settings +- name: Bootstrap HashiCorp Nomad ACL + ednz_cloud.hashistack.nomad_acl_bootstrap: + api_url: https://nomad.example.com + +# Example: Bootstrap HashiCorp Nomad ACL with a custom bootstrap secret +- name: Bootstrap HashiCorp Nomad ACL with custom settings + ednz_cloud.hashistack.nomad_acl_bootstrap: + api_url: https://nomad.example.com + bootstrap_secret: 2b778dd9-f5f1-6f29-b4b4-9a5fa948757a +""" + +RETURN = r""" +state: + description: + - Information about the state of HashiCorp Nomad after ACL bootstrap. + - This is a complex dictionary with details of the bootstrap. + type: dict + returned: always + sample: + - AccessorID: "b780e702-98ce-521f-2e5f-c6b87de05b24", + - SecretID: "3f4a0fcd-7c42-773c-25db-2d31ba0c05fe", + - Name: "Bootstrap Token", + - Type: "management", + - Policies: null, + - Global: true, + - CreateTime: "2017-08-23T22:47:14.695408057Z", + - CreateIndex: 7, + - ModifyIndex: 7 +""" + +from ansible.module_utils.basic import AnsibleModule +import traceback + +try: + import requests +except ImportError: + HAS_REQUESTS = False + REQUESTS_IMPORT_ERROR = traceback.format_exc() +else: + REQUESTS_IMPORT_ERROR = None + HAS_REQUESTS = True + + +def bootstrap_nomad_acl( + api_url: str, tls_verify: bool, bootstrap_secret: str +) -> Tuple[bool, dict]: + payload = {} + if bootstrap_secret: + payload["BootstrapSecret"] = bootstrap_secret + + try: + response = requests.put( + f"{api_url}/v1/acl/bootstrap", json=payload, verify=tls_verify + ) + response.raise_for_status() + return True, response.json() + except requests.exceptions.RequestException as e: + if ( + response.status_code == 400 + and "ACL bootstrap already done" in response.text + ): + return False, {"message": "Nomad ACL system is already bootstrapped"} + raise ValueError(f"Nomad ACL bootstrap failed: {str(e)}") + + +def run_module(): + module_args = dict( + api_url=dict(type="str", required=True), + bootstrap_secret=dict(type="str", required=False, no_log=True), + tls_verify=dict(type="bool", required=False, default=True), + ) + + result = dict(changed=False, state="") + + module = AnsibleModule(argument_spec=module_args, supports_check_mode=False) + + if not HAS_REQUESTS: + module.fail_json( + msg="Missing required library: requests", exception=REQUESTS_IMPORT_ERROR + ) + + try: + changed, response_data = bootstrap_nomad_acl( + api_url=module.params["api_url"], + tls_verify=module.params["tls_verify"], + bootstrap_secret=module.params.get("bootstrap_secret"), + ) + + result["changed"] = changed + result["state"] = response_data + + module.exit_json(**result) + + except ValueError as e: + module.fail_json(msg=str(e)) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/vault_init.py b/plugins/modules/vault_init.py index 72d0258..2434647 100644 --- a/plugins/modules/vault_init.py +++ b/plugins/modules/vault_init.py @@ -44,12 +44,12 @@ author: EXAMPLES = r""" # Example: Initialize HashiCorp Vault with default settings - name: Initialize HashiCorp Vault - my_namespace.my_collection.my_test: + ednz_cloud.hashistack.vault_init: api_url: https://vault.example.com # Example: Initialize HashiCorp Vault with custom key shares and threshold - name: Initialize HashiCorp Vault with custom settings - my_namespace.my_collection.my_test: + ednz_cloud.hashistack.vault_init: api_url: https://vault.example.com key_shares: 7 key_threshold: 4