From b3d8e46b3d8fb69f76df99dc6a80872ada209784 Mon Sep 17 00:00:00 2001 From: Lukas Koenen <lukas.koenen@h-da.de> Date: Thu, 20 Apr 2023 18:27:14 +0200 Subject: [PATCH] feat: dns challenge --- README.md | 16 ++- defaults/main.yml | 15 ++- ...ages.yml => CentOS-packages-webserver.yml} | 0 ...S-timer.yml => CentOS-timer-webserver.yml} | 0 ...ages.yml => Debian-packages-webserver.yml} | 0 ...n-timer.yml => Debian-timer-webserver.yml} | 0 ...ages.yml => Fedora-packages-webserver.yml} | 0 ...a-timer.yml => Fedora-timer-webserver.yml} | 0 ...ages.yml => Ubuntu-packages-webserver.yml} | 0 ...u-timer.yml => Ubuntu-timer-webserver.yml} | 0 tasks/dns-challenge.yml | 99 +++++++++++++++++++ tasks/main.yml | 62 ++---------- tasks/webserver.yml | 54 ++++++++++ templates/dns-challenge.env.j2 | 5 + templates/dns-challenge.service.j2 | 12 +++ templates/dns-challenge.timer.j2 | 13 +++ vars/main.yml | 6 +- 17 files changed, 220 insertions(+), 62 deletions(-) rename tasks/{CentOS-packages.yml => CentOS-packages-webserver.yml} (100%) rename tasks/{CentOS-timer.yml => CentOS-timer-webserver.yml} (100%) rename tasks/{Debian-packages.yml => Debian-packages-webserver.yml} (100%) rename tasks/{Debian-timer.yml => Debian-timer-webserver.yml} (100%) rename tasks/{Fedora-packages.yml => Fedora-packages-webserver.yml} (100%) rename tasks/{Fedora-timer.yml => Fedora-timer-webserver.yml} (100%) rename tasks/{Ubuntu-packages.yml => Ubuntu-packages-webserver.yml} (100%) rename tasks/{Ubuntu-timer.yml => Ubuntu-timer-webserver.yml} (100%) create mode 100644 tasks/dns-challenge.yml create mode 100644 tasks/webserver.yml create mode 100644 templates/dns-challenge.env.j2 create mode 100644 templates/dns-challenge.service.j2 create mode 100644 templates/dns-challenge.timer.j2 diff --git a/README.md b/README.md index db51b01..2d91e95 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 0daf2e5..43d75cc 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 0000000..ff0cb99 --- /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 3cc640a..a97d708 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 0000000..96e6074 --- /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 0000000..efe3629 --- /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 0000000..be5ca9e --- /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 0000000..382878a --- /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 d12eca5..4a6a30f 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 ') }}" -- GitLab