diff --git a/seb4itik/byow/roles/consul/defaults/main.yml b/seb4itik/byow/roles/consul/defaults/main.yml index 83bd2c0..8592958 100644 --- a/seb4itik/byow/roles/consul/defaults/main.yml +++ b/seb4itik/byow/roles/consul/defaults/main.yml @@ -1,22 +1,34 @@ -# Required -consul_datacenter: null -consul_domain: null -consul_secret_key: null +# Required. consul_server: false +consul_datacenter: null +consul_secret_key: null # Generate with "consul keygen" consul_servers: [] -# Required for registering services -consul_services: [] +# Required if consul_server. +consul_bootstrap_secret: null # Generate with "uuidgen" -# Defaults -consul_version: "1.22.2" -consul_key_file: "/etc/ssl/private/server.{{ consul_datacenter }}.{{ consul_domain }}.key" -consul_cert_file: "/etc/ssl/certs/server.{{ consul_datacenter }}.{{ consul_domain }}.crt" -consul_ca_file: "/etc/ssl/certs/local-ca.crt" -consul_client_addr: "0.0.0.0" +# Required for activating UI. +consul_ui_secret_id: null # Generate with "uuidgen" + +# Defaults. +consul_domain: "consul" +consul_version: "1.22.3" +consul_client_addr: "127.0.0.1" consul_bind_addr: "{{ ansible_default_ipv4.address }}" -consul_advertise_addr: "{{ ansible_default_ipv4.address }}" consul_default_check_interval: "15s" consul_default_check_timeout: "2s" -consul_acl_bootstraped: false +# Attributes for certificate must be: +# cn: server.. +# SANs: DNS:server.. +consul_key_file: "/etc/ssl/private/server.{{ consul_datacenter }}.{{ consul_domain }}.key" +consul_cert_file: "/etc/ssl/certs/server.{{ consul_datacenter }}.{{ consul_domain }}.crt" +consul_ca_file: "/etc/ssl/certs/local-ca.crt" + +# Required for registering services. +consul_services: [] +# - name: "www" +# port: 443 +# check_tcp: "" +# check_http: "http://{{ ansible_default_ipv4.address }}:80/" +# scheme: "" (default "http") diff --git a/seb4itik/byow/roles/consul/handlers/main.yml b/seb4itik/byow/roles/consul/handlers/main.yml index b95688c..3a65131 100644 --- a/seb4itik/byow/roles/consul/handlers/main.yml +++ b/seb4itik/byow/roles/consul/handlers/main.yml @@ -1,5 +1,11 @@ -- name: Restart Consul +- name: Restart Consul - Restart service + listen: "Restart Consul" ansible.builtin.service: name: "consul" state: "restarted" daemon_reload: true + +- name: Restart Consul - Wait a little + listen: "Restart Consul" + ansible.builtin.wait_for: + timeout: 5 diff --git a/seb4itik/byow/roles/consul/tasks/main.yml b/seb4itik/byow/roles/consul/tasks/main.yml index f875612..17d5ff4 100644 --- a/seb4itik/byow/roles/consul/tasks/main.yml +++ b/seb4itik/byow/roles/consul/tasks/main.yml @@ -3,13 +3,6 @@ name: - "unzip" - # Not required for Ubuntu 24, was a test for Debian 13 -- name: Disable IPv6 on all interfaces - ansible.posix.sysctl: - name: "net.ipv6.conf.all.disable_ipv6" - value: "1" - sysctl_set: true - - name: Create Consul directory ansible.builtin.file: path: "/usr/local/consul-{{ consul_version }}" @@ -25,20 +18,12 @@ notify: - "Restart Consul" -- name: Link Consul - ansible.builtin.file: - src: "/usr/local/consul-{{ consul_version }}/consul" - dest: "/usr/local/bin/consul" - state: "link" - notify: - - "Restart Consul" - -- name: Create consul system group +- name: Create Consul system group ansible.builtin.group: name: "consul" system: true -- name: Create consul system user +- name: Create Consul system user ansible.builtin.user: name: "consul" group: "consul" @@ -47,14 +32,14 @@ system: true home: "/opt/consul" -- name: Create consul config directory +- name: Create Consul config directory ansible.builtin.file: path: "/etc/consul" state: "directory" group: "consul" mode: "0750" -- name: Create consul log dir +- name: Create Consul log dir ansible.builtin.file: path: "/var/log/consul" state: "directory" @@ -85,35 +70,162 @@ enabled: true daemon_reload: true -# consul acl bootstrap -format=json -# { -# "CreateIndex": 1042, -# "ModifyIndex": 1042, -# "AccessorID": "50f8bfb6-b259-4c08-f9aa-2dc0eeb08e99", -# "SecretID": "b7f16a8a-0b4f-4661-fea2-f79d95dd011b", -# "Description": "Bootstrap Token (Global Management)", -# "Policies": [ -# { -# "ID": "00000000-0000-0000-0000-000000000001", -# "Name": "global-management" -# } -# ], -# "Local": false, -# "CreateTime": "2026-01-19T18:41:13.294800712+03:00", -# "Hash": "X2AgaFhnQGRhSSF/h0m6qpX1wj/HJWbyXcxkEM/5GrY=" -# } +- name: Generate Consul ACL config + when: "consul_server" + ansible.builtin.template: + src: "templates/acl.json" + dest: "/etc/consul/acl.json" + group: "consul" + mode: "0640" + notify: + - "Restart Consul" + +- name: Running handlers for restarting Consul if required + ansible.builtin.meta: "flush_handlers" + +- name: Bootstrap Consul ACL system + when: "consul_server" + run_once: true + community.general.consul_acl_bootstrap: + bootstrap_secret: "{{ consul_bootstrap_secret }}" + +- name: Create/retrieve Consul node agent accessor_id + ansible.builtin.shell: + executable: "/usr/bin/bash" + cmd: | + set -o pipefail + if test -f /root/.consul_agent_accessor_id; then + echo OK + else + uuidgen > /root/.consul_agent_accessor_id + echo CHANGED + fi + cat /root/.consul_agent_accessor_id + register: "consul_out_agent_accessor_id" + changed_when: "consul_out_agent_accessor_id.stdout_lines[0] == 'CHANGED'" + +- name: Create/retrieve Consul node agent token + community.general.consul_token: + token: "{{ consul_bootstrap_secret }}" + accessor_id: "{{ consul_out_agent_accessor_id.stdout_lines[1] }}" + description: "Node {{ ansible_fqdn }}" + node_identities: + - datacenter: "{{ consul_datacenter }}" + node_name: "{{ ansible_hostname }}" + register: "consul_out_agent_token" + +- name: Create Consul policy for DNS agent + when: "consul_server" + run_once: true + community.general.consul_policy: + token: "{{ consul_bootstrap_secret }}" + name: "dns-access" + rules: | + node_prefix "" { + policy = "read" + } + {% for service in consul_services %} + service "{{ service.name }}" { + policy = "read" + } + {% endfor %} + +- name: Create/retrieve Consul DNS agent accessor_id + ansible.builtin.shell: + executable: "/usr/bin/bash" + cmd: | + set -o pipefail + if test -f /root/.consul_dns_accessor_id; then + echo OK + else + uuidgen > /root/.consul_dns_accessor_id + echo CHANGED + fi + cat /root/.consul_dns_accessor_id + register: "consul_out_dns_accessor_id" + changed_when: "consul_out_dns_accessor_id.stdout_lines[0] == 'CHANGED'" + +- name: Create/retrieve Consul DNS agent token + when: "consul_server" + community.general.consul_token: + token: "{{ consul_bootstrap_secret }}" + accessor_id: "{{ consul_out_dns_accessor_id.stdout_lines[1] }}" + description: "DNS {{ ansible_fqdn }}" + policies: + - name: "dns-access" + register: "consul_out_dns_token" + +- name: Generate Consul agent tokens config + ansible.builtin.template: + src: "templates/tokens.json" + dest: "/etc/consul/tokens.json" + group: "consul" + mode: "0640" + notify: + - "Restart Consul" + +- name: Running handlers for restarting Consul if required + ansible.builtin.meta: "flush_handlers" + +- name: Create Consul policy for UI + when: "consul_server and consul_ui_secret_id != None" + run_once: true + community.general.consul_policy: + token: "{{ consul_bootstrap_secret }}" + name: "ui-access" + rules: | + key_prefix "" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } + +- name: Create/retrieve Consul UI accessor_id + when: "consul_server and consul_ui_secret_id != None and consul_servers[0] == ansible_hostname" + ansible.builtin.shell: + executable: "/usr/bin/bash" + cmd: | + set -o pipefail + if test -f /root/.consul_ui_accessor_id; then + echo OK + else + uuidgen > /root/.consul_ui_accessor_id + echo CHANGED + fi + cat /root/.consul_ui_accessor_id + register: "consul_out_ui_accessor_id" + changed_when: "consul_out_ui_accessor_id.stdout_lines[0] == 'CHANGED'" + +- name: Create/retrieve Consul UI token + when: "consul_server and consul_ui_secret_id != None and consul_servers[0] == ansible_hostname" + community.general.consul_token: + token: "{{ consul_bootstrap_secret }}" + accessor_id: "{{ consul_out_ui_accessor_id.stdout_lines[1] }}" + secret_id: "{{ consul_ui_secret_id }}" + description: "UI" + policies: + - name: "ui-access" - name: Register services to consul loop: "{{ consul_services }}" community.general.consul_agent_service: + token: "{{ consul_bootstrap_secret }}" name: "{{ item.name }}" + address: "{{ item.address | default(ansible_default_ipv4.address) }}" service_port: "{{ item.port }}" tags: "{{ item.tags | default([]) }}" + # Because community.general.consul_agent_check is not idempotent: + register: "consul_out_register_services" - name: Register HTTP checks to consul loop: "{{ consul_services }}" - when: "item.check_http | default(None) != None" + when: "consul_out_register_services.changed and (item.check_http | default(None) != None)" community.general.consul_agent_check: + token: "{{ consul_bootstrap_secret }}" name: "{{ item.name }}_check" service_id: "{{ item.name }}" interval: "{{ item.check_interval | default(consul_default_check_interval) }}" @@ -123,8 +235,9 @@ - name: Register TCP checks to consul loop: "{{ consul_services }}" - when: "item.check_tcp | default(None) != None" + when: "consul_out_register_services and (item.check_tcp | default(None) != None)" community.general.consul_agent_check: + token: "{{ consul_bootstrap_secret }}" name: "{{ item.name }}_check" service_id: "{{ item.name }}" interval: "{{ item.check_interval | default(consul_default_check_interval) }}" diff --git a/seb4itik/byow/roles/consul/templates/acl.json b/seb4itik/byow/roles/consul/templates/acl.json new file mode 100644 index 0000000..2d52dd4 --- /dev/null +++ b/seb4itik/byow/roles/consul/templates/acl.json @@ -0,0 +1,9 @@ +{ + "acl" : { + "enabled" : true, +{% if consul_server %} + "default_policy" : "deny", +{% endif %} + "down_policy" : "extend-cache" + } +} diff --git a/seb4itik/byow/roles/consul/templates/config.json b/seb4itik/byow/roles/consul/templates/config.json index 19abe06..6da23de 100644 --- a/seb4itik/byow/roles/consul/templates/config.json +++ b/seb4itik/byow/roles/consul/templates/config.json @@ -1,28 +1,43 @@ { "log_level": "INFO", + "enable_syslog": true, + "node_name": "{{ ansible_hostname }}", "domain": "{{ consul_domain }}", "datacenter": "{{ consul_datacenter | mandatory }}", - "key_file": "{{ consul_key_file }}", - "cert_file": "{{ consul_cert_file }}", - "ca_file": "{{ consul_ca_file }}", - "verify_incoming": true, - "verify_outgoing": true, - "verify_server_hostname": true, "encrypt": "{{ consul_secret_key }}", "data_dir": "/opt/consul", + "retry_join": ["{{ consul_servers | join('", "') }}"], + "disable_coordinates": true, + "disable_remote_exec": true, + "disable_update_check": true, + "tls": { + "defaults": { + "ca_file": "{{ consul_ca_file }}", + "cert_file": "{{ consul_cert_file }}", + "key_file": "{{ consul_key_file }}", + "verify_incoming": true, + "verify_outgoing": true + }, + "internal_rpc": { + "verify_server_hostname": true + } + }, {% if consul_server %} - "node_name": "{{ ansible_hostname }}", "server": true, - "ui": true, "leave_on_terminate": true, - "client_addr": "{{ consul_client_addr }}", "bind_addr": "{{ consul_bind_addr }}", - "advertise_addr": "{{ consul_advertise_addr }}", - "bootstrap_expect": {{ (consul_servers | length) - 1 }}, - "enable_syslog": true, +{# + "addresses": { + "https": "{{ consul_client_addr }}" + }, +#} + "bootstrap_expect": {{ consul_servers | length }}, + "ui_config": { + "enabled": {{ (consul_ui_secret_id != None) | ternary('true', 'false') }} + }, "performance": { "raft_multiplier": 1 - }, + } +{% else %} {% endif %} - "retry_join": ["{{ consul_servers | join('", "') }}"] } diff --git a/seb4itik/byow/roles/consul/templates/consul.service b/seb4itik/byow/roles/consul/templates/consul.service index 5f2562a..2ee255e 100644 --- a/seb4itik/byow/roles/consul/templates/consul.service +++ b/seb4itik/byow/roles/consul/templates/consul.service @@ -9,10 +9,10 @@ ConditionFileNotEmpty=/etc/consul/config.json Type=notify User=consul Group=consul -ExecStart=/usr/local/bin/consul agent -config-file=/etc/consul/config.json +ExecStart=/usr/local/consul-{{ consul_version }}/consul agent -config-file=/etc/consul/ KillMode=process Restart=on-failure LimitNOFILE=65536 [Install] -WantedBy=multi-user.target \ No newline at end of file +WantedBy=multi-user.target diff --git a/seb4itik/byow/roles/consul/templates/tokens.json b/seb4itik/byow/roles/consul/templates/tokens.json new file mode 100644 index 0000000..2b135bc --- /dev/null +++ b/seb4itik/byow/roles/consul/templates/tokens.json @@ -0,0 +1,10 @@ +{ + "acl" : { + "tokens": { +{% if consul_server %} + "dns": "{{ consul_out_dns_token.token.SecretID }}", +{% endif %} + "default": "{{ consul_out_agent_token.token.SecretID }}" + } + } +}