An Ansible role that creates or renews a certificate with https://letsencrypt.org/ and optionally installs it to servers. Does not require root, stopping any processes, or installation of any software on the remote host(s).
This method currently only supports HTTP/S validation, it uses a proxy from your public facing website (probably over a private management network) to avoid installing or running anything extra on production servers.
Generated certificates will be put in letsencrypt_dir
(default gen/letsencrypt
), specifying letsencrypt_install_dir
will make it also push to all hosts.
You need to have certbot
or letsencrypt
installed in your local environment path, or set variable letsencrypt_command
.
To install either follow their directions, or to install directly to the current venv (I wasn't able to get it to find the correct deps for a direct from git install, hopefully it'll work from pypi soon):
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
pip install -e ../c/gh/letsencrypt/
pip install -e ../c/gh/letsencrypt/acme/
With ansible it's quite easy to configure a proxy from all your front end servers back to the deploy machine. Grab the address from the environment and apply where needed, some examples:
{% set ansible_local_host = ansible_env.SSH_CONNECTION.split(' ')[0] -%}
<IfModule mod_proxy.c>
ProxyPass "/.well-known/acme-challenge/" "http://{{ansible_local_host}}:{{letsencrypt_local_port}}/.well-known/acme-challenge/" retry=1
ProxyPassReverse "/.well-known/acme-challenge/" "http://{{ansible_local_host}}:{{letsencrypt_local_port}}/.well-known/acme-challenge/"
<Location "/.well-known/acme-challenge/">
ProxyPreserveHost On
Order allow,deny
Allow from all
Require all granted
</Location>
</IfModule>
{% set ansible_local_host = ansible_env.SSH_CONNECTION.split(' ')[0] -%}
server {
listen 80;
location /.well-known/acme-challenge/ {
proxy_pass http://{{ansible_local_host}}:{{letsencrypt_local_port}}/;
rewrite /(.*) /$1 break;
}
}
Nginx, from ansible role jdauphant.nginx
nginx_sites:
redir80:
- listen 80
- "location /.well-known/acme-challenge/ {
proxy_pass http://{{ansible_env.SSH_CONNECTION.split(' ')[0]}}:{{letsencrypt_local_port}}/;
rewrite /(.*) /$1 break;
}"
The only required variable is letsencrypt_certs
which must be a list of dicts that contain at least domain
and may also contain san
as a list of Subject Alternative Names for the cert, and email if you want to override the cert email.
letsencrypt_certs:
- domain: example.net
# optional:
sans: [www.example.net, example.com, www.example.com]
email: admin@example.net
- domain: example.org
sans: [www.example.org]
If you want it to install the certs you must set letsencrypt_install_dir
.
letsencrypt_install_dir: /srv/certs/
Default variables
# will use this @$domain for email address, overridable per domain
letsencrypt_email_user: root
# create cert locally for later sync
letsencrypt_local: True
# local port
letsencrypt_local_port: 7010
# always push certs to server
letsencrypt_force_install: True
letsencrypt_server: https://acme-v01.api.letsencrypt.org/directory
# letsencrypt_command: letsencrypt
letsencrypt_dir: gen/letsencrypt
letsencrypt_config_dir: "{{letsencrypt_dir}}/config"
letsencrypt_work_dir: "{{letsencrypt_dir}}/tmp"
letsencrypt_logs_dir: "{{letsencrypt_dir}}/log"
# renew the cert when expiration is within this number of seconds, default is 21 days (email reminders come at 20 days)
letsencrypt_renew_at: 1814400
letsencrypt_cert_file: fullchain.pem
- hosts: servers
roles:
- role: letsencrypt
letsencrypt_install_dir: /srv/cert/
letsencrypt_certs:
- domain: example.net
sans: [www.example.net, example.com, www.example.com]
email: admin@example.net
Uses tag letsencrypt
to only run these tasks, and will automatically renew if the certificate will expire in less than letsencrypt_renew_at
seconds.
So as an example command to keep your certs renewed from a nightly crontab would be:
ansible-playbook -i prod site.yml --tag=letsencrypt
Apache