diff --git a/README.md b/README.md index 7ca52f0..9a1b16a 100644 --- a/README.md +++ b/README.md @@ -26,14 +26,14 @@ By default, this role configures a cron job to run under the provided user accou ### Automatic Certificate Generation -Currently there is one built-in method for generating new certificates using this role: `standalone`. Other methods (e.g. using nginx or apache and a webroot) may be added in the future. +Currently there are two built-in methods for generating new certificates using this role: `standalone` and `dns`. Other methods (e.g. using nginx or apache and a webroot) may be added in the future. **For a complete example**: see the fully functional test playbook in [molecule/default/playbook-standalone-nginx-aws.yml](molecule/default/playbook-standalone-nginx-aws.yml). certbot_create_if_missing: false certbot_create_method: standalone -Set `certbot_create_if_missing` to `yes` or `True` to let this role generate certs. Set the method used for generating certs with the `certbot_create_method` variable—current allowed values include: `standalone`. +Set `certbot_create_if_missing` to `yes` or `True` to let this role generate certs. Set the method used for generating certs with the `certbot_create_method` variable—current allowed values include: `standalone` and `dns`. certbot_admin_email: email@example.com @@ -62,6 +62,60 @@ Services that should be stopped while `certbot` runs it's own standalone server These services will only be stopped the first time a new cert is generated. +#### DNS Certificate Generation + +To use DNS challenge method when creating certificates you must use: `certbot_create_method: dns`. You have the following parameters: + + certbot_dns_plugin: *dns-plugins* + + certbot_dns_target_server: *target.IP* + certbot_dns_target_server_port: *DNS.Port* + certbot_dns_tsig_keyname: "*tsig_key*" + certbot_dns_key_secret: "*key/api_secret*" + certbot_dns_key_algorithm: "*tsig_key_algorithm*" + +If you are using another plugin instead of RFC2136, like CloudFlare or DigitalOcean `certbot_dns_key_secret` will be used as **API Token** and you do not need other parameters. + +DNS TSIG keys can be generate using `dnssec-keygen -a HMAC-SHA256 -b 256 -n HOST certbot.` but futher details and configurations are not covered here. The same applies for configuring DNS (BIND), but sample configuration is shown using BIND/RFC2136: + +``` +key certbot. { + algorithm hmac-sha256; + secret "9arciOclzevu7rEvSww0cpiOu5aPo65NFMBkcEuad5U="; +}; + +zone "example.com" { + type master; + ... + allow-update { key certbot. }; +}; + +``` + +#### DNS Plugins + +Currently there are three built-in methods for **dns-plugins**: `rfc2136`, `cloudflare` and `digitalocean`. You can review DNS plugins supported by Certbot [here](https://eff-certbot.readthedocs.io/en/stable/using.html#dns-plugins). Other methods (e.g. using nginx or apache and a webroot) may be added in the future. +If you want to use other credentials files using other plugins you have to set `certbot_dns_credentials_custom_file: `. + +#### DNS Integration - Deploy Hook + +When using DNS integration, you do not need to stop/start service after certificate is generated. You only need to restart/reload service. There is a nice feature that already assembles *fullchain.pem* and *privkey.pem* into one file for use directly on haproxy. You use can enable this behavior by adding 'haproxy' to `certbot_create_dns_deploy_hook_services` variable: + + certbot_create_dns_deploy_hook_services: + - haproxy + +It works exactly the same as `certbot_create_standalone_stop_services`. + +Configuring haproxy will not be included, just the "hook" for certbot to make easier for configurating it as certbot *knowns* when a new certificate is issued/renewed. + +#### Testing Certificates - Staging Server + +To use Lets Encrypt Staging CA (Testing Certificate) instead of "Production" CA, you can set `certbot_use_staging_server` to `true`. Defaults to `false`. + +#### Delete Certificates + +To delete certificates prior generating them you must set `certbot_delete_certificate` to `true`. Defaults to `false`. If you want to fully remove certificates and not generate them anymore you must set `certbot_create_if_missing` to `false`. + ### Snap Installation Beginning in December 2020, the Certbot maintainers decided to recommend installing Certbot from Snap rather than maintain scripts like `certbot-auto`. diff --git a/defaults/main.yml b/defaults/main.yml index cd65499..34c21f4 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -8,6 +8,8 @@ certbot_auto_renew_options: "--quiet --no-self-upgrade" # Enable to use staging server instead of production (useful for testing) certbot_use_staging_server: false +# Enable delete certificates prior generating +certbot_delete_certificate: false # Parameters used when creating new Certbot certs. certbot_create_if_missing: false @@ -22,6 +24,9 @@ certbot_certs: [] # - example3.com certbot_create_command: >- {{ certbot_script }} certonly --standalone --noninteractive --agree-tos + {{ '--test-cert' + if certbot_use_staging_server + else '' }} --email {{ cert_item.email | default(certbot_admin_email) }} -d {{ cert_item.domains | join(',') }} {{ '--pre-hook /etc/letsencrypt/renewal-hooks/pre/stop_services' @@ -34,11 +39,12 @@ certbot_create_command: >- # Parameters DNS Plugins (used when certbot_create_method = dns) certbot_dns_plugin: rfc2136 # certbot_dns_credentials_custom_file: # use when plugin is != rfc2136 -certbot_dns_target_server: 127.0.0.1 -certbot_dns_target_server_port: 53 -certbot_dns_tsig_keyname: "certbot." -certbot_dns_key_secret: "azertyAZERTY123456" -certbot_dns_key_algorithm: "HMAC-MD5" +# certbot_dns_target_server: 127.0.0.1 +# certbot_dns_target_server_port: 53 +# certbot_dns_tsig_keyname: "certbot." +# certbot_dns_key_secret: "azertyAZERTY123456" +# certbot_dns_key_algorithm: "HMAC-MD5" + certbot_dns_create_command: >- {{ certbot_script }} certonly --noninteractive --agree-tos {{ '--test-cert' @@ -47,18 +53,26 @@ certbot_dns_create_command: >- --dns-{{ certbot_dns_plugin }} --dns-{{ certbot_dns_plugin }}-credentials {{ certbot_dns_credentials_file }} --email {{ cert_item.email | default(certbot_admin_email) }} -d {{ cert_item.domains | join(',') }} - {{ '--deploy-hook /etc/letsencrypt/renewal-hooks/deploy/renew_hook.sh' - if certbot_create_dns_renew_hook_services + {{ '--deploy-hook /etc/letsencrypt/renewal-hooks/deploy/deploy_hook.sh' + if certbot_create_dns_deploy_hook_services else '' }} -certbot_create_dns_renew_hook_services: +certbot_create_dns_deploy_hook_services: - haproxy + #- nginx + #- apache certbot_create_standalone_stop_services: - nginx # - apache # - varnish +# [root@dnstest-default-9qvkw-test-3 ~]# certbot delete --cert-name certbotdnstest.staging.prodepa.pa.gov.br -n + +certbot_delete_command: >- + {{ certbot_script }} delete --noninteractive + --cert-name {{ cert_item.domains | first | replace('*.', '') }} + # Available options: 'package', 'snap', 'source'. certbot_install_method: 'package' diff --git a/tasks/create-cert-dns-plugin.yml b/tasks/create-cert-dns-plugin.yml index 04131bd..ddbf6ce 100644 --- a/tasks/create-cert-dns-plugin.yml +++ b/tasks/create-cert-dns-plugin.yml @@ -16,13 +16,13 @@ - name: Create deploy hook to execute tasks post cert generatation. template: - src: renew_hook.j2 - dest: /etc/letsencrypt/renewal-hooks/deploy/renew_hook.sh + src: deploy_hook.j2 + dest: /etc/letsencrypt/renewal-hooks/deploy/deploy_hook.sh owner: root group: root mode: 0750 when: - - certbot_create_dns_renew_hook_services is defined + - certbot_create_dns_deploy_hook_services is defined - name: "Create DNS RFC {{ certbot_dns_plugin }} Credentials File." template: @@ -49,16 +49,3 @@ - name: Generate new certificate if one doesn't exist. command: "{{ certbot_dns_create_command }}" when: not letsencrypt_cert.stat.exists - -- name: Assemble certificate crt and key into pem file for haproxy - assemble: - dest: "/etc/letsencrypt/live/{{ cert_item.domains | first | replace('*.', '') }}/{{ cert_item.domains | first | replace('*.', '') }}-haproxy.pem" - src: "/etc/letsencrypt/live/{{ cert_item.domains | first | replace('*.', '') }}/" - regexp: '(fullchain.pem|privkey.pem)' - remote_src: yes - owner: root - group: root - mode: '0600' - when: - - not letsencrypt_cert.stat.exists - - ('haproxy' is in certbot_create_dns_renew_hook_services)|bool \ No newline at end of file diff --git a/tasks/delete-cert.yml b/tasks/delete-cert.yml new file mode 100644 index 0000000..ec34f9e --- /dev/null +++ b/tasks/delete-cert.yml @@ -0,0 +1,26 @@ +--- +- name: Check if certificate already exists. + stat: + path: /etc/letsencrypt/live/{{ cert_item.domains | first | replace('*.', '') }}/cert.pem + register: letsencrypt_cert + +- name: Delete Certificate + command: "{{ certbot_delete_command }}" + when: + - letsencrypt_cert.stat.exists + - certbot_delete_certificate|bool + +- name: Make sure certificate directory is removed + file: + path: /etc/letsencrypt/live/{{ cert_item.domains | first | replace('*.', '') }} + state: absent + when: + - letsencrypt_cert.stat.exists + - certbot_delete_certificate|bool + +- name: Remove cron job for certbot renewal (if configured). + cron: + name: Certbot automatic renewal. + state: absent + when: + - not certbot_auto_renew diff --git a/tasks/install-with-package.yml b/tasks/install-with-package.yml index 10490ff..5ee2358 100644 --- a/tasks/install-with-package.yml +++ b/tasks/install-with-package.yml @@ -2,6 +2,10 @@ - name: Install Certbot. package: "name={{ certbot_package }} state=present" +- name: Install DNS Plugin - {{ certbot_dns_plugin }}. + package: name="certbot-dns-{{ certbot_dns_plugin }} state=present" + when: certbot_dns_plugin is defined + - name: Set Certbot script variable. set_fact: certbot_script: "{{ certbot_package }}" diff --git a/tasks/main.yml b/tasks/main.yml index e2a4382..a8ee5fd 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -13,11 +13,19 @@ - import_tasks: install-from-source.yml when: certbot_install_method == 'source' +- include_tasks: delete-cert.yml + with_items: "{{ certbot_certs }}" + when: + - certbot_delete_certificate|bool + loop_control: + loop_var: cert_item + - include_tasks: create-cert-standalone.yml with_items: "{{ certbot_certs }}" when: - certbot_create_if_missing - certbot_create_method == 'standalone' + - not certbot_delete_certificate loop_control: loop_var: cert_item @@ -26,8 +34,10 @@ when: - certbot_create_if_missing - certbot_create_method == 'dns' + - not certbot_delete_certificate loop_control: loop_var: cert_item - import_tasks: renew-cron.yml - when: certbot_auto_renew + when: + - certbot_auto_renew \ No newline at end of file diff --git a/templates/deploy_hook.j2 b/templates/deploy_hook.j2 new file mode 100644 index 0000000..8a32d45 --- /dev/null +++ b/templates/deploy_hook.j2 @@ -0,0 +1,23 @@ +#!/bin/bash +# {{ ansible_managed }} + +{% for item in certbot_create_dns_deploy_hook_services %} + +{% if item == 'haproxy' %} +# Assemble certificate/chain and private key into one single file for haproxy +cat $RENEWED_LINEAGE/fullchain.pem $RENEWED_LINEAGE/privkey.pem > $RENEWED_LINEAGE/`echo $RENEWED_DOMAINS|awk '{print $1"-haproxy.pem"}'` +# set proper permissions +chmod 0600 $RENEWED_LINEAGE/`echo $RENEWED_DOMAINS|awk '{print $1"-haproxy.pem"}'` +{% endif %} + +{% if ansible_service_mgr == 'systemd' %} +systemctl reload {{ item }} +{% elif ansible_service_mgr == 'upstart' %} +initctl stop {{ item }} && initctl start {{ item }} +{% elif ansible_service_mgr == 'openrc' %} +rc-service {{ item }} restart +{% else %} +service {{ item }} reload +{% endif %} + +{% endfor %} diff --git a/templates/dns_plugin_credentials.j2 b/templates/dns_plugin_credentials.j2 index e858455..c8e7a05 100644 --- a/templates/dns_plugin_credentials.j2 +++ b/templates/dns_plugin_credentials.j2 @@ -11,4 +11,12 @@ dns_{{certbot_dns_plugin}}_name={{certbot_dns_tsig_keyname}} dns_{{certbot_dns_plugin}}_secret={{certbot_dns_key_secret}} # TSIG key algorithm dns_{{certbot_dns_plugin}}_algorithm={{certbot_dns_key_algorithm}} -{% endif %} \ No newline at end of file +{% endif %} + +{% if certbot_dns_plugin == 'cloudflare' %} +dns_cloudflare_api_token={{certbot_dns_key_secret}} +{% endif %} + +{% if certbot_dns_plugin == 'digitalocean' %} +dns_digitalocean_token={{certbot_dns_key_secret}} +{% endif %} diff --git a/templates/renew_hook.j2 b/templates/renew_hook.j2 deleted file mode 100644 index 4cdd0a7..0000000 --- a/templates/renew_hook.j2 +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -# {{ ansible_managed }} - -{% for item in certbot_create_dns_renew_hook_services %} - -{% if item == 'haproxy' %} -echo $RENEWED_LINEAGE > /tmp/RENEWED_LINEAGE.certbot.txt -echo $RENEWED_DOMAINS > /tmp/RENEWED_DOMAINS.certbot.txt -{% endif %} - -{% endfor %}