diff --git a/README.md b/README.md index db51b018ae441cd282c16481eaef5e78784b6bee..2d91e957d95e9ce876b7e539139da878c7f7c803 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,26 @@ Role Variables -------------- ```yaml +--- # certbot settings +certbot_dns_challenge: false # default use webserver, true to obtain certificate using dns challenge +certbot_lego_version: "4.11.0" # version of LEGO client +certbot_dns_provider: "designate" # list of available providers https://go-acme.github.io/lego/dns/ +dns_provider_auth_env_variables: # variables required to authenticate dns provider + OS_AUTH_URL: "https://openstack.example.org" + OS_REGION_NAME: "RegionOne" + OS_AUTH_TYP: "v3applicationcredential" # default is to use application credential rather than password + OS_APPLICATION_CREDENTIAL_ID: "{{ vault_os_application_credential_id }}" + OS_APPLICATION_CREDENTIAL_SECRET: "{{ vault_os_application_credential_secret }}" + + certbot_fqdn: - example.de certbot_admin_email: "admin@example.de" -# certbot_webroot: "/var/www/example" # if undefined use --standalone +# certbot_webroot: "/var/www/example" # if undefined use --standalone # application settings -# certbot_application: "example" # if defined copy certs to application dir and setup deploy hooks +# certbot_application: "example" # if defined copy certs to application dir and setup deploy hooks certbot_application_dir: "/etc/{{ certbot_application }}" certbot_application_deploy_hook: | #!/bin/sh diff --git a/defaults/main.yml b/defaults/main.yml index 0daf2e52cdb1617acb59f0d7bd6062fc84aa1243..43d75cc8713c356bc6ac167c2017eeb40f026d8e 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,12 +1,23 @@ --- # certbot settings +certbot_dns_challenge: false # default use webserver, true to obtain certificate using dns challenge +certbot_lego_version: "4.11.0" # lego version - Let's Encrypt client +certbot_dns_provider: "designate" # list of available providers https://go-acme.github.io/lego/dns/ +dns_provider_auth_env_variables: # variables required to authenticate dns provider + OS_AUTH_URL: "https://openstack.example.org" + OS_REGION_NAME: "RegionOne" + OS_AUTH_TYP: "v3applicationcredential" # default is to use application credential rather than password + OS_APPLICATION_CREDENTIAL_ID: "{{ vault_os_application_credential_id }}" + OS_APPLICATION_CREDENTIAL_SECRET: "{{ vault_os_application_credential_secret }}" + + certbot_fqdn: - example.de certbot_admin_email: "admin@example.de" -# certbot_webroot: "/var/www/example" # if undefined use --standalone +# certbot_webroot: "/var/www/example" # if undefined use --standalone # application settings -# certbot_application: "example" # if defined copy certs to application dir and setup deploy hooks +# certbot_application: "example" # if defined copy certs to application dir and setup deploy hooks certbot_application_dir: "/etc/{{ certbot_application }}" certbot_application_deploy_hook: | #!/bin/sh diff --git a/tasks/CentOS-packages.yml b/tasks/CentOS-packages-webserver.yml similarity index 100% rename from tasks/CentOS-packages.yml rename to tasks/CentOS-packages-webserver.yml diff --git a/tasks/CentOS-timer.yml b/tasks/CentOS-timer-webserver.yml similarity index 100% rename from tasks/CentOS-timer.yml rename to tasks/CentOS-timer-webserver.yml diff --git a/tasks/Debian-packages.yml b/tasks/Debian-packages-webserver.yml similarity index 100% rename from tasks/Debian-packages.yml rename to tasks/Debian-packages-webserver.yml diff --git a/tasks/Debian-timer.yml b/tasks/Debian-timer-webserver.yml similarity index 100% rename from tasks/Debian-timer.yml rename to tasks/Debian-timer-webserver.yml diff --git a/tasks/Fedora-packages.yml b/tasks/Fedora-packages-webserver.yml similarity index 100% rename from tasks/Fedora-packages.yml rename to tasks/Fedora-packages-webserver.yml diff --git a/tasks/Fedora-timer.yml b/tasks/Fedora-timer-webserver.yml similarity index 100% rename from tasks/Fedora-timer.yml rename to tasks/Fedora-timer-webserver.yml diff --git a/tasks/Ubuntu-packages.yml b/tasks/Ubuntu-packages-webserver.yml similarity index 100% rename from tasks/Ubuntu-packages.yml rename to tasks/Ubuntu-packages-webserver.yml diff --git a/tasks/Ubuntu-timer.yml b/tasks/Ubuntu-timer-webserver.yml similarity index 100% rename from tasks/Ubuntu-timer.yml rename to tasks/Ubuntu-timer-webserver.yml diff --git a/tasks/dns-challenge.yml b/tasks/dns-challenge.yml new file mode 100644 index 0000000000000000000000000000000000000000..ff0cb998e64e7b28d46fa9a8d7c4c4b44d38e496 --- /dev/null +++ b/tasks/dns-challenge.yml @@ -0,0 +1,99 @@ +- name: Fetch Binary + ansible.builtin.get_url: + url: "https://github.com/go-acme/lego/releases/download/v{{ certbot_lego_version }}/lego_v{{ certbot_lego_version }}_linux_amd64.tar.gz" + dest: "/tmp/lego_v{{ certbot_lego_version }}_linux_amd64.tar.gz" + mode: "0644" + +- name: Unpack Archive + ansible.builtin.unarchive: + src: "/tmp/lego_v{{ certbot_lego_version }}_linux_amd64.tar.gz" + dest: "/tmp" + remote_src: true + +- name: Copy Binary + ansible.builtin.copy: + src: /tmp/lego + dest: /usr/bin/lego + owner: root + group: root + mode: '0755' + remote_src: true + become: true + +- name: Check Whether Cert Exists + ansible.builtin.stat: + path: "{{ certbot_live_dir }}/fullchain.pem" + register: lecert + become: true + +- name: Ensure Deploy Hook Dir Exists + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: root + group: root + loop: + - /etc/letsencrypt/renewal-hooks + - /etc/letsencrypt/renewal-hooks/deploy + become: true + +- name: Request Cert If Necessary - DNS Challenge + block: + - name: Request Cert + ansible.builtin.command: >- + lego -a --dns {{ certbot_dns_provider }} + --email {{ certbot_admin_email }} -d {{ lego_dflag }} + --path {{ certbot_live_dir }} + run + environment: "{{ dns_provider_auth_env_variables }}" + register: lego + changed_when: lego.rc == 0 + + - name: Mirror Letsencrypt Structure + ansible.builtin.copy: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + owner: root + group: root + mode: '0600' + remote_src: true + loop: + - { src: "{{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.crt", dest: "{{ certbot_live_dir }}/fullchain.pem" } + - { src: "{{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.key", dest: "{{ certbot_live_dir }}/privkey.pem" } + + when: not lecert.stat.exists + become: true + +- name: Render Systemd Files + block: + - name: Render Systemd Environment File + ansible.builtin.template: + src: templates/dns-challenge.env.j2 + dest: /etc/default/dns-challenge.env + mode: "0644" + + - name: Render Systemd Service File + ansible.builtin.template: + src: templates/dns-challenge.service.j2 + dest: /lib/systemd/system/dns-challenge.service + mode: "0644" + + - name: Render Systemd Timer File + ansible.builtin.template: + src: templates/dns-challenge.timer.j2 + dest: /lib/systemd/system/dns-challenge.timer + mode: "0644" + + become: true + +- name: Setup Certbot With Application + ansible.builtin.include_tasks: "application.yml" + when: certbot_application is defined + +- name: Enable LEGO Renew Timer + ansible.builtin.systemd: + name: dns-challenge.timer + state: started + enabled: true + become: true diff --git a/tasks/main.yml b/tasks/main.yml index 3cc640a31d76361ef1fc41cb1c2a1ab1233b7ef6..a97d708121d71398f8e54f12ca9ae0962ee188e1 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,58 +1,8 @@ --- -# TODO: -# - dns challenge +- name: Obtain Cert Using Web Server + ansible.builtin.include_tasks: "webserver.yml" + when: not certbot_dns_challenge -- name: Install Packages Based On Distribution - ansible.builtin.include_tasks: "{{ ansible_facts.distribution }}-packages.yml" - -- name: Check Whether Cert Exists - ansible.builtin.stat: - path: "{{ certbot_live_dir }}/cert.pem" - register: lecert - become: true - -- name: Ensure Deploy Hook Dir Exists - ansible.builtin.file: - path: "{{ item }}" - state: directory - mode: "0755" - owner: root - group: root - loop: - - /etc/letsencrypt/renewal-hooks - - /etc/letsencrypt/renewal-hooks/deploy - become: true - -- name: Request Cert If Necessary - Standalone - ansible.builtin.command: >- - certbot certonly --standalone --noninteractive --agree-tos - --email {{ certbot_admin_email }} -d {{ certbot_dflag }} - when: not lecert.stat.exists and not certbot_webroot is defined - register: cbstandalone - changed_when: cbstandalone.rc == 0 - become: true - -- name: Ensure Webroot Path Exists - ansible.builtin.file: - path: "{{ certbot_webroot }}" - state: directory - mode: "0755" - owner: root - group: root - when: not lecert.stat.exists and certbot_webroot is defined - -- name: Request Cert If Necessary - Webroot - ansible.builtin.command: >- - certbot certonly --webroot --webroot-path {{ certbot_webroot }} --noninteractive --agree-tos - --email {{ certbot_admin_email }} -d {{ certbot_dflag }} - when: not lecert.stat.exists and certbot_webroot is defined - register: cbwebroot - changed_when: cbwebroot.rc == 0 - become: true - -- name: Setup Certbot With Application - ansible.builtin.include_tasks: "application.yml" - when: certbot_application is defined - -- name: Enable Letsencrypt Renew Timer Based On Distribution - ansible.builtin.include_tasks: "{{ ansible_facts.distribution }}-timer.yml" +- name: Obtain Cert DNS Challenge + ansible.builtin.include_tasks: "dns-challenge.yml" + when: certbot_dns_challenge diff --git a/tasks/webserver.yml b/tasks/webserver.yml new file mode 100644 index 0000000000000000000000000000000000000000..96e60749ca9688e3a8a7d841435aad74a88a8051 --- /dev/null +++ b/tasks/webserver.yml @@ -0,0 +1,54 @@ +- name: Install Packages Based On Distribution + ansible.builtin.include_tasks: "{{ ansible_facts.distribution }}-packages-webserver.yml" + +- name: Check Whether Cert Exists + ansible.builtin.stat: + path: "{{ certbot_live_dir }}/cert.pem" + register: lecert + become: true + +- name: Ensure Deploy Hook Dir Exists + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: root + group: root + loop: + - /etc/letsencrypt/renewal-hooks + - /etc/letsencrypt/renewal-hooks/deploy + become: true + +- name: Request Cert If Necessary - Standalone + ansible.builtin.command: >- + certbot certonly --standalone --noninteractive --agree-tos + --email {{ certbot_admin_email }} -d {{ certbot_dflag }} + when: not lecert.stat.exists and not certbot_webroot is defined + register: cbstandalone + changed_when: cbstandalone.rc == 0 + become: true + +- name: Ensure Webroot Path Exists + ansible.builtin.file: + path: "{{ certbot_webroot }}" + state: directory + mode: "0755" + owner: root + group: root + when: not lecert.stat.exists and certbot_webroot is defined + +- name: Request Cert If Necessary - Webroot + ansible.builtin.command: >- + certbot certonly --webroot --webroot-path {{ certbot_webroot }} --noninteractive --agree-tos + --email {{ certbot_admin_email }} -d {{ certbot_dflag }} + when: not lecert.stat.exists and certbot_webroot is defined + register: cbwebroot + changed_when: cbwebroot.rc == 0 + become: true + +- name: Setup Certbot With Application + ansible.builtin.include_tasks: "application.yml" + when: certbot_application is defined + +- name: Enable Letsencrypt Renew Timer Based On Distribution + ansible.builtin.include_tasks: "{{ ansible_facts.distribution }}-timer-webserver.yml" diff --git a/templates/dns-challenge.env.j2 b/templates/dns-challenge.env.j2 new file mode 100644 index 0000000000000000000000000000000000000000..efe3629545669c7502a3bcbf42f318cf7361ae7d --- /dev/null +++ b/templates/dns-challenge.env.j2 @@ -0,0 +1,5 @@ +# {{ ansible_managed }} + +{% for var, val in dns_provider_auth_env_variables.items() %} +{{ var }}={{ val }} +{% endfor %} diff --git a/templates/dns-challenge.service.j2 b/templates/dns-challenge.service.j2 new file mode 100644 index 0000000000000000000000000000000000000000..be5ca9e6101de2cdf7bf238ad53f18417ca26e06 --- /dev/null +++ b/templates/dns-challenge.service.j2 @@ -0,0 +1,12 @@ +# {{ ansible_managed }} + +[Unit] +Description=LEGO DNS challenge + +[Service] +Type=oneshot +ExecStart=/usr/bin/lego -a --dns {{ certbot_dns_provider }} --email {{ certbot_admin_email }} -d {{ lego_dflag }} --path {{ certbot_live_dir }} renew +ExecStartPost=cp {{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.crt {{ certbot_live_dir }}/fullchain.pem +ExecStartPost=cp {{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.key {{ certbot_live_dir }}/privkey.pem +{{ "ExecStartPost=/etc/letsencrypt/renewal-hooks/deploy/" + certbot_application if certbot_application is defined else "" }} +EnvironmentFile=/etc/default/dns-challenge.env diff --git a/templates/dns-challenge.timer.j2 b/templates/dns-challenge.timer.j2 new file mode 100644 index 0000000000000000000000000000000000000000..382878a3a4303bd7d8a5dcf0a025202bac1efb66 --- /dev/null +++ b/templates/dns-challenge.timer.j2 @@ -0,0 +1,13 @@ +# {{ ansible_managed }} + +[Unit] +Description=Run LEGO DNS challenge twice every day + +[Timer] +OnCalendar=*-*-* 00,12:00:00 +RandomizedDelaySec=43200 +Persistent=true +Unit=dns-challenge.service + +[Install] +WantedBy=timers.target diff --git a/vars/main.yml b/vars/main.yml index d12eca560203f5505c97f30a0a9bf820ad5f1208..4a6a30f51c56635de58b4bdf66854a2ae3a05bbb 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -1,9 +1,11 @@ --- -certbot_live_dir: >- - /etc/letsencrypt/live/{{ +certbot_fqdn_first: >- + {{ certbot_fqdn | first | replace("*.", "") | trim(".") }} +certbot_live_dir: "/etc/letsencrypt/live/{{ certbot_fqdn_first }}" certbot_dflag: "{{ certbot_fqdn | map('trim', '.') | join(',') }}" +lego_dflag : "{{ certbot_fqdn | map('trim', '.') | join(' -d ') }}"