From e0b88c6dadf72ead851fed0c20042dc525264872 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Sat, 19 Jul 2025 11:29:56 +0100 Subject: [PATCH] Support any provided plugin via auto-installation --- README.md | 157 ++++++++++++++++++++++++++++++++++++++++++- defaults/main.yaml | 7 +- tasks/cert.yaml | 5 +- tasks/install.yaml | 34 ++++++---- vars/os/Debian.yaml | 3 + vars/os/RedHat/9.yml | 7 -- 6 files changed, 186 insertions(+), 27 deletions(-) delete mode 100644 vars/os/RedHat/9.yml diff --git a/README.md b/README.md index 8aaeff0..2f9d648 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,157 @@ -# Ansible Certbot +# Ansible Certbot Role +An Ansible role for installing Certbot and automatically requesting Let's Encrypt SSL certificates with support for multiple DNS providers and authentication methods. + +## Description + +This role automates the installation and configuration of Certbot, the official Let's Encrypt client. It supports multiple certificate acquisition methods including webroot validation and DNS challenges through various providers like DigitalOcean, GoDaddy, and AWS Route53. + +## Features + +- Installs Certbot and required plugins +- Supports multiple DNS providers for domain validation +- Automatic certificate renewal via systemd timer +- Certificate expansion for adding new domains +- Flexible plugin installation (package manager or PyPI) +- Automatic FQDN certificate generation option + +## Requirements + +- Ansible 2.9+ +- Target systems: Linux distributions with systemd support +- For DNS challenges: appropriate DNS provider credentials + +## Role Variables + +### Required Variables + +| Variable | Description | Default | +| --------------------- | -------------------------------------------- | ------------------------------ | +| `certbot_certs_email` | Email address for Let's Encrypt registration | `root@{{ ansible_inventory }}` | + +### Certificate Configuration + +| Variable | Description | Default | +| --------------------------- | ---------------------------------------- | ------- | +| `certbot_certs` | List of certificates to request | `[]` | +| `certbot_request_fqdn_cert` | Automatically request cert for host FQDN | `true` | + +### Plugin Configuration + +| Variable | Description | Default | +| -------------------------------- | -------------------------------------------------------- | -------------- | +| `certbot_plugins_source` | Plugin installation source (`package_manager` or `pypi`) | `pypi` | +| `certbot_plugins_package_prefix` | Prefix for plugin package names | `certbot-dns-` | + +### DNS Provider Credentials + +| Variable | Description | Default | +| ---------------------------- | ----------------------------------------- | ----------- | +| `certbot_digitalocean_token` | DigitalOcean API token for DNS challenges | `undefined` | + +### System Configuration + +| Variable | Description | Default | +| ----------------------- | --------------------------------- | --------------- | +| `certbot_packages` | Base certbot packages to install | `[certbot]` | +| `certbot_timer_service` | Systemd timer service for renewal | `certbot.timer` | + +### Plugin Arguments + +The `certbot_plugin_arguments` dictionary defines command-line arguments for different authentication methods: + +```yaml +certbot_plugin_arguments: + digitalocean: --dns-digitalocean --dns-digitalocean-credentials /root/do_secrets.ini + godaddy: --authenticator dns-godaddy --dns-godaddy-credentials /root/gd_secrets.ini + route53: --dns-route53 + default: "--webroot -w /var/www/acme-challenge" +``` + +## Usage Examples + +### Basic Webroot Validation + +```yaml +- hosts: webservers + roles: + - ansible-certbot + vars: + certbot_certs_email: admin@example.com + certbot_certs: + - hostname: example.com + sans: + - www.example.com +``` + +### DigitalOcean DNS Challenge + +```yaml +- hosts: servers + roles: + - ansible-certbot + vars: + certbot_certs_email: admin@example.com + certbot_digitalocean_token: "your_do_token_here" + certbot_certs: + - hostname: example.com + plugin: digitalocean + sans: + - "*.example.com" + - www.example.com +``` + +### Multiple Certificates with Different Plugins + +```yaml +- hosts: servers + roles: + - ansible-certbot + vars: + certbot_certs_email: admin@example.com + certbot_digitalocean_token: "your_do_token_here" + certbot_certs: + - hostname: api.example.com + plugin: digitalocean + - hostname: blog.example.com + plugin: default # Uses webroot + - hostname: shop.example.com + plugin: route53 + extra_arguments: "--dns-route53-propagation-seconds 60" +``` + +### Custom Plugin Installation + +```yaml +- hosts: servers + roles: + - ansible-certbot + vars: + certbot_plugins_source: package_manager + certbot_plugins_package_prefix: python3-certbot-dns- + certbot_certs: + - hostname: example.com + plugin: cloudflare +``` + +## Certificate Configuration Format + +Each certificate in the `certbot_certs` list should follow this format: + +```yaml +certbot_certs: + - hostname: primary.domain.com # Required: Primary domain name + plugin: digitalocean # Optional: Authentication plugin + sans: # Optional: Subject Alternative Names + - www.primary.domain.com + - alt.domain.com + extra_arguments: "--key-type rsa" # Optional: Additional certbot arguments +``` + +## Dependencies + +- `community.crypto` collection (for certificate information parsing) + +## License + +MIT diff --git a/defaults/main.yaml b/defaults/main.yaml index abbc4ba..92670a6 100644 --- a/defaults/main.yaml +++ b/defaults/main.yaml @@ -12,10 +12,7 @@ certbot_plugin_arguments: certbot_packages: - certbot -certbot_extension_packages: - - python3-certbot-dns-digitalocean - - python3-certbot-dns-route53 - -certbot_extension_pypi_packages: [] +certbot_plugins_source: pypi +certbot_plugins_package_prefix: certbot-dns- certbot_timer_service: certbot.timer \ No newline at end of file diff --git a/tasks/cert.yaml b/tasks/cert.yaml index 9dfdfcf..840b07e 100644 --- a/tasks/cert.yaml +++ b/tasks/cert.yaml @@ -3,11 +3,14 @@ ansible.builtin.stat: path: "/etc/letsencrypt/live/{{ item.hostname }}/cert.pem" register: cert_stat + check_mode: false - name: Certbot - Get the SANs from the certificate file community.crypto.x509_certificate_info: path: "/etc/letsencrypt/live/{{ item.hostname }}/cert.pem" register: cert_info + changed_when: false + check_mode: false when: cert_stat.stat.exists - name: Certbot - Calculate the SAN list @@ -15,6 +18,6 @@ cert_sans: "{{ ['DNS:'] | product(item.sans | default([item.hostname])) | map('join') | list }}" - name: Certbot - Request a certificate # noqa no-changed-when ignore-errors - ansible.builtin.command: "certbot certonly -n --expand --agree-tos {{ certbot_plugin_arguments[item.plugin | default('default')] }} -d '{{ item.hostname }}' {% for san in item.sans | default([]) %} -d '{{ san }}' {% endfor %} -m {{ certbot_certs_email }}" # noqa no-change-when + ansible.builtin.command: "certbot certonly -n --expand --agree-tos {{ certbot_plugin_arguments[item.plugin | default('default')] | default('') }} -d '{{ item.hostname }}' {% for san in item.sans | default([]) %} -d '{{ san }}' {% endfor %} -m {{ certbot_certs_email }}{% if item.extra_arguments is defined %} {{ item.extra_arguments }}{% endif %}" # noqa no-change-when ignore_errors: true when: not cert_stat.stat.exists or cert_sans | difference(cert_info.subject_alt_name) | list | length > 0 diff --git a/tasks/install.yaml b/tasks/install.yaml index 54dbd43..2f5d1f9 100644 --- a/tasks/install.yaml +++ b/tasks/install.yaml @@ -4,23 +4,31 @@ name: "{{ certbot_packages }}" state: present -- name: Certbot - Install certbot extensions (package manager) - ansible.builtin.package: - name: "{{ certbot_extension_packages }}" - state: present - when: - - certbot_extension_packages | length +- name: Certbot - Build plugin list + set_fact: + _certbot_plugins_used: "{{ certbot_certs | map(attribute='plugin') | list | unique | map('format', certbot_plugins_package_prefix + '%s') | list }}" -- name: Certbot - Install certbot extensions (pypi) - ansible.builtin.pip: - name: "{{ certbot_extension_pypi_packages }}" - state: present - when: - - certbot_extension_pypi_packages | length +- name: Certbot - Install extension packages + when: + - _certbot_plugins_used | length > 0 + block: + - name: Certbot - Install certbot extensions (package manager) + ansible.builtin.package: + name: "{{ _certbot_plugins_used }}" + state: present + when: + - certbot_plugins_source == 'package_manager' + + - name: Certbot - Install certbot extensions (pypi) + ansible.builtin.pip: + name: "{{ _certbot_plugins_used }}" + state: present + when: + - certbot_plugins_source == 'pypi' - name: Certbot - Enable certbot renewal timer ansible.builtin.systemd: name: "{{ certbot_timer_service }}" state: started enabled: true - when: certbot_timer_service \ No newline at end of file + when: certbot_timer_service diff --git a/vars/os/Debian.yaml b/vars/os/Debian.yaml index ed664bf..c1db40d 100644 --- a/vars/os/Debian.yaml +++ b/vars/os/Debian.yaml @@ -1,3 +1,6 @@ --- certbot_packages: - python3-certbot + +certbot_plugins_source: package_manager +certbot_plugins_package_prefix: python3-certbot-dns- \ No newline at end of file diff --git a/vars/os/RedHat/9.yml b/vars/os/RedHat/9.yml deleted file mode 100644 index 61d80d7..0000000 --- a/vars/os/RedHat/9.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -certbot_extension_packages: [] - -certbot_extension_pypi_packages: - - certbot-dns-digitalocean - -certbot_timer_service: certbot-renew.timer \ No newline at end of file