HowTo: Certificate Renewal with Ansible


#1

Here is an ansible file for renewing certs (issuing new should be easy to add, if wanted):

---
- hosts: all
  vars:
    email: "admin@email"
    domains:
      - { domain: 'domain.tld' }
      - { domain: 'domain2', altnames: ['test.domain2', 'www.domain2'], auto_www: false }
      - { domain: 'domain3', altnames: ['domain4'] }
      - { domain: 'domain5', email: 'otheradmin@domain5' }
  tasks:
    - name: Check certificate date
      stat: path=/etc/letsencrypt/live/{{ item.domain }}/fullchain.pem
      register: mtimes
      with_items: domains
    - name: Request new certificate
      command: "letsencrypt certonly --webroot --webroot-path /var/www/letsencrypt --email {{ item.item.email|default(email)|quote }} -d {{ item.item.domain|quote }}{% if item.item.auto_www|default(true) %} -d www.{{ item.item.domain|quote }}{% endif %}{% for altname in item.item.altnames|default([]) %} -d {{ altname|quote }}{% if item.item.auto_www|default(true) %} -d www.{{ altname|quote }}{% endif %}{% endfor %}"
      when: ansible_date_time.epoch|float - item.stat.mtime > 60*60*24*30
      register: output
      with_items: mtimes.results
      notify: Reload Nginx
    - name: Debug
      debug: var=item.stdout
      debug: var=item.stderr
      with_items: output.results
  handlers:
    - name: Reload Nginx
      service: name=nginx state=reloaded

This assumes a common folder for all virtual hosts, which can be achived in nginx with this config snippet:

location ~ .well-known/acme-challenge/ {
	root /var/www/letsencrypt;
	default_type text/plain;
}

#2

Perhaps instead of looking at raw modified timestamp of a file, it’s better to use openssl command to get the actual notAfter value. That way you can be sure when that cert expires, regardless of file’s attributes.


#3

May be an option, but needs good testing not to break, as there is no (official) ansible module for that.


#4

My own script checks certs for a sufficient lifetime and checks the actual certification chain. Excerpts:

openssl x509 -checkend $((EXPIRE*60*60*24)) -in ${_cert}

where $EXPIRE is the minimum lifetime in days

for _chain in ${chains}; do
    cat /etc/ssl/cert.pem ${_chain} \
    | openssl verify -CAfile /dev/stdin ${_cert} >/dev/null 2>&1 \
    && return 0
done

I test with multiple chains because I include both LE certs (cross-signed and signed by ISRG Root X1) in my bundles. As long as one chain is OK, the check as a whole is OK.