Skip to content
Snippets Groups Projects
Commit 5f4c6a35 authored by Lars Seipel's avatar Lars Seipel
Browse files

Merge branch 'dns-do-not-force-fullchain-script' into 'main'

cleanup: remove distribution specific task files and strip dns renewal hook

See merge request !5
parents 4d1a8ab8 026672dd
Branches main
No related tags found
1 merge request!5cleanup: remove distribution specific task files and strip dns renewal hook
Pipeline #185833 passed
Showing
with 174 additions and 240 deletions
ansible-certbot # hdacloud.certs
=========
Deploy certbot and enable auto-renew. Role to obtain certificates from Let's Encrypt using either [certbot][certbot] or [lego-acme][lego-acme].
Refer to the variables below to obtain certificates.
Role Variables ## Role Variables
--------------
The role variables are split up into three categories. A general one and two specialized sets of variables
depending on whether or not you are using `dns_challenge` or not.
### General Settings
| Variable | Description | Type | Default |
|---|---|---|---|
| `cert_fqdns` | List of FQDNs the certificate should be valid for | `List<string>` | `[]` |
| `admin_email` | Email for expiry notifications | `string` | `''` |
| `dns_challenge` | Whether to use a dns challenge for obtaining certificates. Refer to 'DNS challenge settings' if true, otherwise to section 'WebServer Settings' | `boolean` | `false` |
| `renewal_hook_file_name` | Filename of the renewal script file | `string` | `''` |
| `renewal_hook` | Contents of the renewal script. this should be used to restart services after a new certificate has been obtained. | `string` | `''` |
### DNS Challenge: `true`
These settings need to be provided when a DNS challenge is used.
| Variable | Description | Type | Default |
|---|---|---|---|
| `lego_version` | The version of the [lego-acme][lego-acme] client binary to be used. | `string` | `4.11.0` |
| `lego_extra_flags` | Additional arguments / flags to be passed to the `lego` command. | `List<string>` | `[]` |
| `lego_dns_provider` | The DNS provider to be used for the DNS challenge. A full list of the supported providers can be found [here][lego-dns-providers]. | `string` | `designate` |
| `dns_provider_auth_env_variables` | Dictionary of environment variables used by the selected DNS provider. | `Dictionary<string, string>` | `{}` |
```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
# application settings
# 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
cp {{ certbot_live_dir }}/fullchain.pem {{ certbot_application_dir }}
cp {{ certbot_live_dir }}/privkey.pem {{ certbot_application_dir }}
systemctl restart {{ certbot_application }}.service
```
Example Playbook ### DNS Challenge: `false`
----------------
These settings optionally need to be provided when no DNS challenge is used.
| Variable | Description | Type | Default |
|---|---|---|---|
| `certbot_webroot` | If you are already running a web server on your instance provide a path that is served on port `80` here, to allow the HTTP challenge to be completed when obtaining the certificate. | `string` | `undefined` |
## Example Playbook
```yaml ```yaml
# requirements.yaml # requirements.yaml
roles: roles:
- name: hdacloud.certbot - name: hdacloud.certs
src: git+https://code.fbi.h-da.de/hdacloud/ansible_certbot src: git+https://code.fbi.h-da.de/hdacloud/ansible_certbot
version: main version: main
``` ```
#### Example using DNS challenge with designate provider
```yaml ```yaml
- hosts: all - hosts: all
roles: tasks:
- hdacloud.certbot - name: Obtain certificate
ansible.builtin.import_role:
name: hdacloud.certs
vars:
cert_fqdns:
- "{{ my_fqdn }}"
admin_email: "admin@example.org"
dns_challenge: true
lego_version: "4.11.0"
lego_dns_provider: "designate"
dns_provider_auth_env_variables:
OS_AUTH_URL: "https://h-da.cloud:13000"
OS_REGION_NAME: "eu-central"
OS_AUTH_TYP: "v3applicationcredential"
OS_APPLICATION_CREDENTIAL_ID: "{{ os_application_credential_id }}"
OS_APPLICATION_CREDENTIAL_SECRET: "{{ os_application_credential_secret }}"
renewal_hook: |
#!/usr/bin/env bash
systemctl restart httpd
```
### Info when using DNS challenge
In some cases you might need the certificates split up into the cert itself and a separate fullchain certificate
chain. For this case you need to add the `--no-bundle` option to the `lego_extra_flags` array. Then add the
following to the `renewal_hook` script (replacing variables accordingly):
```sh
rm -f /var/lib/lego/fullchain.pem
cat /var/lib/lego/certificates/<cert_fqdns_first>.crt >> <wherever>/fullchain.pem &&
cat /var/lib/lego/certificates/<cert_fqdns_first>.issuer.crt >> <wherever>/fullchain.pem
``` ```
License # License
-------
See [LICENSE](LICENSE) See [LICENSE](LICENSE)
[lego-acme]: https://go-acme.github.io/lego/ 'LEGO ACME'
[certbot]: https://certbot.eff.org/ 'EFF Certbot'
[lego-dns-providers]: https://go-acme.github.io/lego/dns/#dns-providers 'LEGO: DNS Providers'
--- ---
# certbot settings # Packages / timer names based on Distribution
certbot_dns_challenge: false # default use webserver, true to obtain certificate using dns challenge certbot_pkg_name:
certbot_lego_version: "4.11.0" # lego version - Let's Encrypt client CentOS: "certbot"
certbot_dns_provider: "designate" # list of available providers https://go-acme.github.io/lego/dns/ Debian: "certbot"
dns_provider_auth_env_variables: # variables required to authenticate dns provider Ubuntu: "certbot"
OS_AUTH_URL: "https://openstack.example.org" Fedora: "certbot"
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 }}"
# TODO: Might need to change
certbot_timer_name:
CentOS: "certbot-renew.timer"
Debian: "certbot.timer"
Ubuntu: "snap.certbot-renew.timer"
Fedora: "certbot-renew.timer"
certbot_fqdn: # certbot settings
- example.de dns_challenge: false # default use webserver, true to obtain certificate using dns challenge
certbot_admin_email: "admin@example.de" lego_version: "4.11.0" # lego version - Let's Encrypt client
# certbot_webroot: "/var/www/example" # if undefined use --standalone lego_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
# application settings cert_fqdns:
# certbot_application: "example" # if defined copy certs to application dir and setup deploy hooks - example.de
certbot_application_dir: "/etc/{{ certbot_application }}" admin_email: "admin@example.de"
certbot_application_deploy_hook: |
#!/bin/sh
cp {{ certbot_live_dir }}/fullchain.pem {{ certbot_application_dir }}
cp {{ certbot_live_dir }}/privkey.pem {{ certbot_application_dir }}
systemctl restart {{ certbot_application }}.service # LEGO Settings
lego_extra_flags: []
---
- name: Install Certbot
ansible.builtin.package:
name: # sadly doesn't as a list
- "{{ item }}"
state: present
loop:
- epel-release
- certbot
become: true
---
- name: Enable Letsencrypt Renew Timer
ansible.builtin.systemd:
name: certbot-renew.timer
state: started
enabled: true
become: true
---
- name: Install Certbot
ansible.builtin.apt:
name: certbot
state: present
update_cache: true
become: true
---
- name: Enable Letsencrypt Renew Timer
ansible.builtin.systemd:
name: certbot.timer
state: started
enabled: true
become: true
---
- name: Install Certbot
ansible.builtin.package:
name: certbot
state: present
become: true
---
- name: Enable Letsencrypt Renew Timer
ansible.builtin.systemd:
name: certbot-renew.timer
state: started
enabled: true
become: true
---
- name: Install Certbot
ansible.builtin.apt:
name: certbot
state: present
update_cache: true
become: true
---
- name: Enable Letsencrypt Renew Timer
ansible.builtin.systemd:
name: certbot.timer
state: started
enabled: true
become: true
---
- name: Ensure Application Certificate Directory exists
ansible.builtin.file:
path: "{{ certbot_application_dir }}"
state: directory
owner: root
group: root
mode: "0755"
become: true
- name: Check Whether Application Cert Exists
ansible.builtin.stat:
path: "{{ certbot_application_dir }}/fullchain.pem"
register: leoacert
become: true
- name: Setup Application Deploy Hook
ansible.builtin.copy:
dest: "/etc/letsencrypt/renewal-hooks/deploy/{{ certbot_application }}"
content: "{{ certbot_application_deploy_hook }}"
mode: "0755"
become: true
- name: Copy Application Cert If Necessary
ansible.builtin.copy:
src: "{{ item }}"
dest: "{{ certbot_application_dir }}"
remote_src: true
mode: "0644"
loop:
- "{{ certbot_live_dir }}/fullchain.pem"
- "{{ certbot_live_dir }}/privkey.pem"
when: not leoacert.stat.exists
become: true
- name: Fetch Binary - name: Fetch Binary
ansible.builtin.get_url: 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" url: "https://github.com/go-acme/lego/releases/download/v{{ lego_version }}/lego_v{{ lego_version }}_linux_amd64.tar.gz"
dest: "/tmp/lego_v{{ certbot_lego_version }}_linux_amd64.tar.gz" dest: "/tmp/lego_v{{ lego_version }}_linux_amd64.tar.gz"
mode: "0644" mode: "0644"
- name: Unpack Archive - name: Unpack Archive
ansible.builtin.unarchive: ansible.builtin.unarchive:
src: "/tmp/lego_v{{ certbot_lego_version }}_linux_amd64.tar.gz" src: "/tmp/lego_v{{ lego_version }}_linux_amd64.tar.gz"
dest: "/tmp" dest: "/tmp"
remote_src: true remote_src: true
- name: Copy Binary - name: Copy Binary
ansible.builtin.copy: ansible.builtin.copy:
src: /tmp/lego src: /tmp/lego
dest: /usr/bin/lego dest: /usr/local/bin/lego
owner: root owner: root
group: root group: root
mode: '0755' mode: '0755'
remote_src: true remote_src: true
become: true become: true
- name: Check Whether Cert Exists - name: Create lego directories
ansible.builtin.stat:
path: "{{ certbot_live_dir }}/fullchain.pem"
register: lecert
become: true become: true
- name: Ensure Deploy Hook Dir Exists
ansible.builtin.file: ansible.builtin.file:
path: "{{ item }}"
state: directory state: directory
path: "{{ lego_path }}/renewal-hooks" # all intermediate subdirectories will be created
mode: "0755" mode: "0755"
owner: root owner: root
group: root group: root
loop:
- /etc/letsencrypt/renewal-hooks
- /etc/letsencrypt/renewal-hooks/deploy
become: true
- name: Render deploy hook script - name: Check Whether Cert Exists
ansible.builtin.template: ansible.builtin.stat:
src: templates/renew-hook.sh.j2 path: "{{ lego_path }}/certificates/{{ cert_fqdns_first }}.crt"
dest: /etc/letsencrypt/renewal-hooks/deploy/create-fullchain.sh register: lego_cert
mode: '0755'
become: true become: true
- name: Request Cert If Necessary - DNS Challenge - name: Request Cert If Necessary - DNS Challenge
when: not lecert.stat.exists when: not lego_cert.stat.exists
become: true become: true
block: ansible.builtin.command: >-
- name: Request Cert lego -a --dns {{ lego_dns_provider }}
ansible.builtin.command: >- --email {{ admin_email }} -d {{ lego_dflag }}
lego -a --dns {{ certbot_dns_provider }} --path {{ lego_path }}
--email {{ certbot_admin_email }} -d {{ lego_dflag }} run {% for f in lego_extra_flags %}{{ f }} {% endfor %}
--path {{ certbot_live_dir }} environment: "{{ dns_provider_auth_env_variables }}"
run --no-bundle register: lego
environment: "{{ dns_provider_auth_env_variables }}" changed_when: lego.rc == 0
register: lego
changed_when: lego.rc == 0
- name: Mirror Letsencrypt Structure
block:
- name: Copy cert and key files
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 }}/cert.pem" }
- { src: "{{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.key", dest: "{{ certbot_live_dir }}/privkey.pem" }
- name: Build fullchain.pem file
ansible.builtin.shell:
cmd: >-
cat "{{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.crt" >> "{{ certbot_live_dir }}/fullchain.pem" &&
cat "{{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.issuer.crt" >> "{{ certbot_live_dir }}/fullchain.pem"
changed_when: false
- name: Render Systemd Files - name: Render Systemd Files
become: true become: true
...@@ -93,18 +59,18 @@ ...@@ -93,18 +59,18 @@
- name: Render Systemd Service File - name: Render Systemd Service File
ansible.builtin.template: ansible.builtin.template:
src: templates/dns-challenge.service.j2 src: templates/dns-challenge.service.j2
dest: /lib/systemd/system/dns-challenge.service dest: /etc/systemd/system/dns-challenge.service
mode: "0644" mode: "0644"
- name: Render Systemd Timer File - name: Render Systemd Timer File
ansible.builtin.template: ansible.builtin.template:
src: templates/dns-challenge.timer.j2 src: templates/dns-challenge.timer.j2
dest: /lib/systemd/system/dns-challenge.timer dest: /etc/systemd/system/dns-challenge.timer
mode: "0644" mode: "0644"
- name: Setup Certbot With Application - name: Setup renewal hook
ansible.builtin.include_tasks: "application.yml" ansible.builtin.include_tasks: "renewal-hook.yml"
when: certbot_application is defined when: renewal_hook is defined
- name: Enable LEGO Renew Timer - name: Enable LEGO Renew Timer
ansible.builtin.systemd: ansible.builtin.systemd:
......
--- ---
- name: Obtain Cert Using Web Server - name: Obtain Cert Using Web Server
ansible.builtin.include_tasks: "webserver.yml" ansible.builtin.include_tasks: "webserver.yml"
when: not certbot_dns_challenge when: not dns_challenge
- name: Obtain Cert DNS Challenge - name: Obtain Cert DNS Challenge
ansible.builtin.include_tasks: "dns-challenge.yml" ansible.builtin.include_tasks: "dns-challenge.yml"
when: certbot_dns_challenge when: dns_challenge
---
- name: Setup Renewal Hook
ansible.builtin.copy:
dest: "{{ dns_challenge | ternary(lego_path + '/renewal-hooks/', '/etc/letsencrypt/renewal-hooks/deploy/') }}{{ renewal_hook_file_name }}"
content: "{{ renewal_hook }}"
mode: "0755"
become: true
---
- name: Install Packages Based On Distribution - name: Install Packages Based On Distribution
ansible.builtin.include_tasks: "{{ ansible_facts.distribution }}-packages-webserver.yml" become: true
block:
- name: Install epel-release on CentOS
when: ansible_facts.distribution == "CentOS"
ansible.builtin.yum:
name: epel-release
state: present
- name: Install certbot
ansible.builtin.package:
name: "{{ certbot_pkg_name[ansible_facts.distribution] }}"
state: present
- name: Disable 'epel' again
when: ansible_facts.distribution == "CentOS"
ansible.builtin.yum_repository:
name: epel
state: absent
- name: Check Whether Cert Exists - name: Check Whether Cert Exists
ansible.builtin.stat: ansible.builtin.stat:
...@@ -9,20 +28,17 @@ ...@@ -9,20 +28,17 @@
- name: Ensure Deploy Hook Dir Exists - name: Ensure Deploy Hook Dir Exists
ansible.builtin.file: ansible.builtin.file:
path: "{{ item }}" path: /etc/letsencrypt/renewal-hooks/deploy
state: directory state: directory
mode: "0755" mode: "0755"
owner: root owner: root
group: root group: root
loop:
- /etc/letsencrypt/renewal-hooks
- /etc/letsencrypt/renewal-hooks/deploy
become: true become: true
- name: Request Cert If Necessary - Standalone - name: Request Cert If Necessary - Standalone
ansible.builtin.command: >- ansible.builtin.command: >-
certbot certonly --standalone --noninteractive --agree-tos certbot certonly --standalone --noninteractive --agree-tos
--email {{ certbot_admin_email }} -d {{ certbot_dflag }} --email {{ admin_email }} -d {{ certbot_dflag }}
when: not lecert.stat.exists and not certbot_webroot is defined when: not lecert.stat.exists and not certbot_webroot is defined
register: cbstandalone register: cbstandalone
changed_when: cbstandalone.rc == 0 changed_when: cbstandalone.rc == 0
...@@ -40,15 +56,19 @@ ...@@ -40,15 +56,19 @@
- name: Request Cert If Necessary - Webroot - name: Request Cert If Necessary - Webroot
ansible.builtin.command: >- ansible.builtin.command: >-
certbot certonly --webroot --webroot-path {{ certbot_webroot }} --noninteractive --agree-tos certbot certonly --webroot --webroot-path {{ certbot_webroot }} --noninteractive --agree-tos
--email {{ certbot_admin_email }} -d {{ certbot_dflag }} --email {{ admin_email }} -d {{ certbot_dflag }}
when: not lecert.stat.exists and certbot_webroot is defined when: not lecert.stat.exists and certbot_webroot is defined
register: cbwebroot register: cbwebroot
changed_when: cbwebroot.rc == 0 changed_when: cbwebroot.rc == 0
become: true become: true
- name: Setup Certbot With Application - name: Setup Certbot renewal hook
ansible.builtin.include_tasks: "application.yml" ansible.builtin.include_tasks: "renewal-hook.yml"
when: certbot_application is defined when: renewal_hook is defined
- name: Enable Letsencrypt Renew Timer Based On Distribution - name: Enable Letsencrypt Renew Timer Based On Distribution
ansible.builtin.include_tasks: "{{ ansible_facts.distribution }}-timer-webserver.yml" become: true
ansible.builtin.systemd:
name: "{{ certbot_itmer_name[ansible_facts.distribution] }}"
state: started
enabled: true
...@@ -5,8 +5,5 @@ Description=LEGO DNS challenge ...@@ -5,8 +5,5 @@ Description=LEGO DNS challenge
[Service] [Service]
Type=oneshot Type=oneshot
ExecStart=/usr/bin/lego -a --dns {{ certbot_dns_provider }} --email {{ certbot_admin_email }} -d {{ lego_dflag }} --path {{ certbot_live_dir }} renew --no-bundle --renew-hook /etc/letsencrypt/renewal-hooks/deploy/create-fullchain.sh ExecStart=/usr/local/bin/lego -a --dns {{ lego_dns_provider }} --email {{ admin_email }} -d {{ lego_dflag }} --path {{ lego_path }} renew {% for f in lego_extra_flags %}{{ f }} {% endfor %} {{ '--renew-hook ' + lego_path + '/renewal-hooks/' + renewal_hook_file_name if renewal_hook is defined else ''}}
ExecStartPost=cp {{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.crt {{ certbot_live_dir }}/cert.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 EnvironmentFile=/etc/default/dns-challenge.env
#!/usr/bin/env bash
cat {{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.crt >> {{ certbot_live_dir }}/fullchain.pem
cat {{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.issuer.crt >> {{ certbot_live_dir }}/fullchain.pem
\ No newline at end of file
--- ---
certbot_fqdn_first: >- cert_fqdns_first: >-
{{ {{
certbot_fqdn cert_fqdns
| first | first
| replace("*.", "") | replace("*.", "")
| trim(".") | trim(".")
}} }}
certbot_live_dir: "/etc/letsencrypt/live/{{ certbot_fqdn_first }}"
certbot_dflag: "{{ certbot_fqdn | map('trim', '.') | join(',') }}" # Certbot
lego_dflag: "{{ certbot_fqdn | map('trim', '.') | join(' -d ') }}" certbot_live_dir: "/etc/letsencrypt/live/{{ cert_fqdns_first }}"
certbot_dflag: "{{ cert_fqdns | map('trim', '.') | join(',') }}"
# LEGO (when DNS challenge)
lego_path: /var/lib/lego
lego_dflag: "{{ cert_fqdns | map('trim', '.') | join(' -d ') }}"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment