dhach.acme_letsencrypt
acme-letsencrypt
Overview
This is an Ansible role that lets you:
- Create ECC or RSA private keys
- Generate certificate requests for multiple domains
- Send requests to Let's Encrypt or another ACME provider
- Install files in a specific location
I created this role to obtain Let's Encrypt certificates without needing third-party tools (like certbot or acme-tiny) and to have better control over the process.
Currently, it only supports HTTP Challenge type.
Since this is an Ansible role, it does not automatically renew certificates. To renew them, you can run this role regularly from your CI/CD pipeline or set it up to run in Ansible Pull-mode using a crontab.
Ansible Version
Starting with version 2.0.0, this role is only guaranteed to work with Ansible version 2.10 or higher.
If you need older versions (less than 2.10), use any version tagged with 1.x.x.
Requirements
On the target host, you'll need:
- openssl
Example playbooks
Using a secp384r1 ECC Key for a SAN certificate (for multiple domains):
- name: "Get certificates for webserver01"
hosts: webserver01
become: true
roles:
- dhach.acme_letsencrypt
vars:
le_base_directory: /etc/letsencrypt
le_certificates:
- name: ecc.example.com
domains:
- secp.example.com
- ecc.example.com
key:
curve: secp384r1
Now using a secp256r1 key, force recreation, and request from the Let's Encrypt production server:
- name: "Get certificates for webserver02"
hosts: webserver02
become: true
roles:
- dhach.acme_letsencrypt
vars:
le_acme_directory: https://acme-v02.api.letsencrypt.org/directory
le_certificates:
- name: another-domain.example.com
domains:
- another-domain-abc.example.com
- another-domain-def.example.com
- another-domain-ghi.example.com
- another-domain-jkl.example.com
- another-domain-mno.example.com
ssl:
type: ECC
curve: secp256r1
renew: true
Or using RSA keys to get one certificate for each domain:
- name: "Get certificates for webserver03"
hosts: webserver03
become: true
roles:
- dhach.acme_letsencrypt
vars:
le_certificates:
- name: example.com
domains:
- foo.example.com
key:
type: RSA
size: 4096
- name: more.example.com
domains:
- bar.example.com
key:
type: RSA
size: 4096
Where to find the files
All generated keys and certificates are saved under {{ le_base_directory }}/{{ le_certificates['name'] }}
.
For example, if you use the name 'example.com' and set le_base_directory
to '/etc/letsencrypt/', the files will be located at:
/etc/letsencrypt/example.com/
├── domain.csr
├── domain.key
├── domain.pem
├── fullchain.pem
└── intermediate.pem
How to configure your webserver
ACME servers require you to respond to a challenge by placing a specific file in a defined path that your webserver serves via HTTP.
Your webserver must serve the location /.well-known/acme-challenge with the contents from this directory: {{ le_base_directory }}/.well-known/acme-challenge/
.
Examples using the default le_base_directory
value:
Nginx:
location /.well-known/acme-challenge {
alias /etc/letsencrypt/.well-known/acme-challenge/;
}
Apache:
Alias /.well-known/acme-challenge/ "/etc/letsencrypt/.well-known/acme-challenge/"
<Directory "/etc/letsencrypt/.well-known/acme-challenge/">
AllowOverride None
Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
Require method GET POST OPTIONS
</Directory>
Role Variables and defaults
All configurable variables are listed below, with their default values.
Certificate requests
You can choose for which domains to request certificates and specify details for key generation.
All requests are structured as SAN (SubjectAltName) requests.
ECC:
le_certificates:
- name: example.com
domains:
- foo.example.com
- bar.example.com
key:
type: ECC
curve: secp384r1
renew: false
RSA:
le_certificates:
- name: example.com
domains:
- foo.example.com
- bar.example.com
key:
type: RSA
size: 4096
renew: false
Directories and permissions
le_base_directory
: The main directory for all files (default: /etc/letsencrypt)
le_files_owner
: The owner of the generated files (default: root)
le_files_group
: The group for the generated files (default: root)
Let's Encrypt account key
le_account_key_path
: The path for the Let's Encrypt account key (default: "{{ le_base_directory }}/account.key")
le_account_key_type
: The type of key (RSA or ECC) for the account key (default: RSA)
le_account_key_size
: Size of the key, only for RSA (default: 4096)
le_account_key_curve
: The curve to use for ECC keys (default: secp384r1)
le_account_key_regenerate
: Whether to regenerate an existing key (default: false)
Let's Encrypt / ACME version and directory
le_acme_version
: The ACME Version to use (default: 2)
le_acme_directory
: The directory URL to seek certificates (default: Let's Encrypt Staging)
To use Let's Encrypt Production, the URL is: https://acme-v02.api.letsencrypt.org/directory.
le_renew_if_invalid_after
: Try to renew certificates if they are this many days from expiration (default: 30)
le_force_renew
: Force renew the certificates (default: false)
le_csr_only
: Set to true to only create private keys and CSRs (default: false)
Contributing and issues
Contributions are welcome. Please open issues or create pull requests.
I'm happy to address any issues and improve the role.
Testing
All tests are done using Molecule.
Testing is conducted using GitHub Actions with Ubuntu, Debian, and CentOS.
To test locally, create a Python virtual environment, install dependencies, and run the tests. Docker is required:
python3 -m venv venv
source venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install -r test-requirements.txt
molecule test
As this role needs to send requests to an ACME server and requires domain control to set DNS records, testing is limited. It checks for key and CSR creation and that they have the expected content.
Complete testing with all features is done against Let's Encrypt's staging server on a real internet-connected machine, but manually.
License
GNU General Public License v3.0
Requests certificates from Let's Encrypt (or another ACME server)
ansible-galaxy install dhach.acme_letsencrypt