Obtaining a wildcard LetsEncrypt cert with Ansible
Earlier this year, LetsEncrypt made their wildcard x509 certificates available to the general public. Whilst this is a massive step forward over individual certificates for each domain, it does come with the overhead of having to distribute the wildcard certificate to the (possibly many) places you would use it. Ignoring that issue for now, I wrote a quick Ansible playbook which uses the dns-01
challenge method and my Memset DNS management modules (available in Ansible 2.6+) to provide the verification.
Without further ado:
--- - hosts: localhost gather_facts: no vars: tmpdir: "/tmp/le/" account_key: "le-account-key.pem" keyname: "star-domain-com-key.pem" csrname: "star-domain-com.csr" certname: "star-domain-com.pem" fullchain: "star-domain-com-fullchain.pem" common_name: "*.domain.com" tasks: - name: localhost | create temp dir file: path: "{{ tmpdir }}" state: directory - name: localhost | create temp account key openssl_privatekey: path: "{{ tmpdir }}{{ account_key }}" - name: localhost | create private key openssl_privatekey: path: "{{ tmpdir }}{{ keyname }}" - name: localhost | create CSR openssl_csr: path: "{{ tmpdir }}{{ csrname }}" privatekey_path: "{{ tmpdir }}{{ keyname }}" common_name: "{{ common_name }}" country_name: GB organization_name: my-OU email_address: ops@domain.com - name: LetsEncrypt | submit request acme_certificate: account_key_src: "{{ tmpdir }}{{ account_key }}" account_email: me@domain.com src: "{{ tmpdir }}{{ csrname }}" fullchain_dest: "{{ tmpdir }}{{ certname }}" challenge: dns-01 acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory acme_version: 2 terms_agreed: yes remaining_days: 60 register: challenge - name: Memset | create DNS challenge record memset_zone_record: api_key: "{{ memset_dns_api_key }}" state: present zone: domain.com type: TXT record: "{{ challenge['challenge_data']['*.domain.com']['dns-01']['resource'] }}" data: "{{ challenge['challenge_data']['*.domain.com']['dns-01']['resource_value'] }}" - name: Memset | request DNS reload memset_dns_reload: api_key: "{{ memset_dns_api_key }}" poll: true - name: LetsEncrypt | retrieve cert acme_certificate: account_key_src: "{{ tmpdir }}{{ account_key }}" account_email: me@domain.com src: "{{ tmpdir }}{{ csrname }}" dest: "{{ tmpdir }}{{ certname }}" fullchain_dest: "{{ tmpdir }}{{ fullchain }}" challenge: dns-01 acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory acme_version: 2 terms_agreed: yes remaining_days: 60 data: "{{ challenge }}" register: cert_retrieval - name: Memset | delete DNS challenge record memset_zone_record: api_key: "{{ memset_dns_api_key }}" state: absent zone: domain.com type: TXT record: "{{ challenge['challenge_data']['*.domain.com']['dns-01']['resource'] }}" data: "{{ challenge['challenge_data']['*.domain.com']['dns-01']['resource_value'] }}" when: cert_retrieval is changed - name: localhost | remove the account key file: path: "{{ tmpdir }}{{ account_key }}" state: absent when: cert_retrieval is changed
Points to note
I’ve deliberately used the staging endpoint provided by LetsEncrypt; the certs this issues won’t be valid for use but it allows you to test your playbook without hitting the account rate limits.
The last two tasks cleanup the account key and DNS challenge record, but only if the certificate was successfully issued.
Testing
$ openssl x509 -in star-domain-com.pem -noout -text | grep "Subject: CN" Subject: CN = *.domain.com
There we have it; one wildcard certificate from LetsEncrypt!
Hi there,
I’ve been working on a project to renew my companies SSL Certificates over the last couple months, and I think I finally have it all working except one issue that I can’t seem to find information on: Getting the remaining_days piece working. I’m confused as to how to use it correctly and it doesn’t appear to be working in my project. As a result my Renew task errors out most of the time with the message that I’ve hit my limit. Some of my confusion lays around which files the module needs from the last time certificates were generated and which files it needs me to generate new for the new certificates I’m requesting. The CSR and Account Key for example. Can I keep everything as normal and simply pass in the old certificate so the module can check its expiration? Or does LE keep track of this and if it’s not working then it’s a bug? Any help understanding this would be appreciated. Thank you!
Hi John, apologies for taking a while to get back to you. I’m afraid I’m not 100% on this, but having read the docs (https://docs.ansible.com/ansible/devel/modules/acme_certificate_module.html), I’m pretty sure that information is stored in LE’s database – I don’t pass the old cert to the command so it has no way of determining the remaining lifespan. Hope that’s some help!