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
No related branches found
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
# requirements.yaml
roles:
- name: hdacloud.certbot
- name: hdacloud.certs
src: git+https://code.fbi.h-da.de/hdacloud/ansible_certbot
version: main
```
#### Example using DNS challenge with designate provider
```yaml
- hosts: all
roles:
- hdacloud.certbot
tasks:
- 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)
[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
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 }}"
# Packages / timer names based on Distribution
certbot_pkg_name:
CentOS: "certbot"
Debian: "certbot"
Ubuntu: "certbot"
Fedora: "certbot"
# 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:
- example.de
certbot_admin_email: "admin@example.de"
# certbot_webroot: "/var/www/example" # if undefined use --standalone
# certbot settings
dns_challenge: false # default use webserver, true to obtain certificate using dns challenge
lego_version: "4.11.0" # lego version - Let's Encrypt client
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
# 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
cert_fqdns:
- example.de
admin_email: "admin@example.de"
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
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"
url: "https://github.com/go-acme/lego/releases/download/v{{ lego_version }}/lego_v{{ lego_version }}_linux_amd64.tar.gz"
dest: "/tmp/lego_v{{ 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"
src: "/tmp/lego_v{{ lego_version }}_linux_amd64.tar.gz"
dest: "/tmp"
remote_src: true
- name: Copy Binary
ansible.builtin.copy:
src: /tmp/lego
dest: /usr/bin/lego
dest: /usr/local/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
- name: Create lego directories
become: true
- name: Ensure Deploy Hook Dir Exists
ansible.builtin.file:
path: "{{ item }}"
state: directory
path: "{{ lego_path }}/renewal-hooks" # all intermediate subdirectories will be created
mode: "0755"
owner: root
group: root
loop:
- /etc/letsencrypt/renewal-hooks
- /etc/letsencrypt/renewal-hooks/deploy
become: true
- name: Render deploy hook script
ansible.builtin.template:
src: templates/renew-hook.sh.j2
dest: /etc/letsencrypt/renewal-hooks/deploy/create-fullchain.sh
mode: '0755'
- name: Check Whether Cert Exists
ansible.builtin.stat:
path: "{{ lego_path }}/certificates/{{ cert_fqdns_first }}.crt"
register: lego_cert
become: true
- name: Request Cert If Necessary - DNS Challenge
when: not lecert.stat.exists
when: not lego_cert.stat.exists
become: true
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 --no-bundle
environment: "{{ dns_provider_auth_env_variables }}"
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
ansible.builtin.command: >-
lego -a --dns {{ lego_dns_provider }}
--email {{ admin_email }} -d {{ lego_dflag }}
--path {{ lego_path }}
run {% for f in lego_extra_flags %}{{ f }} {% endfor %}
environment: "{{ dns_provider_auth_env_variables }}"
register: lego
changed_when: lego.rc == 0
- name: Render Systemd Files
become: true
......@@ -93,18 +59,18 @@
- name: Render Systemd Service File
ansible.builtin.template:
src: templates/dns-challenge.service.j2
dest: /lib/systemd/system/dns-challenge.service
dest: /etc/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
dest: /etc/systemd/system/dns-challenge.timer
mode: "0644"
- name: Setup Certbot With Application
ansible.builtin.include_tasks: "application.yml"
when: certbot_application is defined
- name: Setup renewal hook
ansible.builtin.include_tasks: "renewal-hook.yml"
when: renewal_hook is defined
- name: Enable LEGO Renew Timer
ansible.builtin.systemd:
......
---
- name: Obtain Cert Using Web Server
ansible.builtin.include_tasks: "webserver.yml"
when: not certbot_dns_challenge
when: not dns_challenge
- name: Obtain Cert DNS Challenge
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
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
ansible.builtin.stat:
......@@ -9,20 +28,17 @@
- name: Ensure Deploy Hook Dir Exists
ansible.builtin.file:
path: "{{ item }}"
path: /etc/letsencrypt/renewal-hooks/deploy
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 }}
--email {{ admin_email }} -d {{ certbot_dflag }}
when: not lecert.stat.exists and not certbot_webroot is defined
register: cbstandalone
changed_when: cbstandalone.rc == 0
......@@ -40,15 +56,19 @@
- 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 }}
--email {{ 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: Setup Certbot renewal hook
ansible.builtin.include_tasks: "renewal-hook.yml"
when: renewal_hook is defined
- 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
[Service]
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
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 "" }}
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 ''}}
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
| 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 ') }}"
# Certbot
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