diff --git a/defaults/main.yml b/defaults/main.yml index bf21f69..0490b29 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,2 +1,21 @@ --- # defaults file for renew_consul_certificates +renew_consul_certificates_config_dir: /etc/consul-template.d/consul +renew_consul_certificates_consul_user: consul +renew_consul_certificates_consul_group: consul +renew_consul_certificates_vault_addr: "https://consul.example.com" +renew_consul_certificates_vault_token: mysupersecretconsultokenthatyoushouldchange +renew_consul_certificates_vault_token_unwrap: false +renew_consul_certificates_vault_token_renew: true +renew_consul_certificates_ca_dest: /opt/consul/tls/ca.pem +renew_consul_certificates_cert_dest: /opt/consul/tls/cert.pem +renew_consul_certificates_key_dest: /opt/consul/tls/key.pem +renew_consul_certificates_info: + issuer_path: pki/issue/your-issuer + common_name: consul01.example.com + ttl: 90d + is_server: false + include_consul_service: false +renew_consul_certificates_consul_dc_name: dc1.consul +renew_consul_certificates_consul_service_name: consul.service.consul +renew_consul_certificates_start_service: false diff --git a/handlers/main.yml b/handlers/main.yml index 735d49a..6c9f41b 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -1,2 +1,19 @@ --- # handlers file for renew_consul_certificates +- name: "Reload service file" + ansible.builtin.systemd: + daemon_reload: true + listen: "systemctl-daemon-reload" + +- name: "Enable consul-certs service" + ansible.builtin.service: + name: consul-certs + enabled: true + listen: "systemctl-enable-consul-certs" + +- name: "Start consul-certs service" + ansible.builtin.service: + name: consul-certs + state: restarted + listen: "systemctl-restart-consul-certs" + when: renew_consul_certificates_start_service diff --git a/molecule/with_custom_config/all.yml b/molecule/with_custom_config/all.yml new file mode 100644 index 0000000..70b994a --- /dev/null +++ b/molecule/with_custom_config/all.yml @@ -0,0 +1,17 @@ +--- +renew_vault_certificates_config_dir: /etc/consul-template.d/vault +renew_vault_certificates_vault_user: vault +renew_vault_certificates_vault_group: vault +renew_vault_certificates_vault_addr: "https://vault.example.com" +renew_vault_certificates_vault_token: mysupersecretvaulttokenthatyoushouldchange +renew_vault_certificates_vault_token_unwrap: false +renew_vault_certificates_vault_token_renew: true +renew_vault_certificates_cert_dest: /opt/vault/tls/cert.pem +renew_vault_certificates_key_dest: /opt/vault/tls/key.pem +renew_vault_certificates_info: + issuer_path: pki/issue/vault-issuer + common_name: vault01.example.com + ttl: 90d + include_consul_service: true +renew_vault_certificates_consul_service_name: vault.service.consul +renew_vault_certificates_start_service: false diff --git a/molecule/with_custom_config/converge.yml b/molecule/with_custom_config/converge.yml new file mode 100644 index 0000000..897496d --- /dev/null +++ b/molecule/with_custom_config/converge.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: "Include ednxzu.renew_vault_certificates" + ansible.builtin.include_role: + name: "ednxzu.renew_vault_certificates" diff --git a/molecule/with_custom_config/group_vars/all.yml b/molecule/with_custom_config/group_vars/all.yml new file mode 100644 index 0000000..2c06ecf --- /dev/null +++ b/molecule/with_custom_config/group_vars/all.yml @@ -0,0 +1,17 @@ +--- +renew_consul_certificates_config_dir: /etc/consul-template.d/consul +renew_consul_certificates_consul_user: consul +renew_consul_certificates_consul_group: consul +renew_consul_certificates_vault_addr: "https://consul.example.com" +renew_consul_certificates_vault_token: mysupersecretconsultokenthatyoushouldchange +renew_consul_certificates_vault_token_unwrap: false +renew_consul_certificates_vault_token_renew: true +renew_consul_certificates_cert_dest: /opt/consul/tls/cert.pem +renew_consul_certificates_key_dest: /opt/consul/tls/key.pem +renew_consul_certificates_info: + issuer_path: pki/issue/consul-issuer + common_name: consul01.example.com + ttl: 90d + include_consul_service: true +renew_consul_certificates_consul_service_name: consul.service.consul +renew_consul_certificates_start_service: false diff --git a/molecule/with_custom_config/molecule.yml b/molecule/with_custom_config/molecule.yml new file mode 100644 index 0000000..6132acb --- /dev/null +++ b/molecule/with_custom_config/molecule.yml @@ -0,0 +1,37 @@ +--- +dependency: + name: galaxy + options: + requirements-file: ./requirements.yml +driver: + name: docker +platforms: + - name: instance + image: geerlingguy/docker-${MOLECULE_TEST_OS}-ansible + command: "" + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup + cgroupns_mode: host + privileged: true + pre_build_image: true +provisioner: + name: ansible + config_options: + defaults: + remote_tmp: /tmp/.ansible +verifier: + name: testinfra +scenario: + name: with_custom_config + test_sequence: + - dependency + - cleanup + - destroy + - syntax + - create + - prepare + - converge + - idempotence + - verify + - cleanup + - destroy diff --git a/molecule/with_custom_config/prepare.yml b/molecule/with_custom_config/prepare.yml new file mode 100644 index 0000000..de48134 --- /dev/null +++ b/molecule/with_custom_config/prepare.yml @@ -0,0 +1,15 @@ +--- +- name: Prepare + hosts: all + tasks: + - name: "Create group vault" + ansible.builtin.group: + name: "vault" + state: present + + - name: "Create user vault" + ansible.builtin.user: + name: "vault" + group: "vault" + shell: /bin/false + state: present diff --git a/molecule/with_custom_config/requirements.yml b/molecule/with_custom_config/requirements.yml new file mode 100644 index 0000000..0a4a9fb --- /dev/null +++ b/molecule/with_custom_config/requirements.yml @@ -0,0 +1,5 @@ +--- +# requirements file for molecule +roles: + - name: ednxzu.manage_repositories + - name: ednxzu.manage_apt_packages diff --git a/molecule/with_custom_config/tests/conftest.py b/molecule/with_custom_config/tests/conftest.py new file mode 100644 index 0000000..f7ddb3f --- /dev/null +++ b/molecule/with_custom_config/tests/conftest.py @@ -0,0 +1,22 @@ +"""PyTest Fixtures.""" +from __future__ import absolute_import + +import os + +import pytest + + +def pytest_runtest_setup(item): + """Run tests only when under molecule with testinfra installed.""" + try: + import testinfra + except ImportError: + pytest.skip("Test requires testinfra", allow_module_level=True) + if "MOLECULE_INVENTORY_FILE" in os.environ: + pytest.testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + os.environ["MOLECULE_INVENTORY_FILE"] + ).get_hosts("all") + else: + pytest.skip( + "Test should run only from inside molecule.", allow_module_level=True + ) diff --git a/molecule/with_custom_config/tests/test_default.py b/molecule/with_custom_config/tests/test_default.py new file mode 100644 index 0000000..70bf16f --- /dev/null +++ b/molecule/with_custom_config/tests/test_default.py @@ -0,0 +1,48 @@ +"""Role testing files using testinfra.""" + + +def test_hosts_file(host): + """Validate /etc/hosts file.""" + etc_hosts = host.file("/etc/hosts") + assert etc_hosts.exists + assert etc_hosts.user == "root" + assert etc_hosts.group == "root" + +def test_consul_template_config(host): + """Validate /etc/consul-template.d/consul/ files.""" + etc_consul_template_d_consul_config_hcl = host.file("/etc/consul-template.d/consul/consul_config.hcl") + assert etc_consul_template_d_consul_config_hcl.exists + assert etc_consul_template_d_consul_config_hcl.user == "consul" + assert etc_consul_template_d_consul_config_hcl.group == "consul" + assert etc_consul_template_d_consul_config_hcl.mode == 0o600 + +def test_template_files(host): + """Validate /etc/consul-template.d/consul/templates/ files.""" + consul_cert_pem_tpl = host.file("/etc/consul-template.d/consul/templates/consul_cert.pem.tpl") + consul_key_pem_tpl = host.file("/etc/consul-template.d/consul/templates/consul_key.pem.tpl") + for file in consul_cert_pem_tpl, consul_key_pem_tpl: + assert file.exists + assert file.user == "consul" + assert file.group == "consul" + assert file.mode == 0o600 + assert consul_cert_pem_tpl.content_string == '{{ with secret "pki/issue/consul-issuer" "common_name=consul01.example.com" "ttl=90d" "alt_names=localhost,consul.service.consul,active.consul.service.consul,standby.consul.service.consul" "ip_sans=127.0.0.1" }}\n{{ .Data.certificate }}\n{{ .Data.issuing_ca }}\n{{ end }}\n' + assert consul_key_pem_tpl.content_string == '{{ with secret "pki/issue/consul-issuer" "common_name=consul01.example.com" "ttl=90d" "alt_names=localhost,consul.service.consul,active.consul.service.consul,standby.consul.service.consul" "ip_sans=127.0.0.1" }}\n{{ .Data.private_key }}\n{{ end }}\n' + +def test_consul_certs_service_file(host): + """Validate consul-certs service file.""" + etc_systemd_system_consul_certs_service = host.file("/etc/systemd/system/consul-certs.service") + assert etc_systemd_system_consul_certs_service.exists + assert etc_systemd_system_consul_certs_service.user == "root" + assert etc_systemd_system_consul_certs_service.group == "root" + assert etc_systemd_system_consul_certs_service.mode == 0o644 + assert etc_systemd_system_consul_certs_service.content_string != "" + +def test_consul_certs_service(host): + """Validate consul-certs service.""" + consul_certs_service = host.service("consul-certs.service") + assert consul_certs_service.is_enabled + assert not consul_certs_service.is_running + assert consul_certs_service.systemd_properties["Restart"] == "on-failure" + assert consul_certs_service.systemd_properties["User"] == "consul" + assert consul_certs_service.systemd_properties["Group"] == "consul" + assert consul_certs_service.systemd_properties["FragmentPath"] == "/etc/systemd/system/consul-certs.service" diff --git a/tasks/configure.yml b/tasks/configure.yml index 23c3d04..17acbbc 100644 --- a/tasks/configure.yml +++ b/tasks/configure.yml @@ -1,2 +1,48 @@ --- -# task/configure file for renew_consul_certificates \ No newline at end of file +# task/configure file for renew_consul_certificates +- name: "Configure files for consul certificate renewal" + notify: + - "systemctl-enable-consul-certs" + - "systemctl-restart-consul-certs" + block: + - name: "Copy consul_config.hcl template" + ansible.builtin.template: + src: consul_config.hcl.j2 + dest: "{{ renew_consul_certificates_config_dir }}/consul_config.hcl" + owner: "{{ renew_consul_certificates_consul_user }}" + group: "{{ renew_consul_certificates_consul_group }}" + mode: '0600' + + - name: "Copy consul_ca.pem.tpl template" + ansible.builtin.template: + src: consul_ca.pem.tpl.j2 + dest: "{{ renew_consul_certificates_config_dir }}/templates/consul_ca.pem.tpl" + owner: "{{ renew_consul_certificates_consul_user }}" + group: "{{ renew_consul_certificates_consul_group }}" + mode: '0600' + + - name: "Copy consul_cert.pem.tpl template" + ansible.builtin.template: + src: consul_cert.pem.tpl.j2 + dest: "{{ renew_consul_certificates_config_dir }}/templates/consul_cert.pem.tpl" + owner: "{{ renew_consul_certificates_consul_user }}" + group: "{{ renew_consul_certificates_consul_group }}" + mode: '0600' + + - name: "Copy consul_cert.key.tpl template" + ansible.builtin.template: + src: consul_key.pem.tpl.j2 + dest: "{{ renew_consul_certificates_config_dir }}/templates/consul_key.pem.tpl" + owner: "{{ renew_consul_certificates_consul_user }}" + group: "{{ renew_consul_certificates_consul_group }}" + mode: '0600' + +- name: "Configure consul-certs systemd service" + ansible.builtin.template: + src: consul-certs.service.j2 + dest: /etc/systemd/system/consul-certs.service + owner: root + group: root + mode: '0644' + notify: + - "systemctl-daemon-reload" diff --git a/tasks/install.yml b/tasks/install.yml index 59b625b..b559f50 100644 --- a/tasks/install.yml +++ b/tasks/install.yml @@ -1,2 +1,15 @@ --- -# task/install file for renew_consul_certificates \ No newline at end of file +# task/install file for renew_consul_certificates +- name: "Configure hashicorp repository" + ansible.builtin.include_role: + name: ednxzu.manage_repositories + vars: + manage_repositories_enable_default_repo: false + manage_repositories_enable_custom_repo: true + manage_repositories_custom_repo: "{{ renew_consul_certificates_repository }}" + +- name: "Install consul-template" + ansible.builtin.include_role: + name: ednxzu.manage_apt_packages + vars: + manage_apt_packages_list: "{{ renew_consul_certificates_packages }}" diff --git a/tasks/main.yml b/tasks/main.yml index a1a5e6a..088dca8 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,2 +1,10 @@ --- # task/main file for renew_consul_certificates +- name: "Import prerequisites.yml" + ansible.builtin.include_tasks: prerequisites.yml + +- name: "Import install.yml" + ansible.builtin.include_tasks: install.yml + +- name: "Import configure.yml" + ansible.builtin.include_tasks: configure.yml diff --git a/tasks/prerequisites.yml b/tasks/prerequisites.yml index 4d6db05..66371c5 100644 --- a/tasks/prerequisites.yml +++ b/tasks/prerequisites.yml @@ -1,2 +1,37 @@ --- -# task/prerequisites file for renew_consul_certificates \ No newline at end of file +# task/prerequisites file for renew_consul_certificates +- name: "Install required roles" + ansible.builtin.command: + cmd: "ansible-galaxy install {{ item }}" + loop: "{{ renew_consul_certificates_prerequisites_roles }}" + changed_when: false + delegate_to: localhost + run_once: true + +- name: "Create directory {{ renew_consul_certificates_config_dir }}" + ansible.builtin.file: + path: "{{ renew_consul_certificates_config_dir }}" + state: directory + owner: "{{ renew_consul_certificates_consul_user }}" + group: "{{ renew_consul_certificates_consul_group }}" + mode: '0755' + +- name: "Create directory templates directory in {{ renew_consul_certificates_config_dir }}" + ansible.builtin.file: + path: "{{ renew_consul_certificates_config_dir }}/templates" + state: directory + owner: "{{ renew_consul_certificates_consul_user }}" + group: "{{ renew_consul_certificates_consul_group }}" + mode: '0755' + +- name: "Ensure certificate/key directory(ies) exist(s)" + ansible.builtin.file: + path: "{{item | dirname }}" + state: directory + owner: "{{ renew_consul_certificates_consul_user }}" + group: "{{ renew_consul_certificates_consul_group }}" + mode: '0755' + loop: + - "{{ renew_consul_certificates_cert_dest }}" + - "{{ renew_consul_certificates_key_dest }}" + - "{{ renew_consul_certificates_ca_dest }}" diff --git a/templates/consul-certs.service.j2 b/templates/consul-certs.service.j2 new file mode 100644 index 0000000..f43d719 --- /dev/null +++ b/templates/consul-certs.service.j2 @@ -0,0 +1,16 @@ +[Unit] +Description=Automatic renewal of consul certificate using consul-template +Requires=network-online.target +After=network-online.target consul.service +ConditionFileNotEmpty={{ renew_consul_certificates_config_dir }}/consul_config.hcl + +[Service] +User={{ renew_consul_certificates_consul_user }} +Group={{ renew_consul_certificates_consul_group }} +ExecStart=/usr/bin/consul-template $OPTIONS -config={{ renew_consul_certificates_config_dir }}/consul_config.hcl +ExecReload=/bin/kill --signal HUP $MAINPID +KillSignal=SIGINT +Restart=on-failure + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/templates/consul_ca.pem.j2 b/templates/consul_ca.pem.j2 new file mode 100644 index 0000000..545fbb1 --- /dev/null +++ b/templates/consul_ca.pem.j2 @@ -0,0 +1,5 @@ +{% raw %}{{ with secret {% endraw %}"{{ renew_consul_certificates_info['issuer_path'] }}" "common_name={{ renew_consul_certificates_info['common_name'] }}" "ttl={{ renew_consul_certificates_info['ttl'] }}" "alt_names=localhost{% if renew_consul_certificates_info['is_server'] %},server.{{ renew_consul_certificates_consul_dc_name }}{% endif %}{% if renew_consul_certificates_info['include_consul_service']%},{{ renew_consul_certificates_consul_service_name }}{% endif %}" "ip_sans=127.0.0.1"{% raw %} }}{% endraw %} + +{% raw %}{{ .Data.issuing_ca }}{% endraw %} + +{% raw %}{{ end }}{% endraw %} \ No newline at end of file diff --git a/templates/consul_cert.pem.tpl.j2 b/templates/consul_cert.pem.tpl.j2 new file mode 100644 index 0000000..223634d --- /dev/null +++ b/templates/consul_cert.pem.tpl.j2 @@ -0,0 +1,5 @@ +{% raw %}{{ with secret {% endraw %}"{{ renew_consul_certificates_info['issuer_path'] }}" "common_name={{ renew_consul_certificates_info['common_name'] }}" "ttl={{ renew_consul_certificates_info['ttl'] }}" "alt_names=localhost{% if renew_consul_certificates_info['is_server'] %},server.{{ renew_consul_certificates_consul_dc_name }}{% endif %}{% if renew_consul_certificates_info['include_consul_service']%},{{ renew_consul_certificates_consul_service_name }}{% endif %}" "ip_sans=127.0.0.1"{% raw %} }}{% endraw %} + +{% raw %}{{ .Data.certificate }}{% endraw %} + +{% raw %}{{ end }}{% endraw %} diff --git a/templates/consul_config.hcl.j2 b/templates/consul_config.hcl.j2 new file mode 100644 index 0000000..10f7f14 --- /dev/null +++ b/templates/consul_config.hcl.j2 @@ -0,0 +1,33 @@ +vault { + address = "{{ renew_consul_certificates_vault_addr }}" + token = "{{ renew_consul_certificates_vault_token }}" + unwrap_token = {{ renew_consul_certificates_vault_token_unwrap|lower }} + renew_token = {{ renew_consul_certificates_vault_token_renew|lower }} +} + +template { + source = "{{ renew_consul_certificates_config_dir }}/templates/consul_ca.pem.tpl" + destination = "{{ renew_consul_certificates_ca_dest }}" + perms = 0700 + user = "{{ renew_consul_certificates_consul_user }}" + group = "{{ renew_consul_certificates_consul_group }}" + command = "sh -c 'echo \"$(date) Update certificate and key file for {{ renew_consul_certificates_info['common_name'] }}\" && pkill -SIGHUP vault '" +} + +template { + source = "{{ renew_consul_certificates_config_dir }}/templates/consul_cert.pem.tpl" + destination = "{{ renew_consul_certificates_cert_dest }}" + perms = 0700 + user = "{{ renew_consul_certificates_consul_user }}" + group = "{{ renew_consul_certificates_consul_group }}" + command = "sh -c 'echo \"$(date) Update certificate and key file for {{ renew_consul_certificates_info['common_name'] }}\" && pkill -SIGHUP vault '" +} + +template { + source = "{{ renew_consul_certificates_config_dir }}/templates/consul_key.pem.tpl" + destination = "{{ renew_consul_certificates_key_dest }}" + perms = 0700 + user = "{{ renew_consul_certificates_consul_user }}" + group = "{{ renew_consul_certificates_consul_group }}" + command = "sh -c 'echo \"$(date) Update certificate and key file for {{ renew_consul_certificates_info['common_name'] }}\" && pkill -SIGHUP vault '" +} diff --git a/templates/consul_key.pem.tpl.j2 b/templates/consul_key.pem.tpl.j2 new file mode 100644 index 0000000..234de6c --- /dev/null +++ b/templates/consul_key.pem.tpl.j2 @@ -0,0 +1,5 @@ +{% raw %}{{ with secret {% endraw %}"{{ renew_consul_certificates_info['issuer_path'] }}" "common_name={{ renew_consul_certificates_info['common_name'] }}" "ttl={{ renew_consul_certificates_info['ttl'] }}" "alt_names=localhost{% if renew_consul_certificates_info['is_server'] %},server.{{ renew_consul_certificates_consul_dc_name }}{% endif %}{% if renew_consul_certificates_info['include_consul_service']%},{{ renew_consul_certificates_consul_service_name }}{% endif %}" "ip_sans=127.0.0.1"{% raw %} }}{% endraw %} + +{% raw %}{{ .Data.private_key }}{% endraw %} + +{% raw %}{{ end }}{% endraw %} diff --git a/vars/main.yml b/vars/main.yml index 8691bd9..226a430 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -1,2 +1,17 @@ --- # vars file for renew_consul_certificates +renew_consul_certificates_prerequisites_roles: + - ednxzu.manage_repositories + - ednxzu.manage_apt_packages +renew_consul_certificates_repository: + - uri: "https://apt.releases.hashicorp.com" + gpg_key: "https://apt.releases.hashicorp.com/gpg" + comments: "hashicorp repository" + type: "deb" + suites: "{{ ansible_distribution_release }}" + components: "main" + filename: "hashicorp" +renew_consul_certificates_packages: + - name: consul-template + version: latest + state: present